123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299 |
- /* 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 "stdh.h"
- #include <Engine/Base/Console.h>
- #include <Engine/Graphics/GfxLibrary.h>
- #include <Engine/Graphics/Raster.h>
- #include <Engine/Graphics/ViewPort.h>
- #include <Engine/Base/Statistics_internal.h>
- #include <Engine/Templates/StaticArray.cpp>
- #include <Engine/Templates/StaticStackArray.cpp>
- 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<DepthInfo> _adiDelayed; // active delayed points
- // don't ask, these are for D3D
- CStaticStackArray<CTVERTEX> _avtxDelayed;
- CStaticStackArray<COLOR> _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;
- #ifdef SE1_D3D
- ASSERT(eAPI == GAT_OGL || eAPI == GAT_D3D || eAPI == GAT_NONE);
- #else // SE1_D3D
- ASSERT(eAPI == GAT_OGL || eAPI == GAT_NONE);
- #endif // SE1_D3D
- 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; idi<ctCount; idi++) {
- DepthInfo &di = pdi[idi];
- // skip if not in required mirror level or was already checked in this iteration
- if( iMirrorLevel!=di.di_iMirrorLevel || _iCheckIteration!=di.di_iSwapLastRequest) continue;
- const PIX pixJ = pra->ra_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_fOoK<fPointOoK);
- }
- // done
- _sfStats.StopTimer(CStatForm::STI_GFXAPI);
- return;
- }
- // Direct3D
- #ifdef SE1_D3D
- if( eAPI==GAT_D3D)
- {
- _sfStats.StartTimer(CStatForm::STI_GFXAPI);
- // ok, this will get really complicated ...
- // We'll have to do it thru back buffer because darn DX8 won't let us have values from z-buffer;
- // Anyway, we'll lock backbuffer, read color from the lens location and try to write little triangle there
- // with slightly modified color. Then we'll readout that color and see if triangle passes z-test. Voila! :)
- // P.S. To avoid lock-modify-lock, we need to batch all the locks in one. Uhhhh ... :(
- COLOR col;
- INDEX idi;
- SLONG slColSize;
- HRESULT hr;
- D3DLOCKED_RECT rectLocked;
- D3DSURFACE_DESC surfDesc;
- LPDIRECT3DSURFACE8 pBackBuffer;
- // fetch back buffer (different for full screen and windowed mode)
- const BOOL bFullScreen = _pGfx->gl_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; idi<ctCount; idi++) {
- DepthInfo &di = pdi[idi];
- // skip if not in required mirror level or was already checked in this iteration
- if( iMirrorLevel!=di.di_iMirrorLevel || _iCheckIteration!=di.di_iSwapLastRequest) continue;
- // fetch pixel
- _acolDelayed[idi] = 0;
- const RECT rectToLock = { di.di_pixI, di.di_pixJ, di.di_pixI+1, di.di_pixJ+1 };
- hr = pBackBuffer->LockRect( &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; idi<ctCount; idi++) {
- DepthInfo &di = pdi[idi];
- col = _acolDelayed[idi];
- // skip if not in required mirror level or was already checked in this iteration, or wasn't fetched at all
- if( iMirrorLevel!=di.di_iMirrorLevel || _iCheckIteration!=di.di_iSwapLastRequest || col==0) continue;
- const ULONG d3dCol = rgba2argb(col^0x20103000);
- const PIX pixI = di.di_pixI - pdp->dp_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; idi<ctCount; idi++) {
- DepthInfo &di = pdi[idi];
- col = _acolDelayed[idi];
- // skip if not in required mirror level or was already checked in this iteration, or wasn't fetched at all
- if( iMirrorLevel!=di.di_iMirrorLevel || _iCheckIteration!=di.di_iSwapLastRequest || col==0) continue;
- // fetch pixel
- const RECT rectToLock = { di.di_pixI, di.di_pixJ, di.di_pixI+1, di.di_pixJ+1 };
- hr = pBackBuffer->LockRect( &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, 0L, 2L);
- if( gap_iOptimizeDepthReads==0 || iMirrorLevel>7) return;
- ASSERT( pdp!=NULL && iMirrorLevel>=0);
- // check only if time lapse allows
- const CTimerValue tvNow = _pTimer->GetHighPrecisionTimer();
- 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( iPoint<ctPoints) {
- DepthInfo &di = _adiDelayed[iPoint];
- // if the point is not active any more
- if( iMirrorLevel==di.di_iMirrorLevel && di.di_iSwapLastRequest<_iCheckIteration-KEEP_BEHIND) {
- // delete it by moving the last one on its place
- di = _adiDelayed[ctPoints-1];
- ctPoints--;
- // if the point is still active
- } else {
- // go to next point
- iPoint++;
- }
- }
- // remove unused points at the end
- if( ctPoints==0) _adiDelayed.PopAll();
- else _adiDelayed.PopUntil(ctPoints-1);
- // ignore stalls
- if( tmDelta>1.0f) return;
- // check and upadete visibility of what has left
- ASSERT( ctPoints == _adiDelayed.Count());
- if( ctPoints>0) UpdateDepthPointsVisibility( pdp, iMirrorLevel, &_adiDelayed[0], ctPoints);
- // mark checking
- _iCheckIteration++;
- }
|