DepthCheck.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299
  1. /* Copyright (c) 2002-2012 Croteam Ltd.
  2. This program is free software; you can redistribute it and/or modify
  3. it under the terms of version 2 of the GNU General Public License as published by
  4. the Free Software Foundation
  5. This program is distributed in the hope that it will be useful,
  6. but WITHOUT ANY WARRANTY; without even the implied warranty of
  7. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  8. GNU General Public License for more details.
  9. You should have received a copy of the GNU General Public License along
  10. with this program; if not, write to the Free Software Foundation, Inc.,
  11. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
  12. #include "stdh.h"
  13. #include <Engine/Base/Console.h>
  14. #include <Engine/Graphics/GfxLibrary.h>
  15. #include <Engine/Graphics/Raster.h>
  16. #include <Engine/Graphics/ViewPort.h>
  17. #include <Engine/Base/Statistics_internal.h>
  18. #include <Engine/Templates/StaticArray.cpp>
  19. #include <Engine/Templates/StaticStackArray.cpp>
  20. extern INDEX gap_iOptimizeDepthReads;
  21. #ifdef SE1_D3D
  22. extern COLOR UnpackColor_D3D( UBYTE *pd3dColor, D3DFORMAT d3dFormat, SLONG &slColorSize);
  23. #endif // SE1_D3D
  24. static INDEX _iCheckIteration = 0;
  25. static CTimerValue _tvLast[8]; // 8 is max mirror recursion
  26. #define KEEP_BEHIND 8
  27. // info of one point for delayed depth buffer lookup
  28. struct DepthInfo {
  29. INDEX di_iID; // unique identifier
  30. PIX di_pixI, di_pixJ; // last requested coordinates
  31. FLOAT di_fOoK; // last requested depth
  32. INDEX di_iSwapLastRequest; // index of swap when last requested
  33. INDEX di_iMirrorLevel; // level of mirror recursion in which flare is
  34. BOOL di_bVisible; // whether the point was visible
  35. };
  36. CStaticStackArray<DepthInfo> _adiDelayed; // active delayed points
  37. // don't ask, these are for D3D
  38. CStaticStackArray<CTVERTEX> _avtxDelayed;
  39. CStaticStackArray<COLOR> _acolDelayed;
  40. // read depth buffer and update visibility flag of depth points
  41. static void UpdateDepthPointsVisibility( const CDrawPort *pdp, const INDEX iMirrorLevel,
  42. DepthInfo *pdi, const INDEX ctCount)
  43. {
  44. const GfxAPIType eAPI = _pGfx->gl_eCurrentAPI;
  45. #ifdef SE1_D3D
  46. ASSERT(eAPI == GAT_OGL || eAPI == GAT_D3D || eAPI == GAT_NONE);
  47. #else // SE1_D3D
  48. ASSERT(eAPI == GAT_OGL || eAPI == GAT_NONE);
  49. #endif // SE1_D3D
  50. ASSERT( pdp!=NULL && ctCount>0);
  51. const CRaster *pra = pdp->dp_Raster;
  52. // OpenGL
  53. if( eAPI==GAT_OGL)
  54. {
  55. _sfStats.StartTimer(CStatForm::STI_GFXAPI);
  56. FLOAT fPointOoK;
  57. // for each stored point
  58. for( INDEX idi=0; idi<ctCount; idi++) {
  59. DepthInfo &di = pdi[idi];
  60. // skip if not in required mirror level or was already checked in this iteration
  61. if( iMirrorLevel!=di.di_iMirrorLevel || _iCheckIteration!=di.di_iSwapLastRequest) continue;
  62. const PIX pixJ = pra->ra_Height-1 - di.di_pixJ; // OpenGL has Y-inversed buffer!
  63. pglReadPixels( di.di_pixI, pixJ, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &fPointOoK);
  64. OGL_CHECKERROR;
  65. // it is visible if there is nothing nearer in z-buffer already
  66. di.di_bVisible = (di.di_fOoK<fPointOoK);
  67. }
  68. // done
  69. _sfStats.StopTimer(CStatForm::STI_GFXAPI);
  70. return;
  71. }
  72. // Direct3D
  73. #ifdef SE1_D3D
  74. if( eAPI==GAT_D3D)
  75. {
  76. _sfStats.StartTimer(CStatForm::STI_GFXAPI);
  77. // ok, this will get really complicated ...
  78. // We'll have to do it thru back buffer because darn DX8 won't let us have values from z-buffer;
  79. // Anyway, we'll lock backbuffer, read color from the lens location and try to write little triangle there
  80. // with slightly modified color. Then we'll readout that color and see if triangle passes z-test. Voila! :)
  81. // P.S. To avoid lock-modify-lock, we need to batch all the locks in one. Uhhhh ... :(
  82. COLOR col;
  83. INDEX idi;
  84. SLONG slColSize;
  85. HRESULT hr;
  86. D3DLOCKED_RECT rectLocked;
  87. D3DSURFACE_DESC surfDesc;
  88. LPDIRECT3DSURFACE8 pBackBuffer;
  89. // fetch back buffer (different for full screen and windowed mode)
  90. const BOOL bFullScreen = _pGfx->gl_ulFlags & GLF_FULLSCREEN;
  91. if( bFullScreen) {
  92. hr = _pGfx->gl_pd3dDevice->GetBackBuffer( 0, D3DBACKBUFFER_TYPE_MONO, &pBackBuffer);
  93. } else {
  94. hr = pra->ra_pvpViewPort->vp_pSwapChain->GetBackBuffer( 0, D3DBACKBUFFER_TYPE_MONO, &pBackBuffer);
  95. }
  96. // what, cannot get a back buffer?
  97. if( hr!=D3D_OK) {
  98. // to hell with it all
  99. _sfStats.StopTimer(CStatForm::STI_GFXAPI);
  100. return;
  101. }
  102. // keep format of back-buffer
  103. pBackBuffer->GetDesc(&surfDesc);
  104. const D3DFORMAT d3dfBack = surfDesc.Format;
  105. // prepare array that'll back-buffer colors from depth point locations
  106. _acolDelayed.Push(ctCount);
  107. // store all colors
  108. for( idi=0; idi<ctCount; idi++) {
  109. DepthInfo &di = pdi[idi];
  110. // skip if not in required mirror level or was already checked in this iteration
  111. if( iMirrorLevel!=di.di_iMirrorLevel || _iCheckIteration!=di.di_iSwapLastRequest) continue;
  112. // fetch pixel
  113. _acolDelayed[idi] = 0;
  114. const RECT rectToLock = { di.di_pixI, di.di_pixJ, di.di_pixI+1, di.di_pixJ+1 };
  115. hr = pBackBuffer->LockRect( &rectLocked, &rectToLock, D3DLOCK_READONLY);
  116. if( hr!=D3D_OK) continue; // skip if lock didn't make it
  117. // read, convert and store original color
  118. _acolDelayed[idi] = UnpackColor_D3D( (UBYTE*)rectLocked.pBits, d3dfBack, slColSize) | CT_OPAQUE;
  119. pBackBuffer->UnlockRect();
  120. }
  121. // prepare to draw little triangles there with slightly adjusted colors
  122. _sfStats.StopTimer(CStatForm::STI_GFXAPI);
  123. gfxEnableDepthTest();
  124. gfxDisableDepthWrite();
  125. gfxDisableBlend();
  126. gfxDisableAlphaTest();
  127. gfxDisableTexture();
  128. _sfStats.StartTimer(CStatForm::STI_GFXAPI);
  129. // prepare array and shader
  130. _avtxDelayed.Push(ctCount*3);
  131. d3dSetVertexShader(D3DFVF_CTVERTEX);
  132. // draw one trianle around each depth point
  133. INDEX ctVertex = 0;
  134. for( idi=0; idi<ctCount; idi++) {
  135. DepthInfo &di = pdi[idi];
  136. col = _acolDelayed[idi];
  137. // skip if not in required mirror level or was already checked in this iteration, or wasn't fetched at all
  138. if( iMirrorLevel!=di.di_iMirrorLevel || _iCheckIteration!=di.di_iSwapLastRequest || col==0) continue;
  139. const ULONG d3dCol = rgba2argb(col^0x20103000);
  140. const PIX pixI = di.di_pixI - pdp->dp_MinI; // convert raster loc to drawport loc
  141. const PIX pixJ = di.di_pixJ - pdp->dp_MinJ;
  142. // batch it and advance to next triangle
  143. CTVERTEX &vtx0 = _avtxDelayed[ctVertex++];
  144. CTVERTEX &vtx1 = _avtxDelayed[ctVertex++];
  145. CTVERTEX &vtx2 = _avtxDelayed[ctVertex++];
  146. vtx0.fX=pixI; vtx0.fY=pixJ-2; vtx0.fZ=di.di_fOoK; vtx0.ulColor=d3dCol; vtx0.fU=vtx0.fV=0;
  147. vtx1.fX=pixI-2; vtx1.fY=pixJ+2; vtx1.fZ=di.di_fOoK; vtx1.ulColor=d3dCol; vtx1.fU=vtx0.fV=0;
  148. vtx2.fX=pixI+2; vtx2.fY=pixJ; vtx2.fZ=di.di_fOoK; vtx2.ulColor=d3dCol; vtx2.fU=vtx0.fV=0;
  149. }
  150. // draw a bunch
  151. hr = _pGfx->gl_pd3dDevice->DrawPrimitiveUP( D3DPT_TRIANGLELIST, ctVertex/3, &_avtxDelayed[0], sizeof(CTVERTEX));
  152. D3D_CHECKERROR(hr);
  153. // readout colors again and compare to old ones
  154. for( idi=0; idi<ctCount; idi++) {
  155. DepthInfo &di = pdi[idi];
  156. col = _acolDelayed[idi];
  157. // skip if not in required mirror level or was already checked in this iteration, or wasn't fetched at all
  158. if( iMirrorLevel!=di.di_iMirrorLevel || _iCheckIteration!=di.di_iSwapLastRequest || col==0) continue;
  159. // fetch pixel
  160. const RECT rectToLock = { di.di_pixI, di.di_pixJ, di.di_pixI+1, di.di_pixJ+1 };
  161. hr = pBackBuffer->LockRect( &rectLocked, &rectToLock, D3DLOCK_READONLY);
  162. if( hr!=D3D_OK) continue; // skip if lock didn't make it
  163. // read new color
  164. const COLOR colNew = UnpackColor_D3D( (UBYTE*)rectLocked.pBits, d3dfBack, slColSize) | CT_OPAQUE;
  165. pBackBuffer->UnlockRect();
  166. // if we managed to write adjusted color, point is visible!
  167. di.di_bVisible = (col!=colNew);
  168. }
  169. // phew, done! :)
  170. D3DRELEASE( pBackBuffer, TRUE);
  171. _acolDelayed.PopAll();
  172. _avtxDelayed.PopAll();
  173. _sfStats.StopTimer(CStatForm::STI_GFXAPI);
  174. return;
  175. }
  176. #endif // SE1_D3D
  177. }
  178. // check point against depth buffer
  179. extern BOOL CheckDepthPoint( const CDrawPort *pdp, PIX pixI, PIX pixJ, FLOAT fOoK, INDEX iID, INDEX iMirrorLevel/*=0*/)
  180. {
  181. // no raster?
  182. const CRaster *pra = pdp->dp_Raster;
  183. if( pra==NULL) return FALSE;
  184. // almoust out of raster?
  185. pixI += pdp->dp_MinI;
  186. pixJ += pdp->dp_MinJ;
  187. if( pixI<1 || pixJ<1 || pixI>pra->ra_Width-2 || pixJ>pra->ra_Height-2) return FALSE;
  188. // if shouldn't delay
  189. if( gap_iOptimizeDepthReads==0) {
  190. // just check immediately
  191. DepthInfo di = { iID, pixI, pixJ, fOoK, _iCheckIteration, iMirrorLevel, FALSE };
  192. UpdateDepthPointsVisibility( pdp, iMirrorLevel, &di, 1);
  193. return di.di_bVisible;
  194. }
  195. // for each stored point
  196. for( INDEX idi=0; idi<_adiDelayed.Count(); idi++) {
  197. DepthInfo &di = _adiDelayed[idi];
  198. // if same id
  199. if( di.di_iID == iID) {
  200. // remember parameters
  201. di.di_pixI = pixI;
  202. di.di_pixJ = pixJ;
  203. di.di_fOoK = fOoK;
  204. di.di_iSwapLastRequest = _iCheckIteration;
  205. // return visibility
  206. return di.di_bVisible;
  207. }
  208. }
  209. // if not found...
  210. // create new one
  211. DepthInfo &di = _adiDelayed.Push();
  212. // remember parameters
  213. di.di_iID = iID;
  214. di.di_pixI = pixI;
  215. di.di_pixJ = pixJ;
  216. di.di_fOoK = fOoK;
  217. di.di_iSwapLastRequest = _iCheckIteration;
  218. di.di_iMirrorLevel = iMirrorLevel;
  219. di.di_bVisible = FALSE;
  220. // not visible by default
  221. return FALSE;
  222. }
  223. // check all delayed depth points
  224. extern void CheckDelayedDepthPoints( const CDrawPort *pdp, INDEX iMirrorLevel/*=0*/)
  225. {
  226. // skip if not delayed or mirror level is to high
  227. gap_iOptimizeDepthReads = Clamp( gap_iOptimizeDepthReads, 0L, 2L);
  228. if( gap_iOptimizeDepthReads==0 || iMirrorLevel>7) return;
  229. ASSERT( pdp!=NULL && iMirrorLevel>=0);
  230. // check only if time lapse allows
  231. const CTimerValue tvNow = _pTimer->GetHighPrecisionTimer();
  232. const TIME tmDelta = (tvNow-_tvLast[iMirrorLevel]).GetSeconds();
  233. ASSERT( tmDelta>=0);
  234. if( gap_iOptimizeDepthReads==2 && tmDelta<0.1f) return;
  235. // prepare
  236. _tvLast[iMirrorLevel] = tvNow;
  237. INDEX ctPoints = _adiDelayed.Count();
  238. if( ctPoints==0) return; // done if no points in queue
  239. // for each point
  240. INDEX iPoint = 0;
  241. while( iPoint<ctPoints) {
  242. DepthInfo &di = _adiDelayed[iPoint];
  243. // if the point is not active any more
  244. if( iMirrorLevel==di.di_iMirrorLevel && di.di_iSwapLastRequest<_iCheckIteration-KEEP_BEHIND) {
  245. // delete it by moving the last one on its place
  246. di = _adiDelayed[ctPoints-1];
  247. ctPoints--;
  248. // if the point is still active
  249. } else {
  250. // go to next point
  251. iPoint++;
  252. }
  253. }
  254. // remove unused points at the end
  255. if( ctPoints==0) _adiDelayed.PopAll();
  256. else _adiDelayed.PopUntil(ctPoints-1);
  257. // ignore stalls
  258. if( tmDelta>1.0f) return;
  259. // check and upadete visibility of what has left
  260. ASSERT( ctPoints == _adiDelayed.Count());
  261. if( ctPoints>0) UpdateDepthPointsVisibility( pdp, iMirrorLevel, &_adiDelayed[0], ctPoints);
  262. // mark checking
  263. _iCheckIteration++;
  264. }