/* Copyright (c) 2002-2012 Croteam Ltd. This program is free software; you can redistribute it and/or modify it under the terms of version 2 of the GNU General Public License as published by the Free Software Foundation This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "Engine/StdH.h" #include #include #include #include #include #include #include extern INDEX gap_iOptimizeDepthReads; #ifdef SE1_D3D extern COLOR UnpackColor_D3D( UBYTE *pd3dColor, D3DFORMAT d3dFormat, SLONG &slColorSize); #endif // SE1_D3D static INDEX _iCheckIteration = 0; static CTimerValue _tvLast[8]; // 8 is max mirror recursion #define KEEP_BEHIND 8 // info of one point for delayed depth buffer lookup struct DepthInfo { INDEX di_iID; // unique identifier PIX di_pixI, di_pixJ; // last requested coordinates FLOAT di_fOoK; // last requested depth INDEX di_iSwapLastRequest; // index of swap when last requested INDEX di_iMirrorLevel; // level of mirror recursion in which flare is BOOL di_bVisible; // whether the point was visible }; CStaticStackArray _adiDelayed; // active delayed points // don't ask, these are for D3D CStaticStackArray _avtxDelayed; CStaticStackArray _acolDelayed; // read depth buffer and update visibility flag of depth points static void UpdateDepthPointsVisibility( const CDrawPort *pdp, const INDEX iMirrorLevel, DepthInfo *pdi, const INDEX ctCount) { const GfxAPIType eAPI = _pGfx->gl_eCurrentAPI; ASSERT(GfxValidApi(eAPI)); ASSERT( pdp!=NULL && ctCount>0); const CRaster *pra = pdp->dp_Raster; // OpenGL if( eAPI==GAT_OGL) { _sfStats.StartTimer(CStatForm::STI_GFXAPI); FLOAT fPointOoK; // for each stored point for( INDEX idi=0; idira_Height-1 - di.di_pixJ; // OpenGL has Y-inversed buffer! pglReadPixels( di.di_pixI, pixJ, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &fPointOoK); OGL_CHECKERROR; // it is visible if there is nothing nearer in z-buffer already di.di_bVisible = (di.di_fOoKgl_ulFlags & GLF_FULLSCREEN; if( bFullScreen) { hr = _pGfx->gl_pd3dDevice->GetBackBuffer( 0, D3DBACKBUFFER_TYPE_MONO, &pBackBuffer); } else { hr = pra->ra_pvpViewPort->vp_pSwapChain->GetBackBuffer( 0, D3DBACKBUFFER_TYPE_MONO, &pBackBuffer); } // what, cannot get a back buffer? if( hr!=D3D_OK) { // to hell with it all _sfStats.StopTimer(CStatForm::STI_GFXAPI); return; } // keep format of back-buffer pBackBuffer->GetDesc(&surfDesc); const D3DFORMAT d3dfBack = surfDesc.Format; // prepare array that'll back-buffer colors from depth point locations _acolDelayed.Push(ctCount); // store all colors for( idi=0; idiLockRect( &rectLocked, &rectToLock, D3DLOCK_READONLY); if( hr!=D3D_OK) continue; // skip if lock didn't make it // read, convert and store original color _acolDelayed[idi] = UnpackColor_D3D( (UBYTE*)rectLocked.pBits, d3dfBack, slColSize) | CT_OPAQUE; pBackBuffer->UnlockRect(); } // prepare to draw little triangles there with slightly adjusted colors _sfStats.StopTimer(CStatForm::STI_GFXAPI); gfxEnableDepthTest(); gfxDisableDepthWrite(); gfxDisableBlend(); gfxDisableAlphaTest(); gfxDisableTexture(); _sfStats.StartTimer(CStatForm::STI_GFXAPI); // prepare array and shader _avtxDelayed.Push(ctCount*3); d3dSetVertexShader(D3DFVF_CTVERTEX); // draw one trianle around each depth point INDEX ctVertex = 0; for( idi=0; ididp_MinI; // convert raster loc to drawport loc const PIX pixJ = di.di_pixJ - pdp->dp_MinJ; // batch it and advance to next triangle CTVERTEX &vtx0 = _avtxDelayed[ctVertex++]; CTVERTEX &vtx1 = _avtxDelayed[ctVertex++]; CTVERTEX &vtx2 = _avtxDelayed[ctVertex++]; vtx0.fX=pixI; vtx0.fY=pixJ-2; vtx0.fZ=di.di_fOoK; vtx0.ulColor=d3dCol; vtx0.fU=vtx0.fV=0; vtx1.fX=pixI-2; vtx1.fY=pixJ+2; vtx1.fZ=di.di_fOoK; vtx1.ulColor=d3dCol; vtx1.fU=vtx0.fV=0; vtx2.fX=pixI+2; vtx2.fY=pixJ; vtx2.fZ=di.di_fOoK; vtx2.ulColor=d3dCol; vtx2.fU=vtx0.fV=0; } // draw a bunch hr = _pGfx->gl_pd3dDevice->DrawPrimitiveUP( D3DPT_TRIANGLELIST, ctVertex/3, &_avtxDelayed[0], sizeof(CTVERTEX)); D3D_CHECKERROR(hr); // readout colors again and compare to old ones for( idi=0; idiLockRect( &rectLocked, &rectToLock, D3DLOCK_READONLY); if( hr!=D3D_OK) continue; // skip if lock didn't make it // read new color const COLOR colNew = UnpackColor_D3D( (UBYTE*)rectLocked.pBits, d3dfBack, slColSize) | CT_OPAQUE; pBackBuffer->UnlockRect(); // if we managed to write adjusted color, point is visible! di.di_bVisible = (col!=colNew); } // phew, done! :) D3DRELEASE( pBackBuffer, TRUE); _acolDelayed.PopAll(); _avtxDelayed.PopAll(); _sfStats.StopTimer(CStatForm::STI_GFXAPI); return; } #endif // SE1_D3D } // check point against depth buffer extern BOOL CheckDepthPoint( const CDrawPort *pdp, PIX pixI, PIX pixJ, FLOAT fOoK, INDEX iID, INDEX iMirrorLevel/*=0*/) { // no raster? const CRaster *pra = pdp->dp_Raster; if( pra==NULL) return FALSE; // almoust out of raster? pixI += pdp->dp_MinI; pixJ += pdp->dp_MinJ; if( pixI<1 || pixJ<1 || pixI>pra->ra_Width-2 || pixJ>pra->ra_Height-2) return FALSE; // if shouldn't delay if( gap_iOptimizeDepthReads==0) { // just check immediately DepthInfo di = { iID, pixI, pixJ, fOoK, _iCheckIteration, iMirrorLevel, FALSE }; UpdateDepthPointsVisibility( pdp, iMirrorLevel, &di, 1); return di.di_bVisible; } // for each stored point for( INDEX idi=0; idi<_adiDelayed.Count(); idi++) { DepthInfo &di = _adiDelayed[idi]; // if same id if( di.di_iID == iID) { // remember parameters di.di_pixI = pixI; di.di_pixJ = pixJ; di.di_fOoK = fOoK; di.di_iSwapLastRequest = _iCheckIteration; // return visibility return di.di_bVisible; } } // if not found... // create new one DepthInfo &di = _adiDelayed.Push(); // remember parameters di.di_iID = iID; di.di_pixI = pixI; di.di_pixJ = pixJ; di.di_fOoK = fOoK; di.di_iSwapLastRequest = _iCheckIteration; di.di_iMirrorLevel = iMirrorLevel; di.di_bVisible = FALSE; // not visible by default return FALSE; } // check all delayed depth points extern void CheckDelayedDepthPoints( const CDrawPort *pdp, INDEX iMirrorLevel/*=0*/) { // skip if not delayed or mirror level is to high gap_iOptimizeDepthReads = Clamp( gap_iOptimizeDepthReads, 0, 2); if( gap_iOptimizeDepthReads==0 || iMirrorLevel>7) return; ASSERT( pdp!=NULL && iMirrorLevel>=0); // check only if time lapse allows const CTimerValue tvNow = _pTimer->GetLowPrecisionTimer(); const TIME tmDelta = (tvNow-_tvLast[iMirrorLevel]).GetSeconds(); ASSERT( tmDelta>=0); if( gap_iOptimizeDepthReads==2 && tmDelta<0.1f) return; // prepare _tvLast[iMirrorLevel] = tvNow; INDEX ctPoints = _adiDelayed.Count(); if( ctPoints==0) return; // done if no points in queue // for each point INDEX iPoint = 0; while( iPoint1.0f) return; // check and update visibility of what has left ASSERT( ctPoints == _adiDelayed.Count()); if( ctPoints>0) UpdateDepthPointsVisibility( pdp, iMirrorLevel, &_adiDelayed[0], ctPoints); // mark checking _iCheckIteration++; }