//========= Copyright Valve Corporation, All rights reserved. ============// // TOGL CODE LICENSE // // Copyright 2011-2014 Valve Corporation // All Rights Reserved. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. // // dxabstract.cpp // //================================================================================================== #include "togl/rendermechanism.h" #include "tier0/vprof_telemetry.h" #include "tier0/dbg.h" #include "tier0/threadtools.h" #include "tier0/vprof.h" #include "tier1/strtools.h" #include "tier1/utlbuffer.h" #include "dx9asmtogl2.h" #include "mathlib/vmatrix.h" #include "materialsystem/IShader.h" #include "glmgr_flush.inl" #if defined(OSX) || defined(LINUX) || (defined (WIN32) && defined( DX_TO_GL_ABSTRACTION )) #include "appframework/ilaunchermgr.h" extern ILauncherMgr *g_pLauncherMgr; #endif #include "tier0/icommandline.h" #include "tier0/memdbgon.h" #ifdef USE_ACTUAL_DX #pragma comment( lib, "../../dx9sdk/lib/d3d9.lib" ) #pragma comment( lib, "../../dx9sdk/lib/d3dx9.lib" ) #else #ifdef POSIX #define strcat_s( a, b, c) V_strcat( a, c, b ) #endif #define D3D_DEVICE_VALID_MARKER 0x12EBC845 #define GL_PUBLIC_ENTRYPOINT_CHECKS( dev ) Assert( dev->GetCurrentOwnerThreadId() == ThreadGetCurrentId() ); Assert( dev->m_nValidMarker == D3D_DEVICE_VALID_MARKER ); // ------------------------------------------------------------------------------------------------------------------------------ // bool g_bNullD3DDevice; static D3DToGL g_D3DToOpenGLTranslatorGLSL; static IDirect3DDevice9 *g_pD3D_Device; #if GL_BATCH_PERF_ANALYSIS #include "../../thirdparty/miniz/simple_bitmap.h" #include "../../thirdparty/miniz/miniz.c" ConVar gl_batch_vis_abs_scale( "gl_batch_vis_abs_scale", ".050" ); ConVar gl_present_vis_abs_scale( "gl_present_vis_abs_scale", ".050" ); //ConVar gl_batch_vis_y_scale( "gl_batch_vis_y_scale", "0.007" ); ConVar gl_batch_vis_y_scale( "gl_batch_vis_y_scale", "0.0" ); static double s_rdtsc_to_ms; uint64 g_nTotalD3DCycles; uint g_nTotalD3DCalls; class CGLBatchPerfCallTimer { public: inline CGLBatchPerfCallTimer() { g_nTotalD3DCalls++; g_nTotalD3DCycles -= TM_FAST_TIME(); } inline ~CGLBatchPerfCallTimer() { g_nTotalD3DCycles += TM_FAST_TIME(); } }; #define GL_BATCH_PERF_CALL_TIMER CGLBatchPerfCallTimer scopedCallTimer; #else #define GL_BATCH_PERF_CALL_TIMER #endif // GL_BATCH_PERF_ANALYSIS ConVar gl_batch_vis( "gl_batch_vis", "0" ); // ------------------------------------------------------------------------------------------------------------------------------ // // functions that are dependant on g_pLauncherMgr inline GLMDisplayDB *GetDisplayDB( void ) { return g_pLauncherMgr->GetDisplayDB(); } inline void RenderedSize( uint &width, uint &height, bool set ) { g_pLauncherMgr->RenderedSize( width, height, set ); } inline void GetStackCrawl( CStackCrawlParams *params ) { g_pLauncherMgr->GetStackCrawl(params); } // ------------------------------------------------------------------------------------------------------------------------------ // #if defined( WIN32 ) bool D3DMATRIX::operator == ( CONST D3DMATRIX& src) const { return V_memcmp( (void*)this, (void*)&src, sizeof(this) ) == 0; } D3DMATRIX::operator void* () { return (void*)this; } #endif // ------------------------------------------------------------------------------------------------------------------------------ // #ifdef OSX #pragma mark ----- D3DXMATRIX operators #endif D3DXMATRIX D3DXMATRIX::operator*( const D3DXMATRIX &o ) const { D3DXMATRIX result; D3DXMatrixMultiply( &result, this, &o ); // this = lhs o = rhs result = this * o return result; } D3DXMATRIX::operator FLOAT* () { return (float*)this; } float& D3DXMATRIX::operator()( int row, int column ) { return m[row][column]; } const float& D3DXMATRIX::operator()( int row, int column ) const { return m[row][column]; } bool D3DXMATRIX::operator != ( CONST D3DXMATRIX& src ) const { return V_memcmp( (void*)this, (void*)&src, sizeof(this) ) != 0; } // ------------------------------------------------------------------------------------------------------------------------------ // #ifdef OSX #pragma mark ----- D3DXPLANE operators #endif float& D3DXPLANE::operator[]( int i ) { return ((float*)this)[i]; } bool D3DXPLANE::operator==( const D3DXPLANE &o ) { return a == o.a && b == o.b && c == o.c && d == o.d; } bool D3DXPLANE::operator!=( const D3DXPLANE &o ) { return !( *this == o ); } D3DXPLANE::operator float*() { return (float*)this; } D3DXPLANE::operator const float*() const { return (const float*)this; } // ------------------------------------------------------------------------------------------------------------------------------ // #ifdef OSX #pragma mark ----- D3DXVECTOR2 operators #endif D3DXVECTOR2::operator FLOAT* () { return (float*)this; } D3DXVECTOR2::operator CONST FLOAT* () const { return (const float*)this; } // ------------------------------------------------------------------------------------------------------------------------------ // #ifdef OSX #pragma mark ----- D3DXVECTOR3 operators #endif D3DXVECTOR3::D3DXVECTOR3( float a, float b, float c ) { x = a; y = b; z = c; } D3DXVECTOR3::operator FLOAT* () { return (float*)this; } D3DXVECTOR3::operator CONST FLOAT* () const { return (const float*)this; } // ------------------------------------------------------------------------------------------------------------------------------ // #ifdef OSX #pragma mark ----- D3DXVECTOR4 operators #endif D3DXVECTOR4::D3DXVECTOR4( float a, float b, float c, float d ) { x = a; y = b; z = c; w = d; } // ------------------------------------------------------------------------------------------------------------------------------ // DWORD IDirect3DResource9::SetPriority(DWORD PriorityNew) { // DXABSTRACT_BREAK_ON_ERROR(); // GLMPRINTF(( "-X- SetPriority" )); // no-op city return 0; } // ------------------------------------------------------------------------------------------------------------------------------ // #ifdef OSX #pragma mark ----- IDirect3DBaseTexture9 #endif IDirect3DBaseTexture9::~IDirect3DBaseTexture9() { GL_BATCH_PERF_CALL_TIMER; GL_PUBLIC_ENTRYPOINT_CHECKS( m_device ); GLMPRINTF(( ">-A- ~IDirect3DBaseTexture9" )); if (m_device) { Assert( m_device->m_ObjectStats.m_nTotalTextures >= 1 ); m_device->m_ObjectStats.m_nTotalTextures--; GLMPRINTF(( "-A- ~IDirect3DBaseTexture9 taking normal delete path on %08x, device is %08x ", this, m_device )); m_device->ReleasedTexture( this ); if (m_tex) { GLMPRINTF(("-A- ~IDirect3DBaseTexture9 deleted '%s' @ %08x (GLM %08x) %s",m_tex->m_layout->m_layoutSummary, this, m_tex, m_tex->m_debugLabel ? m_tex->m_debugLabel : "" )); m_device->ReleasedCGLMTex( m_tex ); m_tex->m_ctx->DelTex( m_tex ); m_tex = NULL; } else { GLMPRINTF(( "-A- ~IDirect3DBaseTexture9 : whoops, no tex to delete here ?" )); } m_device = NULL; // ** THIS ** is the only place to scrub this. Don't do it in the subclass destructors. } else { GLMPRINTF(( "-A- ~IDirect3DBaseTexture9 taking strange delete path on %08x, device is %08x ", this, m_device )); } GLMPRINTF(( "<-A- ~IDirect3DBaseTexture9" )); } D3DRESOURCETYPE IDirect3DBaseTexture9::GetType() { GL_BATCH_PERF_CALL_TIMER; GL_PUBLIC_ENTRYPOINT_CHECKS( m_device ); return m_restype; //D3DRTYPE_TEXTURE; } DWORD IDirect3DBaseTexture9::GetLevelCount() { GL_BATCH_PERF_CALL_TIMER; GL_PUBLIC_ENTRYPOINT_CHECKS( m_device ); return m_tex->m_layout->m_mipCount; } HRESULT IDirect3DBaseTexture9::GetLevelDesc(UINT Level,D3DSURFACE_DESC *pDesc) { GL_BATCH_PERF_CALL_TIMER; GL_PUBLIC_ENTRYPOINT_CHECKS( m_device ); Assert (Level < static_cast(m_tex->m_layout->m_mipCount)); D3DSURFACE_DESC result = m_descZero; // then mutate it for the level of interest GLMTexLayoutSlice *slice = &m_tex->m_layout->m_slices[ m_tex->CalcSliceIndex( 0, Level ) ]; result.Width = slice->m_xSize; result.Height = slice->m_ySize; *pDesc = result; return S_OK; } // ------------------------------------------------------------------------------------------------------------------------------ // #ifdef OSX #pragma mark ----- IDirect3DTexture9 #endif HRESULT IDirect3DDevice9::CreateTexture(UINT Width,UINT Height,UINT Levels,DWORD Usage,D3DFORMAT Format,D3DPOOL Pool,IDirect3DTexture9** ppTexture,VD3DHANDLE* pSharedHandle, char *pDebugLabel ) { GL_BATCH_PERF_CALL_TIMER; GL_PUBLIC_ENTRYPOINT_CHECKS( this ); m_ObjectStats.m_nTotalTextures++; GLMPRINTF((">-A-IDirect3DDevice9::CreateTexture")); IDirect3DTexture9 *dxtex = new IDirect3DTexture9; dxtex->m_restype = D3DRTYPE_TEXTURE; dxtex->m_device = this; dxtex->m_descZero.Format = Format; dxtex->m_descZero.Type = D3DRTYPE_TEXTURE; dxtex->m_descZero.Usage = Usage; dxtex->m_descZero.Pool = Pool; dxtex->m_descZero.MultiSampleType = D3DMULTISAMPLE_NONE; dxtex->m_descZero.MultiSampleQuality = 0; dxtex->m_descZero.Width = Width; dxtex->m_descZero.Height = Height; GLMTexLayoutKey key; memset( &key, 0, sizeof(key) ); key.m_texGLTarget = GL_TEXTURE_2D; key.m_texFormat = Format; if (Levels>1) { key.m_texFlags |= kGLMTexMipped; } // http://msdn.microsoft.com/en-us/library/bb172625(VS.85).aspx // complain if any usage bits come down that I don't know. uint knownUsageBits = (D3DUSAGE_AUTOGENMIPMAP | D3DUSAGE_RENDERTARGET | D3DUSAGE_DYNAMIC | D3DUSAGE_TEXTURE_SRGB | D3DUSAGE_DEPTHSTENCIL); if ( (Usage & knownUsageBits) != Usage ) { DXABSTRACT_BREAK_ON_ERROR(); } if (Usage & D3DUSAGE_AUTOGENMIPMAP) { key.m_texFlags |= kGLMTexMipped | kGLMTexMippedAuto; } if (Usage & D3DUSAGE_DYNAMIC) { // GLMPRINTF(("-X- DYNAMIC tex usage ignored..")); //FIXME } if (Usage & D3DUSAGE_TEXTURE_SRGB) { key.m_texFlags |= kGLMTexSRGB; } if (Usage & D3DUSAGE_RENDERTARGET) { Assert( !(Usage & D3DUSAGE_DEPTHSTENCIL) ); m_ObjectStats.m_nTotalRenderTargets++; key.m_texFlags |= kGLMTexRenderable; const GLMTexFormatDesc *pFmtDesc = GetFormatDesc( key.m_texFormat ); if ( pFmtDesc->m_glIntFormatSRGB != 0 ) { key.m_texFlags |= kGLMTexSRGB; // this catches callers of CreateTexture who set the "renderable" option - they get an SRGB tex } if (m_ctx->Caps().m_cantAttachSRGB) { // this config can't support SRGB render targets. quietly turn off the sRGB bit. key.m_texFlags &= ~kGLMTexSRGB; } } if ( ( Format == D3DFMT_D16 ) || ( Format == D3DFMT_D24X8 ) || ( Format == D3DFMT_D24S8 ) ) { key.m_texFlags |= kGLMTexIsDepth; } if ( Format == D3DFMT_D24S8 ) { key.m_texFlags |= kGLMTexIsStencil; } key.m_xSize = Width; key.m_ySize = Height; key.m_zSize = 1; CGLMTex *tex = m_ctx->NewTex( &key, Levels, pDebugLabel ); if (!tex) { DXABSTRACT_BREAK_ON_ERROR(); } dxtex->m_tex = tex; dxtex->m_tex->m_srgbFlipCount = 0; m_ObjectStats.m_nTotalSurfaces++; dxtex->m_surfZero = new IDirect3DSurface9; dxtex->m_surfZero->m_restype = (D3DRESOURCETYPE)0; // this is a ref to a tex, not the owner... // do not do an AddRef here. dxtex->m_surfZero->m_device = this; dxtex->m_surfZero->m_desc = dxtex->m_descZero; dxtex->m_surfZero->m_tex = tex; dxtex->m_surfZero->m_face = 0; dxtex->m_surfZero->m_mip = 0; GLMPRINTF(("-A- IDirect3DDevice9::CreateTexture created '%s' @ %08x (GLM %08x) %s",tex->m_layout->m_layoutSummary, dxtex, tex, pDebugLabel ? pDebugLabel : "" )); *ppTexture = dxtex; GLMPRINTF(("<-A-IDirect3DDevice9::CreateTexture")); return S_OK; } IDirect3DTexture9::~IDirect3DTexture9() { GL_BATCH_PERF_CALL_TIMER; GL_PUBLIC_ENTRYPOINT_CHECKS( m_device ); GLMPRINTF(( ">-A- IDirect3DTexture9" )); // IDirect3DBaseTexture9::~IDirect3DBaseTexture9 frees up m_tex // we take care of surfZero if (m_device) { m_device->ReleasedTexture( this ); if (m_surfZero) { ULONG refc = m_surfZero->Release( 0, "~IDirect3DTexture9 public release (surfZero)" ); (void)refc; Assert( !refc ); m_surfZero = NULL; } // leave m_device alone! } GLMPRINTF(( "<-A- IDirect3DTexture9" )); } HRESULT IDirect3DTexture9::LockRect(UINT Level,D3DLOCKED_RECT* pLockedRect,CONST RECT* pRect,DWORD Flags) { GL_BATCH_PERF_CALL_TIMER; GL_PUBLIC_ENTRYPOINT_CHECKS( m_device ); DXABSTRACT_BREAK_ON_ERROR(); return S_OK; } HRESULT IDirect3DTexture9::UnlockRect(UINT Level) { GL_BATCH_PERF_CALL_TIMER; GL_PUBLIC_ENTRYPOINT_CHECKS( m_device ); DXABSTRACT_BREAK_ON_ERROR(); return S_OK; } HRESULT IDirect3DTexture9::GetSurfaceLevel(UINT Level,IDirect3DSurface9** ppSurfaceLevel) { GL_BATCH_PERF_CALL_TIMER; GL_PUBLIC_ENTRYPOINT_CHECKS( m_device ); // we create and pass back a surface, and the client is on the hook to release it. tidy. m_device->m_ObjectStats.m_nTotalSurfaces++; IDirect3DSurface9 *surf = new IDirect3DSurface9; surf->m_restype = (D3DRESOURCETYPE)0; // 0 is special and means this 'surface' does not own its m_tex // Dicey...higher level code seems to want this and not want this. Are we missing some AddRef/Release behavior elsewhere? // trying to turn this off - experimental - 26Oct2010 surf->AddRef(); surf->m_device = this->m_device; GLMTexLayoutSlice *slice = &m_tex->m_layout->m_slices[ m_tex->CalcSliceIndex( 0, Level ) ]; surf->m_desc = m_descZero; surf->m_desc.Width = slice->m_xSize; surf->m_desc.Height = slice->m_ySize; surf->m_tex = m_tex; surf->m_face = 0; surf->m_mip = Level; *ppSurfaceLevel = surf; return S_OK; } // ------------------------------------------------------------------------------------------------------------------------------ // #ifdef OSX #pragma mark ----- IDirect3DCubeTexture9 #endif HRESULT IDirect3DDevice9::CreateCubeTexture(UINT EdgeLength,UINT Levels,DWORD Usage,D3DFORMAT Format,D3DPOOL Pool,IDirect3DCubeTexture9** ppCubeTexture,VD3DHANDLE* pSharedHandle, char *pDebugLabel) { GL_BATCH_PERF_CALL_TIMER; Assert( m_ctx->m_nCurOwnerThreadId == ThreadGetCurrentId() ); GLMPRINTF((">-A- IDirect3DDevice9::CreateCubeTexture")); Assert( m_ctx->m_nCurOwnerThreadId == ThreadGetCurrentId() ); m_ObjectStats.m_nTotalTextures++; IDirect3DCubeTexture9 *dxtex = new IDirect3DCubeTexture9; dxtex->m_restype = D3DRTYPE_CUBETEXTURE; dxtex->m_device = this; dxtex->m_descZero.Format = Format; dxtex->m_descZero.Type = D3DRTYPE_CUBETEXTURE; dxtex->m_descZero.Usage = Usage; dxtex->m_descZero.Pool = Pool; dxtex->m_descZero.MultiSampleType = D3DMULTISAMPLE_NONE; dxtex->m_descZero.MultiSampleQuality = 0; dxtex->m_descZero.Width = EdgeLength; dxtex->m_descZero.Height = EdgeLength; GLMTexLayoutKey key; memset( &key, 0, sizeof(key) ); key.m_texGLTarget = GL_TEXTURE_CUBE_MAP; key.m_texFormat = Format; if (Levels>1) { key.m_texFlags |= kGLMTexMipped; } // http://msdn.microsoft.com/en-us/library/bb172625(VS.85).aspx // complain if any usage bits come down that I don't know. uint knownUsageBits = (D3DUSAGE_AUTOGENMIPMAP | D3DUSAGE_RENDERTARGET | D3DUSAGE_DYNAMIC | D3DUSAGE_TEXTURE_SRGB); if ( (Usage & knownUsageBits) != Usage ) { DXABSTRACT_BREAK_ON_ERROR(); } if (Usage & D3DUSAGE_AUTOGENMIPMAP) { key.m_texFlags |= kGLMTexMipped | kGLMTexMippedAuto; } if (Usage & D3DUSAGE_RENDERTARGET) { key.m_texFlags |= kGLMTexRenderable; m_ObjectStats.m_nTotalRenderTargets++; } if (Usage & D3DUSAGE_DYNAMIC) { //GLMPRINTF(("-X- DYNAMIC tex usage ignored..")); //FIXME } if (Usage & D3DUSAGE_TEXTURE_SRGB) { key.m_texFlags |= kGLMTexSRGB; } key.m_xSize = EdgeLength; key.m_ySize = EdgeLength; key.m_zSize = 1; CGLMTex *tex = m_ctx->NewTex( &key, Levels, pDebugLabel ); if (!tex) { DXABSTRACT_BREAK_ON_ERROR(); } dxtex->m_tex = tex; dxtex->m_tex->m_srgbFlipCount = 0; for( int face = 0; face < 6; face ++) { m_ObjectStats.m_nTotalSurfaces++; dxtex->m_surfZero[face] = new IDirect3DSurface9; dxtex->m_surfZero[face]->m_restype = (D3DRESOURCETYPE)0; // 0 is special and means this 'surface' does not own its m_tex // do not do an AddRef here. dxtex->m_surfZero[face]->m_device = this; dxtex->m_surfZero[face]->m_desc = dxtex->m_descZero; dxtex->m_surfZero[face]->m_tex = tex; dxtex->m_surfZero[face]->m_face = face; dxtex->m_surfZero[face]->m_mip = 0; } GLMPRINTF(("-A- IDirect3DDevice9::CreateCubeTexture created '%s' @ %08x (GLM %08x)",tex->m_layout->m_layoutSummary, dxtex, tex )); *ppCubeTexture = dxtex; GLMPRINTF(("<-A- IDirect3DDevice9::CreateCubeTexture")); return S_OK; } IDirect3DCubeTexture9::~IDirect3DCubeTexture9() { GL_BATCH_PERF_CALL_TIMER; GL_PUBLIC_ENTRYPOINT_CHECKS( m_device ); GLMPRINTF(( ">-A- ~IDirect3DCubeTexture9" )); if (m_device) { GLMPRINTF(( "-A- ~IDirect3DCubeTexture9 taking normal delete path on %08x, device is %08x, surfzero[0] is %08x ", this, m_device, m_surfZero[0] )); m_device->ReleasedTexture( this ); // let IDirect3DBaseTexture9::~IDirect3DBaseTexture9 free up m_tex // we handle the surfZero array for the faces for( int face = 0; face < 6; face ++) { if (m_surfZero[face]) { Assert( m_surfZero[face]->m_device == m_device ); ULONG refc = m_surfZero[face]->Release( 0, "~IDirect3DCubeTexture9 public release (surfZero)"); if ( refc!=0 ) { GLMPRINTF(( "-A- ~IDirect3DCubeTexture9 seeing non zero refcount on surfzero[%d] => %d ", face, refc )); } m_surfZero[face] = NULL; } } // leave m_device alone! } else { GLMPRINTF(( "-A- ~IDirect3DCubeTexture9 taking strange delete path on %08x, device is %08x, surfzero[0] is %08x ", this, m_device, m_surfZero[0] )); } GLMPRINTF(( "<-A- ~IDirect3DCubeTexture9" )); } HRESULT IDirect3DCubeTexture9::GetCubeMapSurface(D3DCUBEMAP_FACES FaceType,UINT Level,IDirect3DSurface9** ppCubeMapSurface) { GL_BATCH_PERF_CALL_TIMER; GL_PUBLIC_ENTRYPOINT_CHECKS( m_device ); // we create and pass back a surface, and the client is on the hook to release it... m_device->m_ObjectStats.m_nTotalSurfaces++; IDirect3DSurface9 *surf = new IDirect3DSurface9; surf->m_restype = (D3DRESOURCETYPE)0; // 0 is special and means this 'surface' does not own its m_tex GLMTexLayoutSlice *slice = &m_tex->m_layout->m_slices[ m_tex->CalcSliceIndex( FaceType, Level ) ]; surf->m_device = this->m_device; surf->m_desc = m_descZero; surf->m_desc.Width = slice->m_xSize; surf->m_desc.Height = slice->m_ySize; surf->m_tex = m_tex; surf->m_face = FaceType; surf->m_mip = Level; *ppCubeMapSurface = surf; return S_OK; } HRESULT IDirect3DCubeTexture9::GetLevelDesc(UINT Level,D3DSURFACE_DESC *pDesc) { GL_BATCH_PERF_CALL_TIMER; Assert (Level < static_cast(m_tex->m_layout->m_mipCount)); D3DSURFACE_DESC result = m_descZero; // then mutate it for the level of interest GLMTexLayoutSlice *slice = &m_tex->m_layout->m_slices[ m_tex->CalcSliceIndex( 0, Level ) ]; result.Width = slice->m_xSize; result.Height = slice->m_ySize; *pDesc = result; return S_OK; } // ------------------------------------------------------------------------------------------------------------------------------ // #ifdef OSX #pragma mark ----- IDirect3DVolumeTexture9 #endif HRESULT IDirect3DDevice9::CreateVolumeTexture(UINT Width,UINT Height,UINT Depth,UINT Levels,DWORD Usage,D3DFORMAT Format,D3DPOOL Pool,IDirect3DVolumeTexture9** ppVolumeTexture,VD3DHANDLE* pSharedHandle, char *pDebugLabel) { GL_BATCH_PERF_CALL_TIMER; GLMPRINTF((">-A- IDirect3DDevice9::CreateVolumeTexture")); // set dxtex->m_restype to D3DRTYPE_VOLUMETEXTURE... Assert( m_ctx->m_nCurOwnerThreadId == ThreadGetCurrentId() ); m_ObjectStats.m_nTotalTextures++; IDirect3DVolumeTexture9 *dxtex = new IDirect3DVolumeTexture9; dxtex->m_restype = D3DRTYPE_VOLUMETEXTURE; dxtex->m_device = this; dxtex->m_descZero.Format = Format; dxtex->m_descZero.Type = D3DRTYPE_VOLUMETEXTURE; dxtex->m_descZero.Usage = Usage; dxtex->m_descZero.Pool = Pool; dxtex->m_descZero.MultiSampleType = D3DMULTISAMPLE_NONE; dxtex->m_descZero.MultiSampleQuality = 0; dxtex->m_descZero.Width = Width; dxtex->m_descZero.Height = Height; // also a volume specific desc dxtex->m_volDescZero.Format = Format; dxtex->m_volDescZero.Type = D3DRTYPE_VOLUMETEXTURE; dxtex->m_volDescZero.Usage = Usage; dxtex->m_volDescZero.Pool = Pool; dxtex->m_volDescZero.Width = Width; dxtex->m_volDescZero.Height = Height; dxtex->m_volDescZero.Depth = Depth; GLMTexLayoutKey key; memset( &key, 0, sizeof(key) ); key.m_texGLTarget = GL_TEXTURE_3D; key.m_texFormat = Format; if (Levels>1) { key.m_texFlags |= kGLMTexMipped; } // http://msdn.microsoft.com/en-us/library/bb172625(VS.85).aspx // complain if any usage bits come down that I don't know. uint knownUsageBits = (D3DUSAGE_AUTOGENMIPMAP | D3DUSAGE_RENDERTARGET | D3DUSAGE_DYNAMIC | D3DUSAGE_TEXTURE_SRGB); if ( (Usage & knownUsageBits) != Usage ) { DXABSTRACT_BREAK_ON_ERROR(); } if (Usage & D3DUSAGE_AUTOGENMIPMAP) { key.m_texFlags |= kGLMTexMipped | kGLMTexMippedAuto; } if (Usage & D3DUSAGE_RENDERTARGET) { key.m_texFlags |= kGLMTexRenderable; m_ObjectStats.m_nTotalRenderTargets++; } if (Usage & D3DUSAGE_DYNAMIC) { GLMPRINTF(("-X- DYNAMIC tex usage ignored..")); //FIXME } if (Usage & D3DUSAGE_TEXTURE_SRGB) { key.m_texFlags |= kGLMTexSRGB; } key.m_xSize = Width; key.m_ySize = Height; key.m_zSize = Depth; CGLMTex *tex = m_ctx->NewTex( &key, Levels, pDebugLabel ); if (!tex) { DXABSTRACT_BREAK_ON_ERROR(); } dxtex->m_tex = tex; dxtex->m_tex->m_srgbFlipCount = 0; m_ObjectStats.m_nTotalSurfaces++; dxtex->m_surfZero = new IDirect3DSurface9; dxtex->m_surfZero->m_restype = (D3DRESOURCETYPE)0; // this is a ref to a tex, not the owner... // do not do an AddRef here. dxtex->m_surfZero->m_device = this; dxtex->m_surfZero->m_desc = dxtex->m_descZero; dxtex->m_surfZero->m_tex = tex; dxtex->m_surfZero->m_face = 0; dxtex->m_surfZero->m_mip = 0; GLMPRINTF(("-A- IDirect3DDevice9::CreateVolumeTexture created '%s' @ %08x (GLM %08x)",tex->m_layout->m_layoutSummary, dxtex, tex )); *ppVolumeTexture = dxtex; GLMPRINTF(("<-A- IDirect3DDevice9::CreateVolumeTexture")); return S_OK; } IDirect3DVolumeTexture9::~IDirect3DVolumeTexture9() { GL_BATCH_PERF_CALL_TIMER; GLMPRINTF((">-A- ~IDirect3DVolumeTexture9")); if (m_device) { m_device->ReleasedTexture( this ); // let IDirect3DBaseTexture9::~IDirect3DBaseTexture9 free up m_tex // we handle m_surfZero if (m_surfZero) { ULONG refc = m_surfZero->Release( 0, "~IDirect3DVolumeTexture9 public release (surfZero)" ); (void)refc; Assert( !refc ); m_surfZero = NULL; } // leave m_device alone! } GLMPRINTF(("<-A- ~IDirect3DVolumeTexture9")); } HRESULT IDirect3DVolumeTexture9::LockBox(UINT Level,D3DLOCKED_BOX* pLockedVolume,CONST D3DBOX* pBox,DWORD Flags) { GL_BATCH_PERF_CALL_TIMER; GL_PUBLIC_ENTRYPOINT_CHECKS( m_device ); GLMTexLockParams lockreq; memset( &lockreq, 0, sizeof(lockreq) ); lockreq.m_tex = this->m_tex; lockreq.m_face = 0; lockreq.m_mip = Level; lockreq.m_region.xmin = pBox->Left; lockreq.m_region.ymin = pBox->Top; lockreq.m_region.zmin = pBox->Front; lockreq.m_region.xmax = pBox->Right; lockreq.m_region.ymax = pBox->Bottom; lockreq.m_region.zmax = pBox->Back; char *lockAddress; int yStride; int zStride; lockreq.m_tex->Lock( &lockreq, &lockAddress, &yStride, &zStride ); pLockedVolume->RowPitch = yStride; pLockedVolume->SlicePitch = yStride; pLockedVolume->pBits = lockAddress; return S_OK; } HRESULT IDirect3DVolumeTexture9::UnlockBox(UINT Level) { GL_BATCH_PERF_CALL_TIMER; GL_PUBLIC_ENTRYPOINT_CHECKS( m_device ); GLMTexLockParams lockreq; memset( &lockreq, 0, sizeof(lockreq) ); lockreq.m_tex = this->m_tex; lockreq.m_face = 0; lockreq.m_mip = Level; this->m_tex->Unlock( &lockreq ); return S_OK; } HRESULT IDirect3DVolumeTexture9::GetLevelDesc( UINT Level, D3DVOLUME_DESC *pDesc ) { GL_BATCH_PERF_CALL_TIMER; GL_PUBLIC_ENTRYPOINT_CHECKS( m_device ); if (Level > static_cast(m_tex->m_layout->m_mipCount)) { DXABSTRACT_BREAK_ON_ERROR(); } D3DVOLUME_DESC result = m_volDescZero; // then mutate it for the level of interest GLMTexLayoutSlice *slice = &m_tex->m_layout->m_slices[ m_tex->CalcSliceIndex( 0, Level ) ]; result.Width = slice->m_xSize; result.Height = slice->m_ySize; result.Depth = slice->m_zSize; *pDesc = result; return S_OK; } // ------------------------------------------------------------------------------------------------------------------------------ // #ifdef OSX #pragma mark ----- IDirect3DSurface9 #endif IDirect3DSurface9::~IDirect3DSurface9() { GL_BATCH_PERF_CALL_TIMER; GL_PUBLIC_ENTRYPOINT_CHECKS( m_device ); // not much to do here, but good to verify that these things are being freed (and they are) //GLMPRINTF(("-A- ~IDirect3DSurface9 - signpost")); if (m_device) { GLMPRINTF(("-A- ~IDirect3DSurface9 - taking real delete path on %08x device %08x", this, m_device)); m_device->ReleasedSurface( this ); memset( &m_desc, 0, sizeof(m_desc) ); if (m_restype != 0) // signal that we are a surface that owns this tex (render target) { if (m_tex) { GLMPRINTF(("-A- ~IDirect3DSurface9 deleted '%s' @ %08x (GLM %08x) %s",m_tex->m_layout->m_layoutSummary, this, m_tex, m_tex->m_debugLabel ? m_tex->m_debugLabel : "" )); m_device->ReleasedCGLMTex( m_tex ); m_tex->m_ctx->DelTex( m_tex ); m_tex = NULL; } else { GLMPRINTF(( "-A- ~IDirect3DSurface9 : whoops, no tex to delete here ?" )); } } else { m_tex = NULL; // we are just a view on the tex, we don't own the tex, do not delete it } m_face = m_mip = 0; m_device = NULL; } else { GLMPRINTF(("-A- ~IDirect3DSurface9 - taking strange delete path on %08x device %08x", this, m_device)); } } HRESULT IDirect3DSurface9::LockRect(D3DLOCKED_RECT* pLockedRect,CONST RECT* pRect,DWORD Flags) { GL_BATCH_PERF_CALL_TIMER; GL_PUBLIC_ENTRYPOINT_CHECKS( m_device ); GLMTexLockParams lockreq; memset( &lockreq, 0, sizeof(lockreq) ); lockreq.m_tex = this->m_tex; lockreq.m_face = this->m_face; lockreq.m_mip = this->m_mip; lockreq.m_region.xmin = pRect->left; lockreq.m_region.ymin = pRect->top; lockreq.m_region.zmin = 0; lockreq.m_region.xmax = pRect->right; lockreq.m_region.ymax = pRect->bottom; lockreq.m_region.zmax = 1; if ((Flags & (D3DLOCK_READONLY | D3DLOCK_NOSYSLOCK)) == (D3DLOCK_READONLY | D3DLOCK_NOSYSLOCK) ) { // smells like readback, force texel readout lockreq.m_readback = true; } char *lockAddress; int yStride; int zStride; lockreq.m_tex->Lock( &lockreq, &lockAddress, &yStride, &zStride ); pLockedRect->Pitch = yStride; pLockedRect->pBits = lockAddress; return S_OK; } HRESULT IDirect3DSurface9::UnlockRect() { GL_BATCH_PERF_CALL_TIMER; GL_PUBLIC_ENTRYPOINT_CHECKS( m_device ); GLMTexLockParams lockreq; memset( &lockreq, 0, sizeof(lockreq) ); lockreq.m_tex = this->m_tex; lockreq.m_face = this->m_face; lockreq.m_mip = this->m_mip; lockreq.m_tex->Unlock( &lockreq ); return S_OK; } HRESULT IDirect3DSurface9::GetDesc(D3DSURFACE_DESC *pDesc) { GL_BATCH_PERF_CALL_TIMER; GL_PUBLIC_ENTRYPOINT_CHECKS( m_device ); *pDesc = m_desc; return S_OK; } // ------------------------------------------------------------------------------------------------------------------------------ // #ifdef OSX #pragma mark ----- IDirect3D9 ------------------------------------------------------- #endif IDirect3D9::~IDirect3D9() { GL_BATCH_PERF_CALL_TIMER; GLMPRINTF(("-A- ~IDirect3D9 - signpost")); } UINT IDirect3D9::GetAdapterCount() { GL_BATCH_PERF_CALL_TIMER; GLMgr::NewGLMgr(); // init GL manager GLMDisplayDB *db = GetDisplayDB(); int dxAdapterCount = db->GetFakeAdapterCount(); return dxAdapterCount; } static void FillD3DCaps9( const GLMRendererInfoFields &glmRendererInfo, D3DCAPS9* pCaps ) { // fill in the pCaps record for adapter... we zero most of it and just fill in the fields that we think the caller wants. Q_memset( pCaps, 0, sizeof(*pCaps) ); /* Device Info */ pCaps->DeviceType = D3DDEVTYPE_HAL; /* Caps from DX7 Draw */ pCaps->Caps = 0; // does anyone look at this ? pCaps->Caps2 = D3DCAPS2_DYNAMICTEXTURES; /* Cursor Caps */ pCaps->CursorCaps = 0; // nobody looks at this /* 3D Device Caps */ pCaps->DevCaps = D3DDEVCAPS_HWTRANSFORMANDLIGHT; pCaps->TextureCaps = D3DPTEXTURECAPS_CUBEMAP | D3DPTEXTURECAPS_MIPCUBEMAP | D3DPTEXTURECAPS_NONPOW2CONDITIONAL | D3DPTEXTURECAPS_PROJECTED; // D3DPTEXTURECAPS_NOPROJECTEDBUMPENV ? // D3DPTEXTURECAPS_POW2 ? // caller looks at POT support like this: // pCaps->m_SupportsNonPow2Textures = // ( !( caps.TextureCaps & D3DPTEXTURECAPS_POW2 ) || // ( caps.TextureCaps & D3DPTEXTURECAPS_NONPOW2CONDITIONAL ) ); // so we should set D3DPTEXTURECAPS_NONPOW2CONDITIONAL bit ? pCaps->PrimitiveMiscCaps = 0; //only the HDR setup looks at this for D3DPMISCCAPS_SEPARATEALPHABLEND. // ? D3DPMISCCAPS_SEPARATEALPHABLEND // ? D3DPMISCCAPS_BLENDOP // ? D3DPMISCCAPS_CLIPPLANESCALEDPOINTS // ? D3DPMISCCAPS_CLIPTLVERTS D3DPMISCCAPS_COLORWRITEENABLE D3DPMISCCAPS_MASKZ D3DPMISCCAPS_TSSARGTEMP pCaps->RasterCaps = D3DPRASTERCAPS_SCISSORTEST | D3DPRASTERCAPS_SLOPESCALEDEPTHBIAS // ref'd in CShaderDeviceMgrDx8::ComputeCapsFromD3D | D3DPRASTERCAPS_DEPTHBIAS // ref'd in CShaderDeviceMgrDx8::ComputeCapsFromD3D ; pCaps->TextureFilterCaps = D3DPTFILTERCAPS_MINFANISOTROPIC | D3DPTFILTERCAPS_MAGFANISOTROPIC; pCaps->MaxTextureWidth = 4096; pCaps->MaxTextureHeight = 4096; pCaps->MaxVolumeExtent = 1024; //guesses pCaps->MaxTextureAspectRatio = 0; // imply no limit on AR pCaps->MaxAnisotropy = glmRendererInfo.m_maxAniso; pCaps->TextureOpCaps = D3DTEXOPCAPS_ADD | D3DTEXOPCAPS_MODULATE2X; //guess //DWORD MaxTextureBlendStages; //DWORD MaxSimultaneousTextures; pCaps->VertexProcessingCaps = D3DVTXPCAPS_TEXGEN_SPHEREMAP; pCaps->MaxActiveLights = 8; // guess // MaxUserClipPlanes. A bit complicated.. // it's difficult to make this fluid without teaching the engine about a cap that could change during run. // start it out set to '2'. // turn it off, if we're in GLSL mode but do not have native clip plane capability. pCaps->MaxUserClipPlanes = 2; // assume good news // is user asking for it to be off ? if ( CommandLine()->CheckParm( "-nouserclip" ) ) { pCaps->MaxUserClipPlanes = 0; } pCaps->MaxVertexBlendMatrices = 0; // see if anyone cares pCaps->MaxVertexBlendMatrixIndex = 0; // see if anyone cares pCaps->MaxPrimitiveCount = 32768; // guess pCaps->MaxStreams = D3D_MAX_STREAMS; // guess pCaps->VertexShaderVersion = 0x300; // model 3.0 pCaps->MaxVertexShaderConst = DXABSTRACT_VS_PARAM_SLOTS; // number of vertex shader constant registers pCaps->PixelShaderVersion = 0x300; // model 3.0 // Here are the DX9 specific ones pCaps->DevCaps2 = D3DDEVCAPS2_STREAMOFFSET; pCaps->PS20Caps.NumInstructionSlots = 512; // guess // only examined once: // pCaps->m_SupportsPixelShaders_2_b = ( ( caps.PixelShaderVersion & 0xffff ) >= 0x0200) && (caps.PS20Caps.NumInstructionSlots >= 512); //pCaps->m_SupportsPixelShaders_2_b = 1; pCaps->NumSimultaneousRTs = 1; // Will be at least 1 pCaps->MaxVertexShader30InstructionSlots = 0; pCaps->MaxPixelShader30InstructionSlots = 0; #if DX_TO_GL_ABSTRACTION pCaps->FakeSRGBWrite = !glmRendererInfo.m_hasGammaWrites; pCaps->CanDoSRGBReadFromRTs = !glmRendererInfo.m_cantAttachSRGB; pCaps->MixedSizeTargets = glmRendererInfo.m_hasMixedAttachmentSizes; #endif } HRESULT IDirect3D9::GetDeviceCaps(UINT Adapter, D3DDEVTYPE DeviceType, D3DCAPS9* pCaps) { GL_BATCH_PERF_CALL_TIMER; // Generally called from "CShaderDeviceMgrDx8::ComputeCapsFromD3D" in ShaderDeviceDX8.cpp // "Adapter" is used to index amongst the set of fake-adapters maintained in the display DB GLMDisplayDB *db = GetDisplayDB(); int glmRendererIndex = -1; int glmDisplayIndex = -1; GLMRendererInfoFields glmRendererInfo; GLMDisplayInfoFields glmDisplayInfo; bool result = db->GetFakeAdapterInfo( Adapter, &glmRendererIndex, &glmDisplayIndex, &glmRendererInfo, &glmDisplayInfo ); (void)result; Assert (!result); // just leave glmRendererInfo filled out for subsequent code to look at as needed. FillD3DCaps9( glmRendererInfo, pCaps ); return S_OK; } HRESULT IDirect3D9::GetAdapterIdentifier( UINT Adapter, DWORD Flags, D3DADAPTER_IDENTIFIER9* pIdentifier ) { GL_BATCH_PERF_CALL_TIMER; // Generally called from "CShaderDeviceMgrDx8::ComputeCapsFromD3D" in ShaderDeviceDX8.cpp Assert( Flags == D3DENUM_WHQL_LEVEL ); // we're not handling any other queries than this yet Q_memset( pIdentifier, 0, sizeof(*pIdentifier) ); GLMDisplayDB *db = GetDisplayDB(); int glmRendererIndex = -1; int glmDisplayIndex = -1; GLMRendererInfoFields glmRendererInfo; GLMDisplayInfoFields glmDisplayInfo; // the D3D "Adapter" number feeds the fake adapter index bool result = db->GetFakeAdapterInfo( Adapter, &glmRendererIndex, &glmDisplayIndex, &glmRendererInfo, &glmDisplayInfo ); (void)result; Assert (!result); #ifndef OSX if( glmRendererInfo.m_rendererID ) #endif { const char *pRenderer = GLMDecode( eGL_RENDERER, glmRendererInfo.m_rendererID & 0x00FFFF00 ); Q_snprintf( pIdentifier->Driver, sizeof(pIdentifier->Driver), "OpenGL %s (%08x)", pRenderer, glmRendererInfo.m_rendererID ); Q_snprintf( pIdentifier->Description, sizeof(pIdentifier->Description), "%s - %dx%d - %dMB VRAM", pRenderer, glmDisplayInfo.m_displayPixelWidth, glmDisplayInfo.m_displayPixelHeight, glmRendererInfo.m_vidMemory >> 20 ); } #ifndef OSX else { static CDynamicFunctionOpenGL< true, const GLubyte *( APIENTRY *)(GLenum name), const GLubyte * > glGetString("glGetString"); const char *pszStringVendor = ( const char * )glGetString( GL_VENDOR ); // NVIDIA Corporation const char *pszStringRenderer = ( const char * )glGetString( GL_RENDERER ); // GeForce GTX 680/PCIe/SSE2 const char *pszStringVersion = ( const char * )glGetString( GL_VERSION ); // 4.2.0 NVIDIA 304.22 Q_snprintf( pIdentifier->Driver, sizeof( pIdentifier->Driver ), "OpenGL %s (%s)", pszStringVendor, pszStringRenderer ); Q_snprintf( pIdentifier->Description, sizeof( pIdentifier->Description ), "%s (%s) %s - %dx%d", pszStringVendor, pszStringRenderer, pszStringVersion, glmDisplayInfo.m_displayPixelWidth, glmDisplayInfo.m_displayPixelHeight ); } #endif // !OSX pIdentifier->VendorId = glmRendererInfo.m_pciVendorID; // 4318; pIdentifier->DeviceId = glmRendererInfo.m_pciDeviceID; // 401; pIdentifier->SubSysId = 0; // 3358668866; pIdentifier->Revision = 0; // 162; pIdentifier->VideoMemory = glmRendererInfo.m_vidMemory; // amount of video memory in bytes #if 0 // this came from the shaderapigl effort Q_strncpy( pIdentifier->Driver, "Fake-Video-Card", MAX_DEVICE_IDENTIFIER_STRING ); Q_strncpy( pIdentifier->Description, "Fake-Video-Card", MAX_DEVICE_IDENTIFIER_STRING ); pIdentifier->VendorId = 4318; pIdentifier->DeviceId = 401; pIdentifier->SubSysId = 3358668866; pIdentifier->Revision = 162; #endif return S_OK; } HRESULT IDirect3D9::CheckDeviceFormat(UINT Adapter,D3DDEVTYPE DeviceType,D3DFORMAT AdapterFormat,DWORD Usage,D3DRESOURCETYPE RType,D3DFORMAT CheckFormat) { GL_BATCH_PERF_CALL_TIMER; if (0) // hush for now, less spew { GLMPRINTF(("-X- ** IDirect3D9::CheckDeviceFormat: \n -- Adapter=%d || DeviceType=%4x:%s || AdapterFormat=%8x:%s\n -- RType %8x: %s\n -- CheckFormat %8x: %s\n -- Usage %8x: %s", Adapter, DeviceType, GLMDecode(eD3D_DEVTYPE, DeviceType), AdapterFormat, GLMDecode(eD3D_FORMAT, AdapterFormat), RType, GLMDecode(eD3D_RTYPE, RType), CheckFormat, GLMDecode(eD3D_FORMAT, CheckFormat), Usage, GLMDecodeMask( eD3D_USAGE, Usage ) )); } HRESULT result = D3DERR_NOTAVAILABLE; // failure DWORD knownUsageMask = D3DUSAGE_RENDERTARGET | D3DUSAGE_DEPTHSTENCIL | D3DUSAGE_DYNAMIC | D3DUSAGE_AUTOGENMIPMAP | D3DUSAGE_QUERY_SRGBREAD | D3DUSAGE_QUERY_FILTER | D3DUSAGE_QUERY_SRGBWRITE | D3DUSAGE_QUERY_POSTPIXELSHADER_BLENDING | D3DUSAGE_QUERY_VERTEXTEXTURE; (void)knownUsageMask; // FramebufferSRGB stuff. // basically a format is only allowed to have SRGB usage for writing, if you have the framebuffer SRGB extension. // so, check for that capability with GLM adapter db, and if it's not there, don't mark that bit as usable in any of our formats. GLMDisplayDB *db = GetDisplayDB(); int glmRendererIndex = -1; int glmDisplayIndex = -1; GLMRendererInfoFields glmRendererInfo; GLMDisplayInfoFields glmDisplayInfo; bool dbresult = db->GetFakeAdapterInfo( Adapter, &glmRendererIndex, &glmDisplayIndex, &glmRendererInfo, &glmDisplayInfo ); (void)dbresult; Assert (!dbresult); Assert ((Usage & knownUsageMask) == Usage); DWORD legalUsage = 0; switch( AdapterFormat ) { case D3DFMT_X8R8G8B8: switch( RType ) { case D3DRTYPE_TEXTURE: switch( CheckFormat ) { case D3DFMT_DXT1: case D3DFMT_DXT3: case D3DFMT_DXT5: legalUsage = D3DUSAGE_DYNAMIC | D3DUSAGE_AUTOGENMIPMAP | D3DUSAGE_QUERY_FILTER; legalUsage |= D3DUSAGE_QUERY_SRGBREAD; //open question: is auto gen of mipmaps is allowed or attempted on any DXT textures. break; case D3DFMT_A8R8G8B8: legalUsage = D3DUSAGE_DYNAMIC | D3DUSAGE_AUTOGENMIPMAP | D3DUSAGE_QUERY_FILTER; legalUsage |= D3DUSAGE_RENDERTARGET | D3DUSAGE_QUERY_SRGBREAD | D3DUSAGE_QUERY_SRGBWRITE | D3DUSAGE_QUERY_POSTPIXELSHADER_BLENDING; break; //$ TODO: Need to merge bitmap changes over from Dota to get these formats. #if 0 case D3DFMT_A2R10G10B10: legalUsage = D3DUSAGE_DYNAMIC | D3DUSAGE_AUTOGENMIPMAP | D3DUSAGE_QUERY_FILTER; legalUsage |= D3DUSAGE_RENDERTARGET | D3DUSAGE_QUERY_SRGBREAD | D3DUSAGE_QUERY_SRGBWRITE | D3DUSAGE_QUERY_POSTPIXELSHADER_BLENDING; break; case D3DFMT_A2B10G10R10: legalUsage = D3DUSAGE_DYNAMIC | D3DUSAGE_AUTOGENMIPMAP | D3DUSAGE_QUERY_FILTER; legalUsage |= D3DUSAGE_RENDERTARGET | D3DUSAGE_QUERY_SRGBREAD | D3DUSAGE_QUERY_SRGBWRITE | D3DUSAGE_QUERY_POSTPIXELSHADER_BLENDING; break; #endif case D3DFMT_R32F: legalUsage = D3DUSAGE_DYNAMIC | D3DUSAGE_AUTOGENMIPMAP | D3DUSAGE_QUERY_FILTER; legalUsage |= D3DUSAGE_RENDERTARGET | D3DUSAGE_QUERY_SRGBREAD | D3DUSAGE_QUERY_SRGBWRITE | D3DUSAGE_QUERY_POSTPIXELSHADER_BLENDING; break; case D3DFMT_A16B16G16R16: legalUsage = D3DUSAGE_DYNAMIC | D3DUSAGE_AUTOGENMIPMAP | D3DUSAGE_QUERY_FILTER; legalUsage |= D3DUSAGE_RENDERTARGET | D3DUSAGE_QUERY_SRGBREAD | D3DUSAGE_QUERY_SRGBWRITE | D3DUSAGE_QUERY_POSTPIXELSHADER_BLENDING; break; case D3DFMT_A16B16G16R16F: legalUsage = D3DUSAGE_DYNAMIC | D3DUSAGE_AUTOGENMIPMAP | D3DUSAGE_RENDERTARGET | D3DUSAGE_QUERY_SRGBREAD | D3DUSAGE_QUERY_SRGBWRITE; if ( !glmRendererInfo.m_atiR5xx ) { legalUsage |= D3DUSAGE_QUERY_FILTER | D3DUSAGE_QUERY_POSTPIXELSHADER_BLENDING; } break; case D3DFMT_A32B32G32R32F: legalUsage = D3DUSAGE_DYNAMIC | D3DUSAGE_AUTOGENMIPMAP | D3DUSAGE_RENDERTARGET | D3DUSAGE_QUERY_SRGBREAD | D3DUSAGE_QUERY_SRGBWRITE; if ( !glmRendererInfo.m_atiR5xx && !glmRendererInfo.m_nvG7x ) { legalUsage |= D3DUSAGE_QUERY_FILTER | D3DUSAGE_QUERY_POSTPIXELSHADER_BLENDING; } break; case D3DFMT_R5G6B5: legalUsage = D3DUSAGE_DYNAMIC | D3DUSAGE_AUTOGENMIPMAP | D3DUSAGE_QUERY_FILTER; break; //----------------------------------------------------------- // these come in from TestTextureFormat in ColorFormatDX8.cpp which is being driven by InitializeColorInformation... // which is going to try all 8 combinations of (vertex texturable / render targetable / filterable ) on every image format it knows. case D3DFMT_R8G8B8: legalUsage = D3DUSAGE_DYNAMIC | D3DUSAGE_AUTOGENMIPMAP | D3DUSAGE_QUERY_FILTER; legalUsage |= D3DUSAGE_QUERY_SRGBREAD; break; case D3DFMT_X8R8G8B8: legalUsage = D3DUSAGE_DYNAMIC | D3DUSAGE_AUTOGENMIPMAP | D3DUSAGE_QUERY_FILTER; legalUsage |= D3DUSAGE_QUERY_SRGBREAD | D3DUSAGE_QUERY_SRGBWRITE; break; // one and two channel textures... we'll have to fake these as four channel tex if we want to support them case D3DFMT_L8: legalUsage = D3DUSAGE_DYNAMIC | D3DUSAGE_AUTOGENMIPMAP | D3DUSAGE_QUERY_FILTER; break; case D3DFMT_A8L8: legalUsage = D3DUSAGE_DYNAMIC | D3DUSAGE_AUTOGENMIPMAP | D3DUSAGE_QUERY_FILTER; break; case D3DFMT_A8: legalUsage = D3DUSAGE_DYNAMIC | D3DUSAGE_AUTOGENMIPMAP | D3DUSAGE_QUERY_FILTER; break; // going to need to go back and double check all of these.. case D3DFMT_X1R5G5B5: legalUsage = D3DUSAGE_DYNAMIC | D3DUSAGE_AUTOGENMIPMAP | D3DUSAGE_QUERY_FILTER; break; case D3DFMT_A4R4G4B4: legalUsage = D3DUSAGE_DYNAMIC | D3DUSAGE_AUTOGENMIPMAP | D3DUSAGE_QUERY_FILTER; break; case D3DFMT_A1R5G5B5: legalUsage = D3DUSAGE_DYNAMIC | D3DUSAGE_AUTOGENMIPMAP | D3DUSAGE_QUERY_FILTER; break; case D3DFMT_V8U8: legalUsage = D3DUSAGE_DYNAMIC | D3DUSAGE_AUTOGENMIPMAP | D3DUSAGE_QUERY_FILTER; break; case D3DFMT_Q8W8V8U8: legalUsage = D3DUSAGE_DYNAMIC | D3DUSAGE_AUTOGENMIPMAP | D3DUSAGE_QUERY_FILTER; // what the heck is QWVU8 ... ? break; case D3DFMT_X8L8V8U8: legalUsage = D3DUSAGE_DYNAMIC | D3DUSAGE_AUTOGENMIPMAP | D3DUSAGE_QUERY_FILTER; // what the heck is XLVU8 ... ? break; // formats with depth... case D3DFMT_D16: legalUsage = D3DUSAGE_DYNAMIC | D3DUSAGE_RENDERTARGET | D3DUSAGE_DEPTHSTENCIL; // just a guess on the legal usages break; case D3DFMT_D24S8: legalUsage = D3DUSAGE_DYNAMIC | D3DUSAGE_RENDERTARGET | D3DUSAGE_DEPTHSTENCIL; // just a guess on the legal usages break; // vendor formats... try marking these all invalid for now case D3DFMT_NV_INTZ: case D3DFMT_NV_RAWZ: case D3DFMT_NV_NULL: case D3DFMT_ATI_D16: case D3DFMT_ATI_D24S8: case D3DFMT_ATI_2N: case D3DFMT_ATI_1N: legalUsage = 0; break; //----------------------------------------------------------- default: Assert(!"Unknown check format"); result = D3DERR_NOTAVAILABLE; break; } if ((Usage & legalUsage) == Usage) { result = S_OK; } else { DWORD unsatBits = Usage & (~legalUsage); // clear the bits of the req that were legal, leaving the illegal ones unsatBits; GLMPRINTF(( "-X- --> NOT OK: flags %8x:%s", unsatBits,GLMDecodeMask( eD3D_USAGE, unsatBits ) )); result = D3DERR_NOTAVAILABLE; } break; case D3DRTYPE_SURFACE: switch( static_cast(CheckFormat) ) { case 0x434f5441: case 0x41415353: result = D3DERR_NOTAVAILABLE; break; case D3DFMT_D24S8: result = S_OK; break; //** IDirect3D9::CheckDeviceFormat adapter=0, DeviceType= 1:D3DDEVTYPE_HAL, AdapterFormat= 5:D3DFMT_X8R8G8B8, RType= 1:D3DRTYPE_SURFACE, CheckFormat=434f5441:UNKNOWN //** IDirect3D9::CheckDeviceFormat adapter=0, DeviceType= 1:D3DDEVTYPE_HAL, AdapterFormat= 5:D3DFMT_X8R8G8B8, RType= 1:D3DRTYPE_SURFACE, CheckFormat=41415353:UNKNOWN //** IDirect3D9::CheckDeviceFormat adapter=0, DeviceType= 1:D3DDEVTYPE_HAL, AdapterFormat= 5:D3DFMT_X8R8G8B8, RType= 1:D3DRTYPE_SURFACE, CheckFormat=434f5441:UNKNOWN //** IDirect3D9::CheckDeviceFormat adapter=0, DeviceType= 1:D3DDEVTYPE_HAL, AdapterFormat= 5:D3DFMT_X8R8G8B8, RType= 1:D3DRTYPE_SURFACE, CheckFormat=41415353:UNKNOWN } break; default: Assert(!"Unknown resource type"); result = D3DERR_NOTAVAILABLE; break; } break; default: Assert(!"Unknown adapter format"); result = D3DERR_NOTAVAILABLE; break; } return result; } UINT IDirect3D9::GetAdapterModeCount(UINT Adapter,D3DFORMAT Format) { GL_BATCH_PERF_CALL_TIMER; GLMPRINTF(( "-X- IDirect3D9::GetAdapterModeCount: Adapter=%d || Format=%8x:%s", Adapter, Format, GLMDecode(eD3D_FORMAT, Format) )); uint modeCount=0; GLMDisplayDB *db = GetDisplayDB(); int glmRendererIndex = -1; int glmDisplayIndex = -1; GLMRendererInfoFields glmRendererInfo; GLMDisplayInfoFields glmDisplayInfo; // the D3D "Adapter" number feeds the fake adapter index bool result = db->GetFakeAdapterInfo( Adapter, &glmRendererIndex, &glmDisplayIndex, &glmRendererInfo, &glmDisplayInfo ); (void)result; Assert (!result); modeCount = db->GetModeCount( glmRendererIndex, glmDisplayIndex ); GLMPRINTF(( "-X- --> result is %d", modeCount )); return modeCount; } HRESULT IDirect3D9::EnumAdapterModes(UINT Adapter,D3DFORMAT Format,UINT Mode,D3DDISPLAYMODE* pMode) { GL_BATCH_PERF_CALL_TIMER; GLMPRINTF(( "-X- IDirect3D9::EnumAdapterModes: Adapter=%d || Format=%8x:%s || Mode=%d", Adapter, Format, GLMDecode(eD3D_FORMAT, Format), Mode )); Assert(Format==D3DFMT_X8R8G8B8); GLMDisplayDB *db = GetDisplayDB(); int glmRendererIndex = -1; int glmDisplayIndex = -1; GLMRendererInfoFields glmRendererInfo; GLMDisplayInfoFields glmDisplayInfo; GLMDisplayModeInfoFields glmModeInfo; // the D3D "Adapter" number feeds the fake adapter index bool result = db->GetFakeAdapterInfo( Adapter, &glmRendererIndex, &glmDisplayIndex, &glmRendererInfo, &glmDisplayInfo ); Assert (!result); if (result) return D3DERR_NOTAVAILABLE; bool result2 = db->GetModeInfo( glmRendererIndex, glmDisplayIndex, Mode, &glmModeInfo ); Assert( !result2 ); if (result2) return D3DERR_NOTAVAILABLE; pMode->Width = glmModeInfo.m_modePixelWidth; pMode->Height = glmModeInfo.m_modePixelHeight; pMode->RefreshRate = glmModeInfo.m_modeRefreshHz; // "adapter default" pMode->Format = Format; // whatever you asked for ? GLMPRINTF(( "-X- IDirect3D9::EnumAdapterModes returning mode size (%d,%d) and D3DFMT_X8R8G8B8",pMode->Width,pMode->Height )); return S_OK; } HRESULT IDirect3D9::CheckDeviceType(UINT Adapter,D3DDEVTYPE DevType,D3DFORMAT AdapterFormat,D3DFORMAT BackBufferFormat,BOOL bWindowed) { GL_BATCH_PERF_CALL_TIMER; //FIXME: we just say "OK" on any query GLMPRINTF(( "-X- IDirect3D9::CheckDeviceType: Adapter=%d || DevType=%d:%s || AdapterFormat=%d:%s || BackBufferFormat=%d:%s || bWindowed=%d", Adapter, DevType, GLMDecode(eD3D_DEVTYPE,DevType), AdapterFormat, GLMDecode(eD3D_FORMAT, AdapterFormat), BackBufferFormat, GLMDecode(eD3D_FORMAT, BackBufferFormat), (int) bWindowed )); return S_OK; } HRESULT IDirect3D9::GetAdapterDisplayMode(UINT Adapter,D3DDISPLAYMODE* pMode) { GL_BATCH_PERF_CALL_TIMER; // asking what the current mode is GLMPRINTF(("-X- IDirect3D9::GetAdapterDisplayMode: Adapter=%d", Adapter )); GLMDisplayDB *db = GetDisplayDB(); int glmRendererIndex = -1; int glmDisplayIndex = -1; GLMRendererInfoFields glmRendererInfo; GLMDisplayInfoFields glmDisplayInfo; GLMDisplayModeInfoFields glmModeInfo; // the D3D "Adapter" number feeds the fake adapter index bool result = db->GetFakeAdapterInfo( Adapter, &glmRendererIndex, &glmDisplayIndex, &glmRendererInfo, &glmDisplayInfo ); Assert(!result); if (result) return D3DERR_INVALIDCALL; int modeIndex = -1; // pass -1 as a mode index to find out about whatever the current mode is on the selected display bool modeResult = db->GetModeInfo( glmRendererIndex, glmDisplayIndex, modeIndex, &glmModeInfo ); Assert (!modeResult); if (modeResult) return D3DERR_INVALIDCALL; pMode->Width = glmModeInfo.m_modePixelWidth; pMode->Height = glmModeInfo.m_modePixelHeight; pMode->RefreshRate = glmModeInfo.m_modeRefreshHz; // "adapter default" pMode->Format = D3DFMT_X8R8G8B8; //FIXME, this is a SWAG return S_OK; } HRESULT IDirect3D9::CheckDepthStencilMatch(UINT Adapter,D3DDEVTYPE DeviceType,D3DFORMAT AdapterFormat,D3DFORMAT RenderTargetFormat,D3DFORMAT DepthStencilFormat) { GL_BATCH_PERF_CALL_TIMER; GLMPRINTF(("-X- IDirect3D9::CheckDepthStencilMatch: Adapter=%d || DevType=%d:%s || AdapterFormat=%d:%s || RenderTargetFormat=%d:%s || DepthStencilFormat=%d:%s", Adapter, DeviceType, GLMDecode(eD3D_DEVTYPE,DeviceType), AdapterFormat, GLMDecode(eD3D_FORMAT, AdapterFormat), RenderTargetFormat, GLMDecode(eD3D_FORMAT, RenderTargetFormat), DepthStencilFormat, GLMDecode(eD3D_FORMAT, DepthStencilFormat) )); // one known request looks like this: // AdapterFormat=5:D3DFMT_X8R8G8B8 || RenderTargetFormat=3:D3DFMT_A8R8G8B8 || DepthStencilFormat=2:D3DFMT_D24S8 // return S_OK for that one combo, DXABSTRACT_BREAK_ON_ERROR() on anything else HRESULT result = D3DERR_NOTAVAILABLE; // failure switch( AdapterFormat ) { case D3DFMT_X8R8G8B8: { if ( (RenderTargetFormat == D3DFMT_A8R8G8B8) && (DepthStencilFormat == D3DFMT_D24S8) ) { result = S_OK; } } break; } Assert( result == S_OK ); return result; } HRESULT IDirect3D9::CheckDeviceMultiSampleType( UINT Adapter,D3DDEVTYPE DeviceType,D3DFORMAT SurfaceFormat,BOOL Windowed,D3DMULTISAMPLE_TYPE MultiSampleType,DWORD* pQualityLevels ) { GL_BATCH_PERF_CALL_TIMER; GLMDisplayDB *db = GetDisplayDB(); int glmRendererIndex = -1; int glmDisplayIndex = -1; GLMRendererInfoFields glmRendererInfo; GLMDisplayInfoFields glmDisplayInfo; //GLMDisplayModeInfoFields glmModeInfo; // the D3D "Adapter" number feeds the fake adapter index bool result = db->GetFakeAdapterInfo( Adapter, &glmRendererIndex, &glmDisplayIndex, &glmRendererInfo, &glmDisplayInfo ); Assert( !result ); if ( result ) return D3DERR_INVALIDCALL; if ( !CommandLine()->FindParm("-glmenabletrustmsaa") ) { // These ghetto drivers don't get MSAA if ( ( glmRendererInfo.m_nvG7x || glmRendererInfo.m_atiR5xx ) && ( MultiSampleType > D3DMULTISAMPLE_NONE ) ) { if ( pQualityLevels ) { *pQualityLevels = 0; } return D3DERR_NOTAVAILABLE; } } switch ( MultiSampleType ) { case D3DMULTISAMPLE_NONE: // always return true if ( pQualityLevels ) { *pQualityLevels = 1; } return S_OK; break; case D3DMULTISAMPLE_2_SAMPLES: case D3DMULTISAMPLE_4_SAMPLES: case D3DMULTISAMPLE_6_SAMPLES: case D3DMULTISAMPLE_8_SAMPLES: // note the fact that the d3d enums for 2, 4, 6, 8 samples are equal to 2,4,6,8... if (glmRendererInfo.m_maxSamples >= (int)MultiSampleType ) { if ( pQualityLevels ) { *pQualityLevels = 1; } return S_OK; } else { return D3DERR_NOTAVAILABLE; } break; default: if ( pQualityLevels ) { *pQualityLevels = 0; } return D3DERR_NOTAVAILABLE; break; } return D3DERR_NOTAVAILABLE; } HRESULT IDirect3D9::CreateDevice(UINT Adapter,D3DDEVTYPE DeviceType,VD3DHWND hFocusWindow,DWORD BehaviorFlags,D3DPRESENT_PARAMETERS* pPresentationParameters,IDirect3DDevice9** ppReturnedDeviceInterface) { GL_BATCH_PERF_CALL_TIMER; #if GLMDEBUG GLMDebugPrintf( "WARNING: GLMEBUG is 1, perf. is going to be low!" ); Warning( "WARNING: GLMEBUG is 1, perf. is going to be low!" ); #endif #if !TOGL_SUPPORT_NULL_DEVICE if (DeviceType == D3DDEVTYPE_NULLREF) { Error( "Must define TOGL_SUPPORT_NULL_DEVICE to use the NULL device" ); DebuggerBreak(); return E_FAIL; } #endif // constrain these inputs for the time being // BackBufferFormat -> A8R8G8B8 // BackBufferCount -> 1; // MultiSampleType -> D3DMULTISAMPLE_NONE // AutoDepthStencilFormat -> D3DFMT_D24S8 // NULL out the return pointer so if we exit early it is not set *ppReturnedDeviceInterface = NULL; // assume success unless something is sour HRESULT result = S_OK; // relax this check for now //if (pPresentationParameters->BackBufferFormat != D3DFMT_A8R8G8B8) //{ // DXABSTRACT_BREAK_ON_ERROR(); // result = -1; //} //rbarris 24Aug10 - relaxing this check - we don't care if the game asks for two backbuffers, it's moot //if ( pPresentationParameters->BackBufferCount != 1 ) //{ // DXABSTRACT_BREAK_ON_ERROR(); // result = D3DERR_NOTAVAILABLE; //} if ( pPresentationParameters->AutoDepthStencilFormat != D3DFMT_D24S8 ) { DXABSTRACT_BREAK_ON_ERROR(); result = D3DERR_NOTAVAILABLE; } if ( result == S_OK ) { // create an IDirect3DDevice9 // it will make a GLMContext and set up some drawables IDirect3DDevice9Params devparams; memset( &devparams, 0, sizeof(devparams) ); devparams.m_adapter = Adapter; devparams.m_deviceType = DeviceType; devparams.m_focusWindow = hFocusWindow; // is this meaningful? is this a WindowRef ? follow it up the chain.. devparams.m_behaviorFlags = BehaviorFlags; devparams.m_presentationParameters = *pPresentationParameters; IDirect3DDevice9 *dev = new IDirect3DDevice9; result = dev->Create( &devparams ); if ( result == S_OK ) { *ppReturnedDeviceInterface = dev; } g_bNullD3DDevice = ( DeviceType == D3DDEVTYPE_NULLREF ); } return result; } // ------------------------------------------------------------------------------------------------------------------------------ // #ifdef OSX #pragma mark ----- IDirect3DQuery9 #endif HRESULT IDirect3DQuery9::Issue(DWORD dwIssueFlags) { GL_BATCH_PERF_CALL_TIMER; Assert( m_device->m_nValidMarker == D3D_DEVICE_VALID_MARKER ); // Flags field for Issue // #define D3DISSUE_END (1 << 0) // Tells the runtime to issue the end of a query, changing it's state to "non-signaled". // #define D3DISSUE_BEGIN (1 << 1) // Tells the runtime to issue the beginng of a query. // Make sure calling thread owns the GL context. Assert( m_ctx->m_nCurOwnerThreadId == ThreadGetCurrentId() ); if (dwIssueFlags & D3DISSUE_BEGIN) { m_nIssueStartThreadID = ThreadGetCurrentId(); m_nIssueStartDrawCallIndex = g_nTotalDrawsOrClears; m_nIssueStartFrameIndex = m_ctx->m_nCurFrame; m_nIssueStartQueryCreationCounter = CGLMQuery::s_nTotalOcclusionQueryCreatesOrDeletes; switch( m_type ) { case D3DQUERYTYPE_OCCLUSION: m_query->Start(); // drop "start counter" call into stream break; default: Assert(!"Can't use D3DISSUE_BEGIN on this query"); break; } } if (dwIssueFlags & D3DISSUE_END) { m_nIssueEndThreadID = ThreadGetCurrentId(); m_nIssueEndDrawCallIndex = g_nTotalDrawsOrClears; m_nIssueEndFrameIndex = m_ctx->m_nCurFrame; m_nIssueEndQueryCreationCounter = CGLMQuery::s_nTotalOcclusionQueryCreatesOrDeletes; switch( m_type ) { case D3DQUERYTYPE_OCCLUSION: m_query->Stop(); // drop "end counter" call into stream break; case D3DQUERYTYPE_EVENT: m_nIssueStartThreadID = m_nIssueEndThreadID; m_nIssueStartDrawCallIndex = m_nIssueEndDrawCallIndex; m_nIssueStartFrameIndex = m_nIssueEndFrameIndex; m_nIssueStartQueryCreationCounter = m_nIssueEndQueryCreationCounter; // End is very weird with respect to Events (fences). // DX9 docs say to use End to put the fence in the stream. So we map End to GLM's Start. // http://msdn.microsoft.com/en-us/library/ee422167(VS.85).aspx m_query->Start(); // drop "set fence" into stream break; } } return S_OK; } HRESULT IDirect3DQuery9::GetData(void* pData,DWORD dwSize,DWORD dwGetDataFlags) { GL_BATCH_PERF_CALL_TIMER; Assert( m_device->m_nValidMarker == D3D_DEVICE_VALID_MARKER ); HRESULT result = S_FALSE ; DWORD nCurThreadId = ThreadGetCurrentId(); // Make sure calling thread owns the GL context. Assert( m_ctx->m_nCurOwnerThreadId == nCurThreadId ); if ( pData ) { *(uint*)pData = 0; } if ( !m_query->IsStarted() || !m_query->IsStopped() ) { Assert(!"Can't GetData before issue/start/stop"); printf("\n** IDirect3DQuery9::GetData: can't GetData before issue/start/stop"); return S_FALSE; } // GetData is not always called with the flush bit. // if an answer is not yet available - return S_FALSE. // if an answer is available - return S_OK and write the answer into *pData. bool done = false; bool flush = (dwGetDataFlags & D3DGETDATA_FLUSH) != 0; // aka spin until done // hmmm both of these paths are the same, maybe we could fold them up if ( m_type == D3DQUERYTYPE_OCCLUSION ) { // Detect cases that are actually just not supported with the way we're using GL queries. (For example, beginning a query, then creating/deleting any query, the ending the same query is not supported.) // Also extra paranoid to detect/work around various NV/AMD driver issues. if ( ( ( m_nIssueStartThreadID != nCurThreadId ) || ( m_nIssueEndThreadID != nCurThreadId ) ) || ( m_nIssueStartDrawCallIndex == m_nIssueEndDrawCallIndex ) || ( m_nIssueStartFrameIndex != m_nIssueEndFrameIndex ) || ( m_nIssueStartQueryCreationCounter != m_nIssueEndQueryCreationCounter ) ) { // The thread Issue() was called on differs from GetData() - NV's driver doesn't like this, not sure about AMD. Just fake the results if a flush is requested. // There are various ways to properly handle this scenario, but in practice it only seems to occur in non-critical times (during shutdown or when mat_queue_mode is changed in L4D2). if ( flush ) { gGL->glFlush(); } #if 0 if ( ( m_nIssueStartThreadID != nCurThreadId ) || ( m_nIssueEndThreadID != nCurThreadId ) ) { GLMDebugPrintf( "IDirect3DQuery9::GetData: GetData() called from different thread verses the issueing thread()!\n" ); } #endif if ( m_nIssueStartQueryCreationCounter != m_nIssueEndQueryCreationCounter ) { GLMDebugPrintf( "IDirect3DQuery9::GetData: One or more queries have been created or released while this query was still issued! This scenario is not supported in GL.\n"); } // Return with a non-standard error code, so the caller has a chance to do something halfway intelligent. return D3DERR_NOTAVAILABLE; } } switch( m_type ) { case D3DQUERYTYPE_OCCLUSION: { if ( flush ) { uint oqValue = 0; CFastTimer tm; tm.Start(); // Is this flush actually necessary? According to the extension it's not. // It doesn't seem to matter if this is a glFlush() or glFinish() with NVidia's driver (tested in MT mode - not sure if it matters), it still can take several calls to IsDone() before we can stop waiting for the query results. // On AMD, this flush logic fails during shutdown (the query results never become available) - tried a bunch of experiments and checks with no luck. m_query->Complete(&oqValue); double flTotalTime = tm.GetDurationInProgress().GetSeconds() * 1000.0f; if ( flTotalTime > .5f ) { // Give up - something silly has obviously gone wrong in the driver, lying is better than stalling potentially forever. // This occurs on AMD (single threaded driver) during shutdown, not sure why yet. It has nothing to do with threading. It may have to do with releasing queries or other objects. // We must return a result otherwise the app itself could hang, waiting infinitely. //Assert( 0 ); Warning( "IDirect3DQuery9::GetData(): Occlusion query flush took %3.3fms!\n", flTotalTime ); } if (pData) { *(uint*)pData = oqValue; } result = S_OK; } else { done = m_query->IsDone(); if (done) { uint oqValue = 0; // or we could just pass pData directly to Complete... m_query->Complete(&oqValue); if (pData) { *(uint*)pData = oqValue; } result = S_OK; } else { result = S_FALSE; Assert( !flush ); } } } break; case D3DQUERYTYPE_EVENT: { done = m_query->IsDone(); if ( ( done ) || ( flush ) ) { m_query->Complete(NULL); // this will block on pre-SLGU result = S_OK; } else { result = S_FALSE; Assert( !flush ); } } break; } return result; } // ------------------------------------------------------------------------------------------------------------------------------ // #ifdef OSX #pragma mark ----- IDirect3DVertexBuffer9 #endif HRESULT IDirect3DDevice9::CreateVertexBuffer(UINT Length,DWORD Usage,DWORD FVF,D3DPOOL Pool,IDirect3DVertexBuffer9** ppVertexBuffer,VD3DHANDLE* pSharedHandle) { GL_BATCH_PERF_CALL_TIMER; GLMPRINTF(( ">-A- IDirect3DDevice9::CreateVertexBuffer" )); Assert( m_ctx->m_nCurOwnerThreadId == ThreadGetCurrentId() ); m_ObjectStats.m_nTotalVertexBuffers++; IDirect3DVertexBuffer9 *newbuff = new IDirect3DVertexBuffer9; newbuff->m_device = this; newbuff->m_ctx = m_ctx; // FIXME need to find home or use for the Usage, FVF, Pool values passed in uint options = 0; if (Usage&D3DUSAGE_DYNAMIC) { options |= GLMBufferOptionDynamic; } newbuff->m_vtxBuffer = m_ctx->NewBuffer( kGLMVertexBuffer, Length, options ) ; newbuff->m_vtxDesc.Type = D3DRTYPE_VERTEXBUFFER; newbuff->m_vtxDesc.Usage = Usage; newbuff->m_vtxDesc.Pool = Pool; newbuff->m_vtxDesc.Size = Length; *ppVertexBuffer = newbuff; GLMPRINTF(( "<-A- IDirect3DDevice9::CreateVertexBuffer" )); return S_OK; } IDirect3DVertexBuffer9::~IDirect3DVertexBuffer9() { GL_BATCH_PERF_CALL_TIMER; GL_PUBLIC_ENTRYPOINT_CHECKS( m_device ); GLMPRINTF(( ">-A- ~IDirect3DVertexBuffer9" )); if (m_device) { m_device->ReleasedVertexBuffer( this ); if (m_ctx && m_vtxBuffer) { GLMPRINTF(( ">-A- ~IDirect3DVertexBuffer9 deleting m_vtxBuffer" )); m_ctx->DelBuffer( m_vtxBuffer ); m_vtxBuffer = NULL; GLMPRINTF(( "<-A- ~IDirect3DVertexBuffer9 deleting m_vtxBuffer - done" )); } m_device = NULL; } GLMPRINTF(( "<-A- ~IDirect3DVertexBuffer9" )); } HRESULT IDirect3DVertexBuffer9::Lock(UINT OffsetToLock,UINT SizeToLock,void** ppbData,DWORD Flags) { GL_BATCH_PERF_CALL_TIMER; GL_PUBLIC_ENTRYPOINT_CHECKS( m_device ); tmZoneFiltered( TELEMETRY_LEVEL2, 25, TMZF_NONE, "VB Lock" ); // FIXME would be good to have "can't lock twice" logic Assert( !(Flags & D3DLOCK_READONLY) ); // not impl'd // Assert( !(Flags & D3DLOCK_NOSYSLOCK) ); // not impl'd - it triggers though GLMBuffLockParams lockreq; lockreq.m_nOffset = OffsetToLock; lockreq.m_nSize = SizeToLock; lockreq.m_bNoOverwrite = (Flags & D3DLOCK_NOOVERWRITE) != 0; lockreq.m_bDiscard = (Flags & D3DLOCK_DISCARD) != 0; m_vtxBuffer->Lock( &lockreq, (char**)ppbData ); GLMPRINTF(("-X- IDirect3DDevice9::Lock on D3D buf %p (GL name %d) offset %d, size %d => address %p", this, this->m_vtxBuffer->m_nHandle, OffsetToLock, SizeToLock, *ppbData)); return S_OK; } HRESULT IDirect3DVertexBuffer9::Unlock() { GL_BATCH_PERF_CALL_TIMER; GL_PUBLIC_ENTRYPOINT_CHECKS( m_device ); tmZoneFiltered( TELEMETRY_LEVEL2, 25, TMZF_NONE, "VB Unlock" ); m_vtxBuffer->Unlock(); return S_OK; } void IDirect3DVertexBuffer9::UnlockActualSize( uint nActualSize, const void *pActualData ) { GL_BATCH_PERF_CALL_TIMER; GL_PUBLIC_ENTRYPOINT_CHECKS( m_device ); tmZoneFiltered( TELEMETRY_LEVEL2, 25, TMZF_NONE, "VB UnlockActualSize" ); m_vtxBuffer->Unlock( nActualSize, pActualData ); } // ------------------------------------------------------------------------------------------------------------------------------ // #ifdef OSX #pragma mark ----- IDirect3DIndexBuffer9 #endif HRESULT IDirect3DDevice9::CreateIndexBuffer(UINT Length,DWORD Usage,D3DFORMAT Format,D3DPOOL Pool,IDirect3DIndexBuffer9** ppIndexBuffer,VD3DHANDLE* pSharedHandle) { GL_BATCH_PERF_CALL_TIMER; GL_PUBLIC_ENTRYPOINT_CHECKS( this ); GLMPRINTF(( ">-A- IDirect3DDevice9::CreateIndexBuffer" )); // it is important to save all the create info, since GetDesc could get called later to query it m_ObjectStats.m_nTotalIndexBuffers++; IDirect3DIndexBuffer9 *newbuff = new IDirect3DIndexBuffer9; newbuff->m_device = this; newbuff->m_restype = D3DRTYPE_INDEXBUFFER; // hmmmmmmm why are we not derived from d3dresource.. newbuff->m_ctx = m_ctx; // FIXME need to find home or use for the Usage, Format, Pool values passed in uint options = 0; if (Usage&D3DUSAGE_DYNAMIC) { options |= GLMBufferOptionDynamic; } newbuff->m_idxBuffer = m_ctx->NewBuffer( kGLMIndexBuffer, Length, options ) ; newbuff->m_idxDesc.Format = Format; newbuff->m_idxDesc.Type = D3DRTYPE_INDEXBUFFER; newbuff->m_idxDesc.Usage = Usage; newbuff->m_idxDesc.Pool = Pool; newbuff->m_idxDesc.Size = Length; *ppIndexBuffer = newbuff; GLMPRINTF(( "<-A- IDirect3DDevice9::CreateIndexBuffer" )); return S_OK; } IDirect3DIndexBuffer9::~IDirect3DIndexBuffer9() { GL_BATCH_PERF_CALL_TIMER; GL_PUBLIC_ENTRYPOINT_CHECKS( m_device ); GLMPRINTF(( ">-A- ~IDirect3DIndexBuffer9" )); if (m_device) { m_device->ReleasedIndexBuffer( this ); if (m_ctx && m_idxBuffer) { GLMPRINTF(( ">-A- ~IDirect3DIndexBuffer9 deleting m_idxBuffer" )); m_ctx->DelBuffer( m_idxBuffer ); GLMPRINTF(( "<-A- ~IDirect3DIndexBuffer9 deleting m_idxBuffer - done" )); } m_device = NULL; } else { } GLMPRINTF(( "<-A- ~IDirect3DIndexBuffer9" )); } HRESULT IDirect3DIndexBuffer9::Lock(UINT OffsetToLock,UINT SizeToLock,void** ppbData,DWORD Flags) { GL_BATCH_PERF_CALL_TIMER; GL_PUBLIC_ENTRYPOINT_CHECKS( m_device ); // FIXME would be good to have "can't lock twice" logic tmZoneFiltered( TELEMETRY_LEVEL2, 25, TMZF_NONE, "IB Lock" ); GLMBuffLockParams lockreq; lockreq.m_nOffset = OffsetToLock; lockreq.m_nSize = SizeToLock; lockreq.m_bNoOverwrite = ( Flags & D3DLOCK_NOOVERWRITE ) != 0; lockreq.m_bDiscard = ( Flags & D3DLOCK_DISCARD ) != 0; m_idxBuffer->Lock( &lockreq, (char**)ppbData ); return S_OK; } HRESULT IDirect3DIndexBuffer9::Unlock() { GL_BATCH_PERF_CALL_TIMER; GL_PUBLIC_ENTRYPOINT_CHECKS( m_device ); tmZoneFiltered( TELEMETRY_LEVEL2, 25, TMZF_NONE, "IB Unlock" ); m_idxBuffer->Unlock(); return S_OK; } void IDirect3DIndexBuffer9::UnlockActualSize( uint nActualSize, const void *pActualData ) { GL_BATCH_PERF_CALL_TIMER; GL_PUBLIC_ENTRYPOINT_CHECKS( m_device ); tmZoneFiltered( TELEMETRY_LEVEL2, 25, TMZF_NONE, "IB UnlockActualSize" ); m_idxBuffer->Unlock( nActualSize, pActualData ); } HRESULT IDirect3DIndexBuffer9::GetDesc(D3DINDEXBUFFER_DESC *pDesc) { GL_BATCH_PERF_CALL_TIMER; GL_PUBLIC_ENTRYPOINT_CHECKS( m_device ); *pDesc = m_idxDesc; return S_OK; } // ------------------------------------------------------------------------------------------------------------------------------ // #ifdef OSX #pragma mark ----- IDirect3DDevice9 ------------------------------------------------- #endif void ConvertPresentationParamsToGLMDisplayParams( D3DPRESENT_PARAMETERS *d3dp, GLMDisplayParams *gldp ) { memset( gldp, 0, sizeof(*gldp) ); gldp->m_fsEnable = !d3dp->Windowed; // see http://msdn.microsoft.com/en-us/library/ee416515(VS.85).aspx // note that the values below are the only ones mentioned by Source engine; there are many others switch(d3dp->PresentationInterval) { case D3DPRESENT_INTERVAL_ONE: gldp->m_vsyncEnable = true; // "The driver will wait for the vertical retrace period (the runtime will beam-follow to prevent tearing)." break; case D3DPRESENT_INTERVAL_IMMEDIATE: gldp->m_vsyncEnable = false; // "The runtime updates the window client area immediately and might do so more than once during the adapter refresh period." break; default: gldp->m_vsyncEnable = true; // if I don't know it, you're getting vsync enabled. break; } gldp->m_backBufferWidth = d3dp->BackBufferWidth; gldp->m_backBufferHeight = d3dp->BackBufferHeight; gldp->m_backBufferFormat = d3dp->BackBufferFormat; gldp->m_multiSampleCount = d3dp->MultiSampleType; // it's a count really gldp->m_enableAutoDepthStencil = d3dp->EnableAutoDepthStencil != 0; gldp->m_autoDepthStencilFormat = d3dp->AutoDepthStencilFormat; gldp->m_fsRefreshHz = d3dp->FullScreen_RefreshRateInHz; // some fields in d3d PB we're not acting on yet... // UINT BackBufferCount; // DWORD MultiSampleQuality; // D3DSWAPEFFECT SwapEffect; // VD3DHWND hDeviceWindow; // DWORD Flags; } void UnpackD3DRSITable( void ); HRESULT IDirect3DDevice9::Create( IDirect3DDevice9Params *params ) { g_pD3D_Device = this; GLMDebugPrintf( "IDirect3DDevice9::Create: BackBufWidth: %u, BackBufHeight: %u, D3DFMT: %u, BackBufCount: %u, MultisampleType: %u, MultisampleQuality: %u\n", params->m_presentationParameters.BackBufferWidth, params->m_presentationParameters.BackBufferHeight, params->m_presentationParameters.BackBufferFormat, params->m_presentationParameters.BackBufferCount, params->m_presentationParameters.MultiSampleType, params->m_presentationParameters.MultiSampleQuality ); UnpackD3DRSITable(); m_ObjectStats.clear(); m_PrevObjectStats.clear(); #if GL_BATCH_PERF_ANALYSIS && GL_BATCH_PERF_ANALYSIS_WRITE_PNGS m_pBatch_vis_bitmap = NULL; #endif GL_BATCH_PERF_CALL_TIMER; GLMPRINTF((">-X-IDirect3DDevice9::Create")); HRESULT result = S_OK; // create an IDirect3DDevice9 // make a GLMContext and set up some drawables m_params = *params; m_ctx = NULL; V_memset( m_pRenderTargets, 0, sizeof( m_pRenderTargets ) ); m_pDepthStencil = NULL; m_pDefaultColorSurface = NULL; m_pDefaultDepthStencilSurface = NULL; memset( m_streams, 0, sizeof(m_streams) ); memset( m_vtx_buffers, 0, sizeof( m_vtx_buffers ) ); memset( m_textures, 0, sizeof(m_textures) ); //memset( m_samplers, 0, sizeof(m_samplers) ); m_indices.m_idxBuffer = NULL; m_vertexShader = NULL; m_pixelShader = NULL; m_pVertDecl = NULL; //============================================================================ // param block for GLM context create GLMDisplayParams glmParams; ConvertPresentationParamsToGLMDisplayParams( ¶ms->m_presentationParameters, &glmParams ); glmParams.m_mtgl = true; // forget this idea -> (params->m_behaviorFlags & D3DCREATE_MULTITHREADED) != 0; // the call above fills in a bunch of things, but doesn't know about anything outside of the presentation params. // those tend to be the things that do not change after create, so we do those here in Create. glmParams.m_focusWindow = params->m_focusWindow; #if 0 //FIXME-HACK // map the D3D "adapter" to a renderer/display pair // (that GPU will have to stay set as-is for any subsequent mode changes) int glmRendererIndex = -1; int glmDisplayIndex = -1; GLMRendererInfoFields glmRendererInfo; GLMDisplayInfoFields glmDisplayInfo; // the D3D "Adapter" number feeds the fake adapter index bool adaptResult = GLMgr::aGLMgr()->GetDisplayDB()->GetFakeAdapterInfo( params->m_adapter, &glmRendererIndex, &glmDisplayIndex, &glmRendererInfo, &glmDisplayInfo ); Assert(!adaptResult); glmParams.m_rendererIndex = glmRendererIndex; glmParams.m_displayIndex = glmDisplayIndex; // glmParams.m_modeIndex hmmmmm, client doesn't give us a mode number, just a resolution.. #endif m_ctx = GLMgr::aGLMgr()->NewContext( this, &glmParams ); if (!m_ctx) { GLMPRINTF(("<-X- IDirect3DDevice9::Create (error out)")); return (HRESULT) -1; } // make an FBO to draw into and activate it. m_ctx->m_drawingFBO = m_ctx->NewFBO(); // bind it to context. will receive attachments shortly. m_ctx->BindFBOToCtx( m_ctx->m_drawingFBO, GL_FRAMEBUFFER_EXT ); m_bFBODirty = false; m_pFBOs = new CGLMFBOMap(); m_pFBOs->SetLessFunc( RenderTargetState_t::LessFunc ); // we create two IDirect3DSurface9's. These will be known as the internal render target 0 and the depthstencil. GLMPRINTF(("-X- IDirect3DDevice9::Create making color render target...")); // color surface result = this->CreateRenderTarget( m_params.m_presentationParameters.BackBufferWidth, // width m_params.m_presentationParameters.BackBufferHeight, // height m_params.m_presentationParameters.BackBufferFormat, // format m_params.m_presentationParameters.MultiSampleType, // MSAA depth m_params.m_presentationParameters.MultiSampleQuality, // MSAA quality true, // lockable &m_pDefaultColorSurface, // ppSurface NULL, // shared handle "InternalRT0" ); if (result != S_OK) { GLMPRINTF(("<-X- IDirect3DDevice9::Create (error out)")); return result; } // do not do an AddRef.. GLMPRINTF(("-X- IDirect3DDevice9::Create making color render target complete -> %08x", m_pDefaultColorSurface )); GLMPRINTF(("-X- IDirect3DDevice9::Create setting color render target...")); result = this->SetRenderTarget(0, m_pDefaultColorSurface); if (result != S_OK) { GLMPRINTF(("< IDirect3DDevice9::Create (error out)")); return result; } GLMPRINTF(("-X- IDirect3DDevice9::Create setting color render target complete.")); Assert (m_params.m_presentationParameters.EnableAutoDepthStencil); GLMPRINTF(("-X- IDirect3DDevice9::Create making depth-stencil...")); result = CreateDepthStencilSurface( m_params.m_presentationParameters.BackBufferWidth, // width m_params.m_presentationParameters.BackBufferHeight, // height m_params.m_presentationParameters.AutoDepthStencilFormat, // format m_params.m_presentationParameters.MultiSampleType, // MSAA depth m_params.m_presentationParameters.MultiSampleQuality, // MSAA quality TRUE, // enable z-buffer discard ???? &m_pDefaultDepthStencilSurface, // ppSurface NULL // shared handle ); if (result != S_OK) { GLMPRINTF(("<-X- IDirect3DDevice9::Create (error out)")); return result; } // do not do an AddRef here.. GLMPRINTF(("-X- IDirect3DDevice9::Create making depth-stencil complete -> %08x", m_pDefaultDepthStencilSurface)); GLMPRINTF(("-X- Direct3DDevice9::Create setting depth-stencil render target...")); result = this->SetDepthStencilSurface(m_pDefaultDepthStencilSurface); if (result != S_OK) { DXABSTRACT_BREAK_ON_ERROR(); GLMPRINTF(("<-X- IDirect3DDevice9::Create (error out)")); return result; } GLMPRINTF(("-X- IDirect3DDevice9::Create setting depth-stencil render target complete.")); UpdateBoundFBO(); bool ready = m_ctx->m_drawingFBO->IsReady(); if (!ready) { GLMPRINTF(("<-X- IDirect3DDevice9::Create (error out)")); return (HRESULT)-1; } // this next part really needs to be inside GLMContext.. or replaced with D3D style viewport setup calls. m_ctx->GenDebugFontTex(); // blast the gl state mirror... memset( &this->gl, 0, sizeof( this->gl ) ); InitStates(); GLScissorEnable_t defScissorEnable = { true }; GLScissorBox_t defScissorBox = { 0,0, (GLsizei)m_params.m_presentationParameters.BackBufferWidth, (GLsizei)m_params.m_presentationParameters.BackBufferHeight }; GLViewportBox_t defViewportBox = { 0,0, (GLsizei)m_params.m_presentationParameters.BackBufferWidth, (GLsizei)m_params.m_presentationParameters.BackBufferHeight, m_params.m_presentationParameters.BackBufferWidth | ( m_params.m_presentationParameters.BackBufferHeight << 16 ) }; GLViewportDepthRange_t defViewportDepthRange = { 0.1, 1000.0 }; GLCullFaceEnable_t defCullFaceEnable = { true }; GLCullFrontFace_t defCullFrontFace = { GL_CCW }; gl.m_ScissorEnable = defScissorEnable; gl.m_ScissorBox = defScissorBox; gl.m_ViewportBox = defViewportBox; gl.m_ViewportDepthRange = defViewportDepthRange; gl.m_CullFaceEnable = defCullFaceEnable; gl.m_CullFrontFace = defCullFrontFace; FullFlushStates(); GLMPRINTF(("<-X- IDirect3DDevice9::Create complete")); // so GetClientRect can return sane answers //uint width, height; RenderedSize( m_params.m_presentationParameters.BackBufferWidth, m_params.m_presentationParameters.BackBufferHeight, true ); // true = set #if GL_TELEMETRY_GPU_ZONES g_TelemetryGPUStats.Clear(); #endif GL_BATCH_PERF( g_nTotalD3DCalls = 0, g_nTotalD3DCycles = 0, m_nBatchVisY = 0, m_nBatchVisFrameIndex = 0, m_nBatchVisFileIdx = 0, m_nNumProgramChanges = 0, m_flTotalD3DTime = 0, m_nTotalD3DCalls = 0, m_flTotalD3DTime = 0, m_nTotalGLCalls = 0, m_flTotalGLTime = 0, m_nOverallDraws = 0, m_nOverallPrims = 0, m_nOverallD3DCalls = 0, m_flOverallD3DTime = 0, m_nOverallGLCalls = 0, m_flOverallGLTime = 0, m_nOverallProgramChanges = 0, m_flOverallPresentTime = 0, m_flOverallPresentTimeSquared = 0, m_nOverallPresents = 0, m_flOverallSwapWindowTime = 0, m_flOverallSwapWindowTimeSquared = 0, m_nTotalPrims = 0; ); g_nTotalDrawsOrClears = 0; gGL->m_nTotalGLCycles = 0; gGL->m_nTotalGLCalls = 0; m_pDummy_vtx_buffer = new CGLMBuffer( m_ctx, kGLMVertexBuffer, 4096, 0 ); m_vtx_buffers[0] = m_pDummy_vtx_buffer; m_vtx_buffers[1] = m_pDummy_vtx_buffer; m_vtx_buffers[2] = m_pDummy_vtx_buffer; m_vtx_buffers[3] = m_pDummy_vtx_buffer; return result; } IDirect3DDevice9::IDirect3DDevice9() : m_nValidMarker( D3D_DEVICE_VALID_MARKER ) { } IDirect3DDevice9::~IDirect3DDevice9() { Assert( m_nValidMarker == D3D_DEVICE_VALID_MARKER ); #if GL_BATCH_PERF_ANALYSIS && GL_BATCH_PERF_ANALYSIS_WRITE_PNGS delete m_pBatch_vis_bitmap; #endif delete m_pDummy_vtx_buffer; for ( int i = 0; i < 4; i++ ) SetRenderTarget( i, NULL ); SetDepthStencilSurface( NULL ); if ( m_pDefaultColorSurface ) { m_pDefaultColorSurface->Release( 0, "IDirect3DDevice9::~IDirect3DDevice9 release color surface" ); m_pDefaultColorSurface = NULL; } if ( m_pDefaultDepthStencilSurface ) { m_pDefaultDepthStencilSurface->Release( 0, "IDirect3DDevice9::~IDirect3DDevice9 release depth surface" ); m_pDefaultDepthStencilSurface = NULL; } if ( m_pFBOs ) { ResetFBOMap(); } GLMPRINTF(( "-D- IDirect3DDevice9::~IDirect3DDevice9 signpost" )); // want to know when this is called, if ever g_pD3D_Device = NULL; if ( m_ObjectStats.m_nTotalFBOs ) GLMDebugPrintf( "Leaking %i FBOs\n", m_ObjectStats.m_nTotalFBOs ); if ( m_ObjectStats.m_nTotalVertexShaders ) ConMsg( "Leaking %i vertex shaders\n", m_ObjectStats.m_nTotalVertexShaders ); if ( m_ObjectStats.m_nTotalPixelShaders ) ConMsg( "Leaking %i pixel shaders\n", m_ObjectStats.m_nTotalPixelShaders ); if ( m_ObjectStats.m_nTotalVertexDecls ) ConMsg( "Leaking %i vertex decls\n", m_ObjectStats.m_nTotalVertexDecls ); if ( m_ObjectStats.m_nTotalIndexBuffers ) ConMsg( "Leaking %i index buffers\n", m_ObjectStats.m_nTotalIndexBuffers ); if ( m_ObjectStats.m_nTotalVertexBuffers ) ConMsg( "Leaking %i vertex buffers\n", m_ObjectStats.m_nTotalVertexBuffers ); if ( m_ObjectStats.m_nTotalTextures ) ConMsg( "Leaking %i textures\n", m_ObjectStats.m_nTotalTextures ); if ( m_ObjectStats.m_nTotalSurfaces ) ConMsg( "Leaking %i surfaces\n", m_ObjectStats.m_nTotalSurfaces ); if ( m_ObjectStats.m_nTotalQueries ) ConMsg( "Leaking %i queries\n", m_ObjectStats.m_nTotalQueries ); if ( m_ObjectStats.m_nTotalRenderTargets ) ConMsg( "Leaking %i render targets\n", m_ObjectStats.m_nTotalRenderTargets ); GLMgr::aGLMgr()->DelContext( m_ctx ); m_ctx = NULL; m_nValidMarker = 0xDEADBEEF; } #ifdef OSX #pragma mark ----- Basics - (IDirect3DDevice9) #endif HRESULT IDirect3DDevice9::Reset(D3DPRESENT_PARAMETERS* pPresentationParameters) { GL_BATCH_PERF_CALL_TIMER; GL_PUBLIC_ENTRYPOINT_CHECKS( this ); Assert( m_nValidMarker == D3D_DEVICE_VALID_MARKER ); HRESULT result = S_OK; // define the task of reset as: // provide new drawable RT's for the backbuffer (color and depthstencil). // fix up viewport / scissor.. // then pass the new presentation parameters through to GLM. // (it will in turn notify appframework on the next present... which may be very soon, as mode changes are usually spotted inside Present() ). // so some of this looks a lot like Create - we're just a subset of what it does. // with a little work you could refactor this to be common code. GLMDebugPrintf( "IDirect3DDevice9::Reset: BackBufWidth: %u, BackBufHeight: %u, D3DFMT: %u, BackBufCount: %u, MultisampleType: %u, MultisampleQuality: %u\n", pPresentationParameters->BackBufferWidth, pPresentationParameters->BackBufferHeight, pPresentationParameters->BackBufferFormat, pPresentationParameters->BackBufferCount, pPresentationParameters->MultiSampleType, pPresentationParameters->MultiSampleQuality ); //------------------------------------------------------------------------------- absorb new presentation params.. m_params.m_presentationParameters = *pPresentationParameters; //------------------------------------------------------------------------------- color buffer.. // release old color surface if it's there.. if ( m_pDefaultColorSurface ) { ULONG refc = m_pDefaultColorSurface->Release( 0, "IDirect3DDevice9::Reset public release color surface" ); (void)refc; Assert( !refc ); m_pDefaultColorSurface = NULL; } GLMPRINTF(("-X- IDirect3DDevice9::Reset making new color render target...")); // color surface result = this->CreateRenderTarget( m_params.m_presentationParameters.BackBufferWidth, // width m_params.m_presentationParameters.BackBufferHeight, // height m_params.m_presentationParameters.BackBufferFormat, // format m_params.m_presentationParameters.MultiSampleType, // MSAA depth m_params.m_presentationParameters.MultiSampleQuality, // MSAA quality true, // lockable &m_pDefaultColorSurface, // ppSurface NULL // shared handle ); if ( result != S_OK ) { GLMPRINTF(("<-X- IDirect3DDevice9::Reset (error out)")); return result; } // do not do an AddRef here.. GLMPRINTF(("-X- IDirect3DDevice9::Reset making color render target complete -> %08x", m_pDefaultColorSurface )); GLMPRINTF(("-X- IDirect3DDevice9::Reset setting color render target...")); result = this->SetDepthStencilSurface( NULL ); result = this->SetRenderTarget( 0, m_pDefaultColorSurface ); if (result != S_OK) { GLMPRINTF(("< IDirect3DDevice9::Reset (error out)")); return result; } GLMPRINTF(("-X- IDirect3DDevice9::Reset setting color render target complete.")); //-------------------------------------------------------------------------------depth stencil buffer // release old depthstencil surface if it's there.. if ( m_pDefaultDepthStencilSurface ) { ULONG refc = m_pDefaultDepthStencilSurface->Release( 0, "IDirect3DDevice9::Reset public release depthstencil surface" ); (void)refc; Assert(!refc); m_pDefaultDepthStencilSurface = NULL; } Assert (m_params.m_presentationParameters.EnableAutoDepthStencil); GLMPRINTF(("-X- IDirect3DDevice9::Reset making depth-stencil...")); result = CreateDepthStencilSurface( m_params.m_presentationParameters.BackBufferWidth, // width m_params.m_presentationParameters.BackBufferHeight, // height m_params.m_presentationParameters.AutoDepthStencilFormat, // format m_params.m_presentationParameters.MultiSampleType, // MSAA depth m_params.m_presentationParameters.MultiSampleQuality, // MSAA quality TRUE, // enable z-buffer discard ???? &m_pDefaultDepthStencilSurface, // ppSurface NULL // shared handle ); if (result != S_OK) { GLMPRINTF(("<-X- IDirect3DDevice9::Reset (error out)")); return result; } // do not do an AddRef here.. GLMPRINTF(("-X- IDirect3DDevice9::Reset making depth-stencil complete -> %08x", m_pDefaultDepthStencilSurface)); GLMPRINTF(("-X- IDirect3DDevice9::Reset setting depth-stencil render target...")); result = this->SetDepthStencilSurface(m_pDefaultDepthStencilSurface); if (result != S_OK) { GLMPRINTF(("<-X- IDirect3DDevice9::Reset (error out)")); return result; } GLMPRINTF(("-X- IDirect3DDevice9::Reset setting depth-stencil render target complete.")); UpdateBoundFBO(); bool ready = m_ctx->m_drawingFBO->IsReady(); if (!ready) { GLMPRINTF(("<-X- IDirect3DDevice9::Reset (error out)")); return D3DERR_DEVICELOST; } //-------------------------------------------------------------------------------zap viewport and scissor to new backbuffer size InitStates(); GLScissorEnable_t defScissorEnable = { true }; GLScissorBox_t defScissorBox = { 0,0, (GLsizei)m_params.m_presentationParameters.BackBufferWidth, (GLsizei)m_params.m_presentationParameters.BackBufferHeight }; GLViewportBox_t defViewportBox = { 0,0, (GLsizei)m_params.m_presentationParameters.BackBufferWidth, (GLsizei)m_params.m_presentationParameters.BackBufferHeight, m_params.m_presentationParameters.BackBufferWidth | ( m_params.m_presentationParameters.BackBufferHeight << 16 ) }; GLViewportDepthRange_t defViewportDepthRange = { 0.1, 1000.0 }; GLCullFaceEnable_t defCullFaceEnable = { true }; GLCullFrontFace_t defCullFrontFace = { GL_CCW }; gl.m_ScissorEnable = defScissorEnable; gl.m_ScissorBox = defScissorBox; gl.m_ViewportBox = defViewportBox; gl.m_ViewportDepthRange = defViewportDepthRange; gl.m_CullFaceEnable = defCullFaceEnable; gl.m_CullFrontFace = defCullFrontFace; FullFlushStates(); //-------------------------------------------------------------------------------finally, propagate new display params to GLM context GLMDisplayParams glmParams; ConvertPresentationParamsToGLMDisplayParams( pPresentationParameters, &glmParams ); // steal back previously sent focus window... glmParams.m_focusWindow = m_ctx->m_displayParams.m_focusWindow; Assert( glmParams.m_focusWindow != NULL ); // so GetClientRect can return sane answers //uint width, height; RenderedSize( pPresentationParameters->BackBufferWidth, pPresentationParameters->BackBufferHeight, true ); // true = set m_ctx->Reset(); m_ctx->SetDisplayParams( &glmParams ); return S_OK; } HRESULT IDirect3DDevice9::SetViewport(CONST D3DVIEWPORT9* pViewport) { GL_BATCH_PERF_CALL_TIMER; GL_PUBLIC_ENTRYPOINT_CHECKS( this ); GLMPRINTF(("-X- IDirect3DDevice9::SetViewport : minZ %f, maxZ %f",pViewport->MinZ, pViewport->MaxZ )); gl.m_ViewportBox.x = pViewport->X; gl.m_ViewportBox.width = pViewport->Width; gl.m_ViewportBox.y = pViewport->Y; gl.m_ViewportBox.height = pViewport->Height; gl.m_ViewportBox.widthheight = pViewport->Width | ( pViewport->Height << 16 ); m_ctx->WriteViewportBox( &gl.m_ViewportBox ); gl.m_ViewportDepthRange.flNear = pViewport->MinZ; gl.m_ViewportDepthRange.flFar = pViewport->MaxZ; m_ctx->WriteViewportDepthRange( &gl.m_ViewportDepthRange ); return S_OK; } HRESULT IDirect3DDevice9::GetViewport( D3DVIEWPORT9* pViewport ) { // 7LS - unfinished, used in scaleformuirenderimpl.cpp (only width and height required) GL_BATCH_PERF_CALL_TIMER; Assert( GetCurrentOwnerThreadId() == ThreadGetCurrentId() ); GLMPRINTF(("-X- IDirect3DDevice9::GetViewport " )); pViewport->X = gl.m_ViewportBox.x; pViewport->Width = gl.m_ViewportBox.width; pViewport->Y = gl.m_ViewportBox.y; pViewport->Height = gl.m_ViewportBox.height; pViewport->MinZ = gl.m_ViewportDepthRange.flNear; pViewport->MaxZ = gl.m_ViewportDepthRange.flFar; return S_OK; } HRESULT IDirect3DDevice9::BeginScene() { GL_BATCH_PERF_CALL_TIMER; GL_PUBLIC_ENTRYPOINT_CHECKS( this ); m_ctx->BeginFrame(); return S_OK; } HRESULT IDirect3DDevice9::EndScene() { GL_BATCH_PERF_CALL_TIMER; GL_PUBLIC_ENTRYPOINT_CHECKS( this ); m_ctx->EndFrame(); return S_OK; } // stolen from glmgrbasics.cpp enum ECarbonModKeyIndex { EcmdKeyBit = 8, /* command key down?*/ EshiftKeyBit = 9, /* shift key down?*/ EalphaLockBit = 10, /* alpha lock down?*/ EoptionKeyBit = 11, /* option key down?*/ EcontrolKeyBit = 12 /* control key down?*/ }; enum ECarbonModKeyMask { EcmdKey = 1 << EcmdKeyBit, EshiftKey = 1 << EshiftKeyBit, EalphaLock = 1 << EalphaLockBit, EoptionKey = 1 << EoptionKeyBit, EcontrolKey = 1 << EcontrolKeyBit }; void IDirect3DDevice9::PrintObjectStats( const ObjectStats_t &stats ) { ConMsg( "Total FBOs: %i\n", stats.m_nTotalFBOs ); ConMsg( "Total vertex shaders: %i\n", stats.m_nTotalVertexShaders ); ConMsg( "Total pixel shaders: %i\n", stats.m_nTotalPixelShaders ); ConMsg( "Total vertex decls: %i\n", stats.m_nTotalVertexDecls ); ConMsg( "Total index buffers: %i\n", stats.m_nTotalIndexBuffers ); ConMsg( "Total vertex buffers: %i\n", stats.m_nTotalVertexBuffers ); ConMsg( "Total textures: %i\n", stats.m_nTotalTextures ); ConMsg( "Total surfaces: %i\n", stats.m_nTotalSurfaces ); ConMsg( "Total queries: %i\n", stats.m_nTotalQueries ); ConMsg( "Total render targets: %i\n", stats.m_nTotalRenderTargets ); } void IDirect3DDevice9::DumpStatsToConsole( const CCommand *pArgs ) { #if GL_BATCH_PERF_ANALYSIS ConMsg( "Overall: Batches: %u, Prims: %u, Program Changes: %u\n", m_nOverallDraws, m_nOverallPrims, m_nOverallProgramChanges ); ConMsg( "Overall: D3D Calls: %u D3D Time: %4.3fms, Avg D3D Time Per Call: %4.9fms\n", m_nOverallD3DCalls, m_flOverallD3DTime, m_nOverallD3DCalls ? ( m_flOverallD3DTime / m_nOverallD3DCalls ) : 0.0f ); ConMsg( "Overall: GL Calls: %u GL Time: %4.3fms, Avg GL Time Per Call: %4.9fms\n", m_nOverallGLCalls, m_flOverallGLTime, m_nOverallGLCalls ? ( m_flOverallGLTime / m_nOverallGLCalls ) : 0.0f ); ConMsg( "D3DPresent: %u, Overall Time: %4.3fms, Avg: %4.6fms, Std Dev: %4.6fms\n", m_nOverallPresents, m_flOverallPresentTime, m_nOverallPresents ? ( m_flOverallPresentTime / m_nOverallPresents ) : 0.0f, m_nOverallPresents ? ( sqrt( ( m_flOverallPresentTimeSquared / m_nOverallPresents ) - ( m_flOverallPresentTime / m_nOverallPresents ) * ( m_flOverallPresentTime / m_nOverallPresents ) ) ) : 0.0f ); ConMsg( "GL SwapWindow(): Overall Time: %4.3fms, Avg: %4.6fms, Std Dev: %4.6fms\n", m_flOverallSwapWindowTime, m_nOverallPresents ? ( m_flOverallSwapWindowTime / m_nOverallPresents ) : 0.0f, m_nOverallPresents ? ( sqrt( ( m_flOverallSwapWindowTimeSquared / m_nOverallPresents ) - ( m_flOverallSwapWindowTime / m_nOverallPresents ) * ( m_flOverallSwapWindowTime / m_nOverallPresents ) ) ) : 0.0f ); if ( ( pArgs ) && ( pArgs->ArgC() == 2 ) && (pArgs->Arg(1)[0] != '0') ) { m_nOverallDraws = 0; m_nOverallPrims = 0; m_nOverallProgramChanges = 0; m_nOverallD3DCalls = 0; m_flOverallD3DTime = 0; m_nOverallGLCalls = 0; m_flOverallGLTime = 0; m_flOverallPresentTime = 0; m_flOverallPresentTimeSquared = 0; m_flOverallSwapWindowTime = 0; m_flOverallSwapWindowTimeSquared = 0; m_nOverallPresents = 0; } #endif ConMsg( "Totals:\n" ); m_ObjectStats.m_nTotalFBOs = m_pFBOs->Count(); PrintObjectStats( m_ObjectStats ); ObjectStats_t delta( m_ObjectStats ); delta -= m_PrevObjectStats; ConMsg( "Delta:\n" ); PrintObjectStats( delta ); m_PrevObjectStats = m_ObjectStats; } static void gl_dump_stats_func( const CCommand &args ) { if ( g_pD3D_Device ) { g_pD3D_Device->DumpStatsToConsole( &args ); } } static ConCommand gl_dump_stats( "gl_dump_stats", gl_dump_stats_func ); #if GLMDEBUG void IDirect3DDevice9::DumpTextures( const CCommand *pArgs ) { Assert( m_nValidMarker == D3D_DEVICE_VALID_MARKER ); (void)pArgs; CGLMTex *pCurTex = g_pFirstCGMLTex; if ( pCurTex ) { Assert( pCurTex->m_pPrevTex == NULL ); } ConMsg( "--- Internal CGLMTex's:\n" ); uint nNumFound = 0; while ( pCurTex ) { nNumFound++; ConMsg( "Tex \"%s\", Layout: \"%s\", Size: %u, RT: %u, Depth: %u, Stencil: %u, MSAA: %u\n", pCurTex->m_debugLabel ? pCurTex->m_debugLabel : "?", pCurTex->m_layout->m_layoutSummary, pCurTex->m_layout->m_storageTotalSize, ( pCurTex->m_layout->m_key.m_texFlags & kGLMTexRenderable ) ? 1 : 0, ( pCurTex->m_layout->m_key.m_texFlags & kGLMTexIsDepth ) ? 1 : 0, ( pCurTex->m_layout->m_key.m_texFlags & kGLMTexIsStencil ) ? 1 : 0, ( pCurTex->m_layout->m_key.m_texFlags & kGLMTexMultisampled ) ? 1 : 0 ); CGLMTex *pNextTex = pCurTex->m_pNextTex; if ( pNextTex ) { Assert( pNextTex->m_pPrevTex == pCurTex ); } pCurTex = pNextTex; } ConMsg( "--- Found %u total CGLMTex's\n", nNumFound ); } static void gl_dump_textures_func( const CCommand &args ) { if ( g_pD3D_Device ) { g_pD3D_Device->DumpTextures( &args ); } } static ConCommand gl_dump_textures( "gl_dump_textures", gl_dump_textures_func ); #endif ConVar gl_blitmode( "gl_blitmode", "1" ); ConVar dxa_nullrefresh_capslock( "dxa_nullrefresh_capslock", "0" ); HRESULT IDirect3DDevice9::Present(CONST RECT* pSourceRect,CONST RECT* pDestRect,VD3DHWND hDestWindowOverride,CONST RGNDATA* pDirtyRegion) { GL_BATCH_PERF( g_nTotalD3DCalls++; ) GL_PUBLIC_ENTRYPOINT_CHECKS( this ); TOGL_NULL_DEVICE_CHECK; if ( m_bFBODirty ) { UpdateBoundFBO(); } // before attempting to present a tex, make sure it's been resolved if it was MSAA. // if we push that responsibility down to m_ctx->Present, it could probably do it without an extra copy. // i.e. anticipate the blit from the resolvedtex to GL_BACK, and just do that instead. // no explicit ResolveTex call first - that got pushed down into GLMContext::Present #if GL_BATCH_PERF_ANALYSIS uint64 nStartGLCycles = gGL->m_nTotalGLCycles; nStartGLCycles; CFastTimer tm; tm.Start(); #endif m_ctx->Present( m_pDefaultColorSurface->m_tex ); #if GL_BATCH_PERF_ANALYSIS double flPresentTime = tm.GetDurationInProgress().GetMillisecondsF(); double flGLSwapWindowTime = g_pLauncherMgr->GetPrevGLSwapWindowTime(); m_flOverallPresentTime += flPresentTime; m_flOverallPresentTimeSquared += flPresentTime * flPresentTime; m_flOverallSwapWindowTime += flGLSwapWindowTime; m_flOverallSwapWindowTimeSquared += flGLSwapWindowTime * flGLSwapWindowTime; m_nOverallPresents++; uint64 nEndGLCycles = gGL->m_nTotalGLCycles; nEndGLCycles; m_flTotalD3DTime += flPresentTime + g_nTotalD3DCycles * s_rdtsc_to_ms; m_nTotalD3DCalls += g_nTotalD3DCalls; m_flTotalGLTime += gGL->m_nTotalGLCycles * s_rdtsc_to_ms; m_nTotalGLCalls += gGL->m_nTotalGLCalls; m_nOverallProgramChanges += m_nNumProgramChanges; m_nOverallDraws += g_nTotalDrawsOrClears; m_nOverallPrims += m_nTotalPrims; m_nOverallD3DCalls += m_nTotalD3DCalls; m_flOverallD3DTime += m_flTotalD3DTime; m_nOverallGLCalls += m_nTotalGLCalls; m_flOverallGLTime += m_flTotalGLTime; static int nPrevBatchVis = -1; #if GL_BATCH_PERF_ANALYSIS_WRITE_PNGS if ((nPrevBatchVis == 1) && m_pBatch_vis_bitmap && m_pBatch_vis_bitmap->is_valid()) { double flTotalGLPresentTime = ( nEndGLCycles - nStartGLCycles ) * s_rdtsc_to_ms; m_pBatch_vis_bitmap->fill_box(0, m_nBatchVisY, (uint)(.5f + flPresentTime / gl_present_vis_abs_scale.GetFloat() * m_pBatch_vis_bitmap->width()), 10, 255, 16, 128); m_pBatch_vis_bitmap->additive_fill_box(0, m_nBatchVisY, (uint)(.5f + flTotalGLPresentTime / gl_present_vis_abs_scale.GetFloat() * m_pBatch_vis_bitmap->width()), 10, 0, 255, 128); m_nBatchVisY += 10; uint y = MAX(m_nBatchVisY + 20, 600), l = 0; m_pBatch_vis_bitmap->draw_formatted_text(0, y+8*(l++), 1, 255, 255, 255, "OpenGL Frame: %u, Batches+Clears: %u, Prims: %u, Program Changes: %u", m_nOverallPresents, g_nTotalDrawsOrClears, m_nTotalPrims, m_nNumProgramChanges ); m_pBatch_vis_bitmap->draw_formatted_text(0, y+8*(l++), 1, 255, 255, 255, "Frame: D3D Calls: %u, D3D Time: %3.3fms", m_nTotalD3DCalls, m_flTotalD3DTime); m_pBatch_vis_bitmap->draw_formatted_text(0, y+8*(l++), 1, 255, 255, 255, "Frame: GL Calls: %u, GL Time: %3.3fms", m_nTotalGLCalls, m_flTotalGLTime); l++; m_pBatch_vis_bitmap->draw_formatted_text(0, y+8*(l++), 1, 255, 255, 255, "Overall: Batches: %u, Prims: %u, Program Changes: %u", m_nOverallDraws, m_nOverallPrims, m_nOverallProgramChanges ); m_pBatch_vis_bitmap->draw_formatted_text(0, y+8*(l++), 1, 255, 255, 255, "Overall: D3D Calls: %u D3D Time: %4.3fms", m_nOverallD3DCalls, m_flOverallD3DTime ); m_pBatch_vis_bitmap->draw_formatted_text(0, y+8*(l++), 1, 255, 255, 255, "Overall: GL Calls: %u GL Time: %4.3fms", m_nOverallGLCalls, m_flOverallGLTime ); size_t png_size = 0; void *pPNG_data = tdefl_write_image_to_png_file_in_memory(m_pBatch_vis_bitmap->get_ptr(), m_pBatch_vis_bitmap->width(), m_pBatch_vis_bitmap->height(), 3, &png_size, true); if (pPNG_data) { char filename[256]; V_snprintf(filename, sizeof(filename), "left4dead2/batchvis_%u_%u.png", m_nBatchVisFileIdx, m_nBatchVisFrameIndex); FILE* pFile = fopen(filename, "wb"); if (pFile) { fwrite(pPNG_data, png_size, 1, pFile); fclose(pFile); } free(pPNG_data); } m_nBatchVisFrameIndex++; m_nBatchVisY = 0; m_pBatch_vis_bitmap->cls(); } #endif if (nPrevBatchVis != (int)gl_batch_vis.GetBool()) { if ( !m_pBatch_vis_bitmap ) m_pBatch_vis_bitmap = new simple_bitmap; nPrevBatchVis = gl_batch_vis.GetBool(); if (!nPrevBatchVis) { DumpStatsToConsole( NULL ); m_pBatch_vis_bitmap->clear(); } else { m_pBatch_vis_bitmap->init(768, 1024); } m_nBatchVisY = 0; m_nBatchVisFrameIndex = 0; m_nBatchVisFileIdx = (uint)time(NULL); //rand(); m_nOverallProgramChanges = 0; m_nOverallDraws = 0; m_nOverallD3DCalls = 0; m_flOverallD3DTime = 0; m_nOverallGLCalls = 0; m_flOverallGLTime = 0; m_flOverallPresentTime = 0; m_flOverallPresentTimeSquared = 0; m_flOverallSwapWindowTime = 0; m_flOverallSwapWindowTimeSquared = 0; m_nOverallPresents = 0; } g_nTotalD3DCycles = 0; g_nTotalD3DCalls = 0; gGL->m_nTotalGLCycles = 0; gGL->m_nTotalGLCalls = 0; m_nNumProgramChanges = 0; m_flTotalD3DTime = 0; m_nTotalD3DCalls = 0; m_flTotalGLTime = 0; m_nTotalGLCalls = 0; m_nTotalPrims = 0; #else if ( gl_batch_vis.GetBool() ) { gl_batch_vis.SetValue( false ); ConMsg( "Must define GL_BATCH_PERF_ANALYSIS to use this feature" ); } #endif g_nTotalDrawsOrClears = 0; #if GL_TELEMETRY_GPU_ZONES g_TelemetryGPUStats.Clear(); #endif return S_OK; } #ifdef OSX #pragma mark ----- Textures - (IDirect3DDevice9) #pragma mark ( create functions for each texture are now adjacent to the rest of the methods for each texture class) #endif HRESULT IDirect3DDevice9::GetTexture(DWORD Stage,IDirect3DBaseTexture9** ppTexture) { GL_BATCH_PERF_CALL_TIMER; GL_PUBLIC_ENTRYPOINT_CHECKS( this ); // if implemented, should it increase the ref count ?? DXABSTRACT_BREAK_ON_ERROR(); return S_OK; } #ifdef OSX #pragma mark ----- RTs and Surfaces - (IDirect3DDevice9) #endif HRESULT IDirect3DDevice9::CreateRenderTarget(UINT Width,UINT Height,D3DFORMAT Format,D3DMULTISAMPLE_TYPE MultiSample,DWORD MultisampleQuality,BOOL Lockable,IDirect3DSurface9** ppSurface,VD3DHANDLE* pSharedHandle, char *pDebugLabel) { GL_BATCH_PERF_CALL_TIMER; GL_PUBLIC_ENTRYPOINT_CHECKS( this ); HRESULT result = S_OK; m_ObjectStats.m_nTotalSurfaces++; m_ObjectStats.m_nTotalRenderTargets++; IDirect3DSurface9 *surf = new IDirect3DSurface9; surf->m_restype = D3DRTYPE_SURFACE; surf->m_device = this; // always set device on creations! GLMTexLayoutKey rtkey; memset( &rtkey, 0, sizeof(rtkey) ); rtkey.m_texGLTarget = GL_TEXTURE_2D; rtkey.m_xSize = Width; rtkey.m_ySize = Height; rtkey.m_zSize = 1; rtkey.m_texFormat = Format; rtkey.m_texFlags = kGLMTexRenderable; rtkey.m_texFlags |= kGLMTexSRGB; // all render target tex are SRGB mode if (m_ctx->Caps().m_cantAttachSRGB) { // this config can't support SRGB render targets. quietly turn off the sRGB bit. rtkey.m_texFlags &= ~kGLMTexSRGB; } if ( (MultiSample !=0) && (!m_ctx->Caps().m_nvG7x) ) { rtkey.m_texFlags |= kGLMTexMultisampled; rtkey.m_texSamples = MultiSample; // FIXME no support for "MS quality" yet } surf->m_tex = m_ctx->NewTex( &rtkey, 1, pDebugLabel ); surf->m_face = 0; surf->m_mip = 0; //desc surf->m_desc.Format = Format; surf->m_desc.Type = D3DRTYPE_SURFACE; surf->m_desc.Usage = 0; //FIXME ??????????? surf->m_desc.Pool = D3DPOOL_DEFAULT; //FIXME ??????????? surf->m_desc.MultiSampleType = MultiSample; surf->m_desc.MultiSampleQuality = MultisampleQuality; surf->m_desc.Width = Width; surf->m_desc.Height = Height; *ppSurface = (result==S_OK) ? surf : NULL; #if IUNKNOWN_ALLOC_SPEW char scratch[1024]; sprintf(scratch,"RT %s", surf->m_tex->m_layout->m_layoutSummary ); surf->SetMark( true, scratch ); #endif return result; } void IDirect3DDevice9::UpdateBoundFBO() { RenderTargetState_t renderTargetState; for ( uint i = 0; i < 4; i++ ) { renderTargetState.m_pRenderTargets[i] = m_pRenderTargets[i] ? m_pRenderTargets[i]->m_tex : NULL; } renderTargetState.m_pDepthStencil = m_pDepthStencil ? m_pDepthStencil->m_tex : NULL; CUtlMap < RenderTargetState_t, CGLMFBO * >::IndexType_t index = m_pFBOs->Find( renderTargetState ); if ( m_pFBOs->IsValidIndex( index ) ) { Assert( (*m_pFBOs)[index] ); m_ctx->m_drawingFBO = (*m_pFBOs)[index]; } else { CGLMFBO *newFBO = m_ctx->NewFBO(); m_pFBOs->Insert( renderTargetState, newFBO ); uint nNumBound = 0; for ( uint i = 0; i < 4; i++ ) { if ( !m_pRenderTargets[i] ) continue; GLMFBOTexAttachParams rtParams; memset( &rtParams, 0, sizeof(rtParams) ); rtParams.m_tex = m_pRenderTargets[i]->m_tex; rtParams.m_face = m_pRenderTargets[i]->m_face; rtParams.m_mip = m_pRenderTargets[i]->m_mip; rtParams.m_zslice = 0; newFBO->TexAttach( &rtParams, (EGLMFBOAttachment)(kAttColor0 + i) ); nNumBound++; } if ( m_pDepthStencil ) { GLMFBOTexAttachParams depthParams; memset( &depthParams, 0, sizeof(depthParams) ); depthParams.m_tex = m_pDepthStencil->m_tex; EGLMFBOAttachment destAttach = (depthParams.m_tex->m_layout->m_format->m_glDataFormat != 34041) ? kAttDepth : kAttDepthStencil; newFBO->TexAttach( &depthParams, destAttach ); nNumBound++; } (void)nNumBound; Assert( nNumBound ); #if GLMDEBUG Assert( newFBO->IsReady() ); #endif m_ctx->m_drawingFBO = newFBO; } m_ctx->BindFBOToCtx( m_ctx->m_drawingFBO, GL_FRAMEBUFFER_EXT ); m_bFBODirty = false; } void IDirect3DDevice9::ResetFBOMap() { if ( !m_pFBOs ) return; FOR_EACH_MAP_FAST( (*m_pFBOs), i ) { const RenderTargetState_t &rtState = m_pFBOs->Key( i ); (void)rtState; CGLMFBO *pFBO = (*m_pFBOs)[i]; m_ctx->DelFBO( pFBO ); } m_pFBOs->Purge(); m_bFBODirty = true; } void IDirect3DDevice9::ScrubFBOMap( CGLMTex *pTex ) { Assert( pTex ); if ( !m_pFBOs ) return; CUtlVectorFixed< RenderTargetState_t, 128 > fbosToRemove; FOR_EACH_MAP_FAST( (*m_pFBOs), i ) { const RenderTargetState_t &rtState = m_pFBOs->Key( i ); CGLMFBO *pFBO = (*m_pFBOs)[i]; (void)pFBO; if ( rtState.RefersTo( pTex ) ) { fbosToRemove.AddToTail( rtState ); } } for ( int i = 0; i < fbosToRemove.Count(); ++i ) { const RenderTargetState_t &rtState = fbosToRemove[i]; CUtlMap < RenderTargetState_t, CGLMFBO * >::IndexType_t index = m_pFBOs->Find( rtState ); if ( !m_pFBOs->IsValidIndex( index ) ) { Assert( 0 ); continue; } CGLMFBO *pFBO = (*m_pFBOs)[index]; m_ctx->DelFBO( pFBO ); m_pFBOs->RemoveAt( index ); m_bFBODirty = true; } //GLMDebugPrintf( "IDirect3DDevice9::ScrubFBOMap: Removed %u entries\n", fbosToRemove.Count() ); } HRESULT IDirect3DDevice9::SetRenderTarget(DWORD RenderTargetIndex,IDirect3DSurface9* pRenderTarget) { GL_BATCH_PERF_CALL_TIMER; GL_PUBLIC_ENTRYPOINT_CHECKS( this ); tmZone( TELEMETRY_LEVEL2, TMZF_NONE, "%s", __FUNCTION__ ); Assert( RenderTargetIndex < 4 ); HRESULT result = S_OK; GLMPRINTF(("-F- SetRenderTarget index=%d, surface=%8x (tex=%8x %s)", RenderTargetIndex, pRenderTarget, pRenderTarget ? pRenderTarget->m_tex : NULL, pRenderTarget ? pRenderTarget->m_tex->m_layout->m_layoutSummary : "" )); // note that it is OK to pass NULL for pRenderTarget, it implies that you would like to detach any color buffer from that target index // behaviors... // if new surf is same as old surf, no change in refcount, in fact, it's early exit IDirect3DSurface9 *oldTarget = m_pRenderTargets[RenderTargetIndex]; if (pRenderTarget == oldTarget) { GLMPRINTF(("-F- --> no change",RenderTargetIndex)); return S_OK; } // Fix this if porting to x86_64 if ( m_pRenderTargets[RenderTargetIndex] ) { // we now know that the new surf is not the same as the old surf. // you can't assume either one is non NULL here though. m_pRenderTargets[RenderTargetIndex]->Release( 1, "-A SetRenderTarget private release" ); } if (pRenderTarget) { pRenderTarget->AddRef( 1, "+A SetRenderTarget private addref" ); // again, private refcount being raised } m_pRenderTargets[RenderTargetIndex] = pRenderTarget; m_bFBODirty = true; /* if (!pRenderTarget) { GLMPRINTF(("-F- --> Setting NULL render target on index=%d ",RenderTargetIndex)); } else { GLMPRINTF(("-F- --> attaching index=%d on drawing FBO (%8x)",RenderTargetIndex, m_drawableFBO)); // attach color to FBO GLMFBOTexAttachParams rtParams; memset( &rtParams, 0, sizeof(rtParams) ); rtParams.m_tex = pRenderTarget->m_tex; rtParams.m_face = pRenderTarget->m_face; rtParams.m_mip = pRenderTarget->m_mip; rtParams.m_zslice = 0; // FIXME if you ever want to be able to render to slices of a 3D tex.. m_drawableFBO->TexAttach( &rtParams, (EGLMFBOAttachment)(kAttColor0 + RenderTargetIndex) ); } */ #if GL_BATCH_PERF_ANALYSIS && GL_BATCH_PERF_ANALYSIS_WRITE_PNGS if ( m_pBatch_vis_bitmap && m_pBatch_vis_bitmap->is_valid() && !RenderTargetIndex ) { m_pBatch_vis_bitmap->fill_box(0, m_nBatchVisY, m_pBatch_vis_bitmap->width(), 1, 30, 20, 20); m_nBatchVisY += 1; } #endif return result; } HRESULT IDirect3DDevice9::GetRenderTarget(DWORD RenderTargetIndex,IDirect3DSurface9** ppRenderTarget) { GL_BATCH_PERF_CALL_TIMER; GL_PUBLIC_ENTRYPOINT_CHECKS( this ); if ( !m_pRenderTargets[ RenderTargetIndex ] ) return D3DERR_NOTFOUND; if ( ( RenderTargetIndex > 4 ) || !ppRenderTarget ) return D3DERR_INVALIDCALL; // safe because of early exit on NULL above m_pRenderTargets[ RenderTargetIndex ]->AddRef(0, "+B GetRenderTarget public addref"); // per http://msdn.microsoft.com/en-us/library/bb174404(VS.85).aspx *ppRenderTarget = m_pRenderTargets[ RenderTargetIndex ]; return S_OK; } HRESULT IDirect3DDevice9::CreateOffscreenPlainSurface( UINT Width,UINT Height,D3DFORMAT Format,D3DPOOL Pool,IDirect3DSurface9** ppSurface,VD3DHANDLE* pSharedHandle ) { GL_BATCH_PERF_CALL_TIMER; GL_PUBLIC_ENTRYPOINT_CHECKS( this ); // set surf->m_restype to D3DRTYPE_SURFACE... // this is almost identical to CreateRenderTarget.. HRESULT result = S_OK; m_ObjectStats.m_nTotalSurfaces++; m_ObjectStats.m_nTotalRenderTargets++; IDirect3DSurface9 *surf = new IDirect3DSurface9; surf->m_restype = D3DRTYPE_SURFACE; surf->m_device = this; // always set device on creations! GLMTexLayoutKey rtkey; memset( &rtkey, 0, sizeof(rtkey) ); rtkey.m_texGLTarget = GL_TEXTURE_2D; rtkey.m_xSize = Width; rtkey.m_ySize = Height; rtkey.m_zSize = 1; rtkey.m_texFormat = Format; rtkey.m_texFlags = kGLMTexRenderable; surf->m_tex = m_ctx->NewTex( &rtkey, 1, "offscreen plain surface" ); surf->m_face = 0; surf->m_mip = 0; //desc surf->m_desc.Format = Format; surf->m_desc.Type = D3DRTYPE_SURFACE; surf->m_desc.Usage = 0; surf->m_desc.Pool = D3DPOOL_DEFAULT; surf->m_desc.MultiSampleType = D3DMULTISAMPLE_NONE; surf->m_desc.MultiSampleQuality = 0; surf->m_desc.Width = Width; surf->m_desc.Height = Height; *ppSurface = (result==S_OK) ? surf : NULL; return result; } HRESULT IDirect3DDevice9::CreateDepthStencilSurface(UINT Width,UINT Height,D3DFORMAT Format,D3DMULTISAMPLE_TYPE MultiSample,DWORD MultisampleQuality,BOOL Discard,IDirect3DSurface9** ppSurface,VD3DHANDLE* pSharedHandle) { GL_BATCH_PERF_CALL_TIMER; GL_PUBLIC_ENTRYPOINT_CHECKS( this ); Assert( ( Format == D3DFMT_D16 ) || ( Format == D3DFMT_D24X8 ) || ( Format == D3DFMT_D24S8 ) ); HRESULT result = S_OK; m_ObjectStats.m_nTotalSurfaces++; m_ObjectStats.m_nTotalRenderTargets++; IDirect3DSurface9 *surf = new IDirect3DSurface9; surf->m_restype = D3DRTYPE_SURFACE; surf->m_device = this; // always set device on creations! GLMTexLayoutKey depthkey; memset( &depthkey, 0, sizeof(depthkey) ); depthkey.m_texGLTarget = GL_TEXTURE_2D; depthkey.m_xSize = Width; depthkey.m_ySize = Height; depthkey.m_zSize = 1; depthkey.m_texFormat = Format; depthkey.m_texFlags = kGLMTexRenderable | kGLMTexIsDepth; if ( Format == D3DFMT_D24S8 ) { depthkey.m_texFlags |= kGLMTexIsStencil; } if ( (MultiSample !=0) && (!m_ctx->Caps().m_nvG7x) ) { depthkey.m_texFlags |= kGLMTexMultisampled; depthkey.m_texSamples = MultiSample; // FIXME no support for "MS quality" yet } surf->m_tex = m_ctx->NewTex( &depthkey, 1, "depth-stencil surface" ); surf->m_face = 0; surf->m_mip = 0; //desc surf->m_desc.Format = Format; surf->m_desc.Type = D3DRTYPE_SURFACE; surf->m_desc.Usage = 0; //FIXME ??????????? surf->m_desc.Pool = D3DPOOL_DEFAULT; //FIXME ??????????? surf->m_desc.MultiSampleType = MultiSample; surf->m_desc.MultiSampleQuality = MultisampleQuality; surf->m_desc.Width = Width; surf->m_desc.Height = Height; *ppSurface = (result==S_OK) ? surf : NULL; return result; } HRESULT IDirect3DDevice9::SetDepthStencilSurface( IDirect3DSurface9* pNewZStencil ) { GL_BATCH_PERF_CALL_TIMER; GL_PUBLIC_ENTRYPOINT_CHECKS( this ); HRESULT result = S_OK; GLMPRINTF(("-F- SetDepthStencilSurface, surface=%8x (tex=%8x %s)", pNewZStencil, pNewZStencil ? pNewZStencil->m_tex : NULL, pNewZStencil ? pNewZStencil->m_tex->m_layout->m_layoutSummary : "" )); if ( pNewZStencil == m_pDepthStencil ) { GLMPRINTF(("-F- --> no change")); return S_OK; } if ( pNewZStencil ) { pNewZStencil->AddRef(1, "+A SetDepthStencilSurface private addref"); } if ( m_pDepthStencil ) { // Note this Release() could cause the surface to be deleted! m_pDepthStencil->Release(1, "-A SetDepthStencilSurface private release"); } m_pDepthStencil = pNewZStencil; m_bFBODirty = true; return result; } HRESULT IDirect3DDevice9::GetDepthStencilSurface(IDirect3DSurface9** ppZStencilSurface) { GL_BATCH_PERF_CALL_TIMER; GL_PUBLIC_ENTRYPOINT_CHECKS( this ); if ( !ppZStencilSurface ) { return D3DERR_INVALIDCALL; } if ( !m_pDepthStencil ) { *ppZStencilSurface = NULL; return D3DERR_NOTFOUND; } m_pDepthStencil->AddRef(0, "+B GetDepthStencilSurface public addref"); // per http://msdn.microsoft.com/en-us/library/bb174384(VS.85).aspx *ppZStencilSurface = m_pDepthStencil; return S_OK; } HRESULT IDirect3DDevice9::GetRenderTargetData(IDirect3DSurface9* pRenderTarget,IDirect3DSurface9* pDestSurface) { GL_BATCH_PERF_CALL_TIMER; GL_PUBLIC_ENTRYPOINT_CHECKS( this ); // is it just a blit ? this->StretchRect( pRenderTarget, NULL, pDestSurface, NULL, D3DTEXF_NONE ); // is this good enough ??? return S_OK; } HRESULT IDirect3DDevice9::GetFrontBufferData(UINT iSwapChain,IDirect3DSurface9* pDestSurface) { GL_BATCH_PERF_CALL_TIMER; GL_PUBLIC_ENTRYPOINT_CHECKS( this ); DXABSTRACT_BREAK_ON_ERROR(); return S_OK; } HRESULT IDirect3DDevice9::StretchRect(IDirect3DSurface9* pSourceSurface,CONST RECT* pSourceRect,IDirect3DSurface9* pDestSurface,CONST RECT* pDestRect,D3DTEXTUREFILTERTYPE Filter) { GL_BATCH_PERF_CALL_TIMER; GL_PUBLIC_ENTRYPOINT_CHECKS( this ); // find relevant slices in GLM tex if ( m_bFBODirty ) { UpdateBoundFBO(); } CGLMTex *srcTex = pSourceSurface->m_tex; int srcSliceIndex = srcTex->CalcSliceIndex( pSourceSurface->m_face, pSourceSurface->m_mip ); GLMTexLayoutSlice *srcSlice = &srcTex->m_layout->m_slices[ srcSliceIndex ]; CGLMTex *dstTex = pDestSurface->m_tex; int dstSliceIndex = dstTex->CalcSliceIndex( pDestSurface->m_face, pDestSurface->m_mip ); GLMTexLayoutSlice *dstSlice = &dstTex->m_layout->m_slices[ dstSliceIndex ]; if ( dstTex->m_rboName != 0 ) { Assert(!"No path yet for blitting into an MSAA tex"); return S_OK; } bool useFastBlit = (gl_blitmode.GetInt() != 0); if ( !useFastBlit && (srcTex->m_rboName !=0)) // old way, we do a resolve to scratch tex first (necessitating two step blit) { m_ctx->ResolveTex( srcTex, true ); } // set up source/dest rect in GLM form GLMRect srcRect, dstRect; // d3d nomenclature: // Y=0 is the visual top and also aligned with V=0. srcRect.xmin = pSourceRect ? pSourceRect->left : 0; srcRect.xmax = pSourceRect ? pSourceRect->right : srcSlice->m_xSize; srcRect.ymin = pSourceRect ? pSourceRect->top : 0; srcRect.ymax = pSourceRect ? pSourceRect->bottom : srcSlice->m_ySize; dstRect.xmin = pDestRect ? pDestRect->left : 0; dstRect.xmax = pDestRect ? pDestRect->right : dstSlice->m_xSize; dstRect.ymin = pDestRect ? pDestRect->top : 0; dstRect.ymax = pDestRect ? pDestRect->bottom : dstSlice->m_ySize; GLenum filterGL = 0; switch(Filter) { case D3DTEXF_NONE: case D3DTEXF_POINT: filterGL = GL_NEAREST; break; case D3DTEXF_LINEAR: filterGL = GL_LINEAR; break; default: // D3DTEXF_ANISOTROPIC Assert(!"Impl aniso stretch"); break; } if (useFastBlit) { m_ctx->Blit2( srcTex, &srcRect, pSourceSurface->m_face, pSourceSurface->m_mip, dstTex, &dstRect, pDestSurface->m_face, pDestSurface->m_mip, filterGL ); } else { m_ctx->BlitTex( srcTex, &srcRect, pSourceSurface->m_face, pSourceSurface->m_mip, dstTex, &dstRect, pDestSurface->m_face, pDestSurface->m_mip, filterGL ); } return S_OK; } // This totally sucks, but this information can't be gleaned any // other way when translating from D3D to GL at this level // // This returns a mask, since multiple GLSL "varyings" can be tagged with centroid static uint32 CentroidMaskFromName( bool bPixelShader, const char *pName ) { // Important note: This code has been customized for TF2 - don't blindly merge it into other branches! if ( !pName ) return 0; // Important: The centroid bitflags must match between all linked vertex/pixel shaders! if ( bPixelShader ) { if ( V_stristr( pName, "lightmappedgeneric_ps" ) || V_strstr( pName, "worldtwotextureblend_ps" ) ) { return (0x01 << 2) | (0x01 << 3); // iterators 2 and 3 } else if ( V_stristr( pName, "lightmappedreflective_ps" ) ) { return (0x01 << 6) | (0x01 << 7); // iterators 6 and 7 } else if ( V_stristr( pName, "water_ps" ) ) { return 0xC0; } else if ( V_stristr( pName, "shadow_ps" ) ) { return 0x1F; } else if ( V_stristr( pName, "ShatteredGlass_ps" ) ) { return 0xC; } else if ( V_stristr( pName, "WorldVertexAlpha_ps" ) || V_stristr( pName, "WorldVertexTransition_ps" ) ) { // These pixel shaders want centroid but shouldn't be used Assert(0); return 0; } else if ( V_stristr( pName, "flashlight_ps" ) ) { return 0xC; } } else // vertex shader { // Vertex shaders also if ( V_stristr( pName, "lightmappedgeneric_vs" ) ) { return (0x01 << 2) | (0x01 << 3); // iterators 2 and 3 } else if ( V_stristr( pName, "lightmappedreflective_vs" ) ) { return (0x01 << 6) | (0x01 << 7); // iterators 6 and 7 } else if ( V_stristr( pName, "water_vs" ) ) { return 0xC0; } else if ( V_stristr( pName, "shadow_vs" ) ) { return 0x1F; } else if ( V_stristr( pName, "ShatteredGlass_vs" ) ) { return 0xC; } else if ( V_stristr( pName, "flashlight_vs" ) ) { return 0xC; } } // This shader doesn't have any centroid iterators return 0; } // This totally sucks, but this information can't be gleaned any // other way when translating from D3D to GL at this level static int ShadowDepthSamplerMaskFromName( const char *pName ) { if ( !pName ) return 0; if ( V_stristr( pName, "water_ps" ) ) { return (1<<7); } else if ( V_stristr( pName, "infected_ps" ) ) { return (1<<1); } else if ( V_stristr( pName, "phong_ps" ) ) { return (1<<4) | (1<<15); } else if ( V_stristr( pName, "vertexlit_and_unlit_generic_bump_ps" ) ) { return (1<<8) | (1<<15); } else if ( V_stristr( pName, "vertexlit_and_unlit_generic_ps" ) ) { return (1<<8) | (1<<15); } else if ( V_stristr( pName, "eye_refract_ps" ) ) { return (1<<6); } else if ( V_stristr( pName, "eyes_flashlight_ps" ) ) { return (1<<4); } else if ( V_stristr( pName, "worldtwotextureblend_ps" ) ) { return (1<<7); } else if ( V_stristr( pName, "teeth_flashlight_ps" ) ) { return (1<<2); } else if ( V_stristr( pName, "flashlight_ps" ) ) // substring of above, make sure this comes last!! { return (1<<7); } else if ( V_stristr( pName, "lightmappedgeneric_ps" ) ) { return (1<<15); } else if ( V_stristr( pName, "deferred_global_light_ps" ) ) { return (1<<14); } else if ( V_stristr( pName, "global_lit_simple_ps" ) ) { return (1<<14); } else if ( V_stristr( pName, "lightshafts_ps" ) ) { return (1<<1); } else if ( V_stristr( pName, "multiblend_combined_ps" ) ) { return (1<<14); } else if ( V_stristr( pName, "multiblend_ps" ) ) { return (1<<14); } else if ( V_stristr( pName, "customhero_ps" ) ) { return (1<<14); } // This shader doesn't have a shadow depth map sampler return 0; } #ifdef OSX #pragma mark ----- Pixel Shaders - (IDirect3DDevice9) #endif HRESULT IDirect3DDevice9::CreatePixelShader(CONST DWORD* pFunction,IDirect3DPixelShader9** ppShader, const char *pShaderName, char *pDebugLabel, const uint32 *pCentroidMask ) { GL_BATCH_PERF_CALL_TIMER; GL_PUBLIC_ENTRYPOINT_CHECKS( this ); HRESULT result = D3DERR_INVALIDCALL; *ppShader = NULL; int nShadowDepthSamplerMask = ShadowDepthSamplerMaskFromName( pShaderName ); uint nCentroidMask = CentroidMaskFromName( true, pShaderName ); if ( pCentroidMask ) { if ( *pCentroidMask != nCentroidMask ) { GLMDebugPrintf( "IDirect3DDevice9::CreatePixelShader: shaderapi's centroid mask (0x%08X) differs from mask derived from shader name (0x%08X) for shader %s\n", *pCentroidMask, nCentroidMask, pDebugLabel ); } // It would be great if we could use these centroid masks passed in from shaderapi - but unfortunately they're only available for pixel shaders, and we also need to compute matching masks for vertex shaders! //nCentroidMask = *pCentroidMask; } { int numTranslations = 1; bool bVertexShader = false; // we can do one or two translated forms. they go together in a single buffer with some markers to allow GLM to break it up. // this also lets us mirror each set of translations to disk with a single file making it easier to view and edit side by side. int maxTranslationSize = 50000; // size of any one translation CUtlBuffer transbuf( 3000, numTranslations * maxTranslationSize, CUtlBuffer::TEXT_BUFFER ); CUtlBuffer tempbuf( 3000, maxTranslationSize, CUtlBuffer::TEXT_BUFFER ); transbuf.PutString( "//GLSLfp\n" ); // this is required so GLM can crack the text apart // note the GLSL translator wants its own buffer tempbuf.EnsureCapacity( maxTranslationSize ); uint glslPixelShaderOptions = D3DToGL_OptionUseEnvParams;// | D3DToGL_OptionAllowStaticControlFlow; // Fake SRGB mode - needed on R500, probably indefinitely. // Do this stuff if caps show m_needsFakeSRGB=true and the sRGBWrite state is true // (but not if it's engine_post which is special) if (!m_ctx->Caps().m_hasGammaWrites) { if ( pShaderName ) { if ( !V_stristr( pShaderName, "engine_post" ) ) { glslPixelShaderOptions |= D3DToGL_OptionSRGBWriteSuffix; } } } g_D3DToOpenGLTranslatorGLSL.TranslateShader( (uint32 *) pFunction, &tempbuf, &bVertexShader, glslPixelShaderOptions, nShadowDepthSamplerMask, nCentroidMask, pDebugLabel ); transbuf.PutString( (char*)tempbuf.Base() ); transbuf.PutString( "\n\n" ); // whitespace if ( bVertexShader ) { // don't cross the streams Assert(!"Can't accept vertex shader in CreatePixelShader"); result = D3DERR_INVALIDCALL; } else { m_ObjectStats.m_nTotalPixelShaders++; IDirect3DPixelShader9 *newprog = new IDirect3DPixelShader9; newprog->m_pixHighWater = 0; newprog->m_pixSamplerMask = 0; newprog->m_pixSamplerTypes = 0; newprog->m_pixProgram = m_ctx->NewProgram( kGLMFragmentProgram, (char *)transbuf.Base(), pShaderName ? pShaderName : "?" ) ; newprog->m_pixProgram->m_nCentroidMask = nCentroidMask; newprog->m_pixProgram->m_nShadowDepthSamplerMask = nShadowDepthSamplerMask; newprog->m_pixProgram->m_bTranslatedProgram = true; newprog->m_pixProgram->m_maxVertexAttrs = 0; newprog->m_device = this; //------ find the frag program metadata and extract it.. { // find the highwater mark char *highWaterPrefix = "//HIGHWATER-"; // try to arrange this so it can work with pure GLSL if needed char *highWaterStr = strstr( (char *)transbuf.Base(), highWaterPrefix ); if (highWaterStr) { char *highWaterActualData = highWaterStr + strlen( highWaterPrefix ); int value = -1; sscanf( highWaterActualData, "%d", &value ); newprog->m_pixHighWater = value; newprog->m_pixProgram->m_descs[kGLMGLSL].m_highWater = value; } else { Assert(!"couldn't find sampler map in pixel shader"); } } { // find the sampler map char *samplerMaskPrefix = "//SAMPLERMASK-"; // try to arrange this so it can work with pure GLSL if needed char *samplerMaskStr = strstr( (char *)transbuf.Base(), samplerMaskPrefix ); if (samplerMaskStr) { char *samplerMaskActualData = samplerMaskStr + strlen( samplerMaskPrefix ); int value = -1; sscanf( samplerMaskActualData, "%04x", &value ); newprog->m_pixSamplerMask = value; newprog->m_pixProgram->m_samplerMask = value; // helps GLM maintain a better linked pair cache even when SRGB sampler state changes int nMaxReg; for ( nMaxReg = 31; nMaxReg >= 0; --nMaxReg ) if ( value & ( 1 << nMaxReg ) ) break; newprog->m_pixProgram->m_maxSamplers = nMaxReg + 1; int nNumUsedSamplers = 0; for ( int i = 31; i >= 0; --i) if ( value & ( 1 << i ) ) nNumUsedSamplers++; newprog->m_pixProgram->m_nNumUsedSamplers = nNumUsedSamplers; } else { Assert(!"couldn't find sampler map in pixel shader"); } } { // find the sampler map char *samplerTypesPrefix = "//SAMPLERTYPES-"; // try to arrange this so it can work with pure GLSL if needed char *samplerTypesStr = strstr( (char *)transbuf.Base(), samplerTypesPrefix ); if (samplerTypesStr) { char *samplerTypesActualData = samplerTypesStr + strlen( samplerTypesPrefix ); int value = -1; sscanf( samplerTypesActualData, "%08x", &value ); newprog->m_pixSamplerTypes = value; newprog->m_pixProgram->m_samplerTypes = value; // helps GLM maintain a better linked pair cache even when SRGB sampler state changes } else { Assert(!"couldn't find sampler types in pixel shader"); } } { // find the fb outputs used by this shader/combo const GLenum buffers[] = { GL_COLOR_ATTACHMENT0_EXT, GL_COLOR_ATTACHMENT1_EXT, GL_COLOR_ATTACHMENT2_EXT, GL_COLOR_ATTACHMENT3_EXT }; char *fragDataMaskPrefix = "//FRAGDATAMASK-"; char *fragDataMaskStr = strstr( (char *)transbuf.Base(), fragDataMaskPrefix ); if ( fragDataMaskStr ) { char *fragDataActualData = fragDataMaskStr + strlen( fragDataMaskPrefix ); int value = -1; sscanf( fragDataActualData, "%04x", &value ); newprog->m_pixFragDataMask = value; newprog->m_pixProgram->m_fragDataMask = value; newprog->m_pixProgram->m_numDrawBuffers = 0; for( int i = 0; i < 4; i++ ) { if( newprog->m_pixProgram->m_fragDataMask & ( 1 << i ) ) { newprog->m_pixProgram->m_drawBuffers[ newprog->m_pixProgram->m_numDrawBuffers ] = buffers[ i ]; newprog->m_pixProgram->m_numDrawBuffers++; } } if( newprog->m_pixProgram->m_numDrawBuffers == 0 ) { Assert(!"couldn't find fragment output in pixel shader"); newprog->m_pixProgram->m_drawBuffers[ 0 ] = buffers[ 0 ]; newprog->m_pixProgram->m_numDrawBuffers = 1; } } else { newprog->m_pixFragDataMask = 0; newprog->m_pixProgram->m_fragDataMask = 0; } } *ppShader = newprog; result = S_OK; } } return result; } IDirect3DPixelShader9::~IDirect3DPixelShader9() { GL_BATCH_PERF_CALL_TIMER; GL_PUBLIC_ENTRYPOINT_CHECKS( m_device ); GLMPRINTF(( ">-A- ~IDirect3DPixelShader9" )); if (m_device) { m_device->ReleasedPixelShader( this ); if (m_pixProgram) { m_pixProgram->m_ctx->DelProgram( m_pixProgram ); m_pixProgram = NULL; } m_device = NULL; } GLMPRINTF(( "<-A- ~IDirect3DPixelShader9" )); } HRESULT IDirect3DDevice9::SetPixelShaderNonInline(IDirect3DPixelShader9* pShader) { GL_BATCH_PERF_CALL_TIMER; GL_PUBLIC_ENTRYPOINT_CHECKS( this ); m_ctx->SetFragmentProgram( pShader ? pShader->m_pixProgram : NULL ); m_pixelShader = pShader; return S_OK; } HRESULT IDirect3DDevice9::SetPixelShaderConstantFNonInline(UINT StartRegister,CONST float* pConstantData,UINT Vector4fCount) { GL_BATCH_PERF_CALL_TIMER; GL_PUBLIC_ENTRYPOINT_CHECKS( this ); TOGL_NULL_DEVICE_CHECK; #if 0 const uint nRegToWatch = 3; if ( ( ( StartRegister + Vector4fCount ) > nRegToWatch ) && ( StartRegister <= nRegToWatch ) ) { GLMDebugPrintf( "-- %f %f %f %f\n", pConstantData[(nRegToWatch - StartRegister)*4+0], pConstantData[(nRegToWatch - StartRegister)*4+1], pConstantData[(nRegToWatch - StartRegister)*4+2], pConstantData[(nRegToWatch - StartRegister)*4+3] ); } #endif m_ctx->SetProgramParametersF( kGLMFragmentProgram, StartRegister, (float *)pConstantData, Vector4fCount ); return S_OK; } HRESULT IDirect3DDevice9::SetPixelShaderConstantB(UINT StartRegister,CONST BOOL* pConstantData,UINT BoolCount) { GL_BATCH_PERF_CALL_TIMER; GL_PUBLIC_ENTRYPOINT_CHECKS( this ); TOGL_NULL_DEVICE_CHECK; m_ctx->SetProgramParametersB( kGLMFragmentProgram, StartRegister, (int *)pConstantData, BoolCount ); return S_OK; } HRESULT IDirect3DDevice9::SetPixelShaderConstantI(UINT StartRegister,CONST int* pConstantData,UINT Vector4iCount) { GL_BATCH_PERF_CALL_TIMER; GL_PUBLIC_ENTRYPOINT_CHECKS( this ); TOGL_NULL_DEVICE_CHECK; GLMPRINTF(("-X- Ignoring IDirect3DDevice9::SetPixelShaderConstantI call, count was %d", Vector4iCount )); // m_ctx->SetProgramParametersI( kGLMFragmentProgram, StartRegister, pConstantData, Vector4iCount ); return S_OK; } #ifdef OSX #pragma mark ----- Vertex Shaders - (IDirect3DDevice9) #endif HRESULT IDirect3DDevice9::CreateVertexShader(CONST DWORD* pFunction, IDirect3DVertexShader9** ppShader, const char *pShaderName, char *pDebugLabel) { GL_BATCH_PERF_CALL_TIMER; GL_PUBLIC_ENTRYPOINT_CHECKS( this ); HRESULT result = D3DERR_INVALIDCALL; *ppShader = NULL; uint32 nCentroidMask = CentroidMaskFromName( false, pShaderName ); { int numTranslations = 1; bool bVertexShader = false; // we can do one or two translated forms. they go together in a single buffer with some markers to allow GLM to break it up. // this also lets us mirror each set of translations to disk with a single file making it easier to view and edit side by side. int maxTranslationSize = 500000; // size of any one translation CUtlBuffer transbuf( 1000, numTranslations * maxTranslationSize, CUtlBuffer::TEXT_BUFFER ); CUtlBuffer tempbuf( 1000, maxTranslationSize, CUtlBuffer::TEXT_BUFFER ); transbuf.PutString( "//GLSLvp\n" ); // this is required so GLM can crack the text apart // note the GLSL translator wants its own buffer tempbuf.EnsureCapacity( maxTranslationSize ); uint glslVertexShaderOptions = D3DToGL_OptionUseEnvParams | D3DToGL_OptionDoFixupZ | D3DToGL_OptionDoFixupY; if ( m_ctx->Caps().m_hasNativeClipVertexMode ) { // note the matched trickery over in IDirect3DDevice9::FlushStates - // if on a chipset that does no have native gl_ClipVertex support, then // omit writes to gl_ClipVertex, and instead submit plane equations that have been altered, // and clipping will take place in GL space using gl_Position instead of gl_ClipVertex. // note that this is very much a hack to mate up with ATI R5xx hardware constraints, and with older // drivers even for later ATI parts like r6xx/r7xx. And it doesn't work on NV parts, so you really // do have to choose the right way to go. glslVertexShaderOptions |= D3DToGL_OptionDoUserClipPlanes; } if ( !CommandLine()->CheckParm("-disableboneuniformbuffers") ) { // If using GLSL, enabling a uniform buffer specifically for bone registers. (Not currently supported with ARB shaders, which are not optimized at all anyway.) glslVertexShaderOptions |= D3DToGL_OptionGenerateBoneUniformBuffer; } g_D3DToOpenGLTranslatorGLSL.TranslateShader( (uint32 *) pFunction, &tempbuf, &bVertexShader, glslVertexShaderOptions, -1, nCentroidMask, pDebugLabel ); transbuf.PutString( (char*)tempbuf.Base() ); transbuf.PutString( "\n\n" ); // whitespace if ( !bVertexShader ) { // don't cross the streams Assert(!"Can't accept pixel shader in CreateVertexShader"); result = D3DERR_INVALIDCALL; } else { m_ObjectStats.m_nTotalVertexShaders++; IDirect3DVertexShader9 *newprog = new IDirect3DVertexShader9; newprog->m_device = this; newprog->m_vtxProgram = m_ctx->NewProgram( kGLMVertexProgram, (char *)transbuf.Base(), pShaderName ? pShaderName : "?" ) ; newprog->m_vtxProgram->m_nCentroidMask = nCentroidMask; newprog->m_vtxProgram->m_bTranslatedProgram = true; newprog->m_vtxProgram->m_maxVertexAttrs = 0; newprog->m_maxVertexAttrs = 0; // find the highwater mark.. char *highWaterPrefix = "//HIGHWATER-"; // try to arrange this so it can work with pure GLSL if needed char *highWaterStr = strstr( (char *)transbuf.Base(), highWaterPrefix ); if (highWaterStr) { char *highWaterActualData = highWaterStr + strlen( highWaterPrefix ); int value = -1; sscanf( highWaterActualData, "%d", &value ); newprog->m_vtxHighWater = value; newprog->m_vtxProgram->m_descs[kGLMGLSL].m_highWater = value; } else { Assert(!"couldn't find highwater mark in vertex shader"); } char *highWaterBonePrefix = "//HIGHWATERBONE-"; // try to arrange this so it can work with pure GLSL if needed char *highWaterBoneStr = strstr( (char *)transbuf.Base(), highWaterBonePrefix ); if (highWaterBoneStr) { char *highWaterActualData = highWaterBoneStr + strlen( highWaterBonePrefix ); int value = -1; sscanf( highWaterActualData, "%d", &value ); newprog->m_vtxHighWaterBone = value; newprog->m_vtxProgram->m_descs[kGLMGLSL].m_VSHighWaterBone = value; } else { newprog->m_vtxHighWaterBone = 0; newprog->m_vtxProgram->m_descs[kGLMGLSL].m_VSHighWaterBone = 0; } // find the attrib map.. char *attribMapPrefix = "//ATTRIBMAP-"; // try to arrange this so it can work with pure GLSL if needed char *attribMapStr = strstr( (char *)transbuf.Base(), attribMapPrefix ); if (attribMapStr) { char *attribMapActualData = attribMapStr + strlen( attribMapPrefix ); uint nMaxVertexAttribs = 0; for( int i=0; i<16; i++) { int value = -1; char *dataItem = attribMapActualData + (i*3); sscanf( dataItem, "%02x", &value ); if (value >=0) { // make sure it's not a terminator if (value == 0xBB) { DXABSTRACT_BREAK_ON_ERROR(); } } else { // probably an 'xx'... check if ( (dataItem[0] != 'x') || (dataItem[1] != 'x') ) { DXABSTRACT_BREAK_ON_ERROR(); // bad news } else { value = 0xBB; // not likely to see one of these... "fog with usage index 11" } } if ( value != 0xBB ) nMaxVertexAttribs = i; newprog->m_vtxAttribMap[i] = value; } newprog->m_vtxProgram->m_maxVertexAttrs = nMaxVertexAttribs + 1; newprog->m_maxVertexAttrs = nMaxVertexAttribs + 1; } else { DXABSTRACT_BREAK_ON_ERROR(); // that's bad... } *ppShader = newprog; result = S_OK; } } return result; } IDirect3DVertexShader9::~IDirect3DVertexShader9() { GL_BATCH_PERF_CALL_TIMER; GL_PUBLIC_ENTRYPOINT_CHECKS( m_device ); GLMPRINTF(( ">-A- ~IDirect3DVertexShader9" )); if (m_device) { m_device->ReleasedVertexShader( this ); if (m_vtxProgram) { m_vtxProgram->m_ctx->DelProgram( m_vtxProgram ); m_vtxProgram = NULL; } m_device = NULL; } else { } GLMPRINTF(( "<-A- ~IDirect3DVertexShader9" )); } HRESULT IDirect3DDevice9::SetVertexShaderNonInline(IDirect3DVertexShader9* pShader) { GL_BATCH_PERF_CALL_TIMER; GL_PUBLIC_ENTRYPOINT_CHECKS( this ); m_ctx->SetVertexProgram( pShader ? pShader->m_vtxProgram : NULL ); m_vertexShader = pShader; return S_OK; } HRESULT IDirect3DDevice9::SetVertexShaderConstantFNonInline(UINT StartRegister,CONST float* pConstantData,UINT Vector4fCount) // groups of 4 floats! { GL_BATCH_PERF_CALL_TIMER; GL_PUBLIC_ENTRYPOINT_CHECKS( this ); TOGL_NULL_DEVICE_CHECK; m_ctx->SetProgramParametersF( kGLMVertexProgram, StartRegister, (float *)pConstantData, Vector4fCount ); return S_OK; } HRESULT IDirect3DDevice9::SetVertexShaderConstantBNonInline(UINT StartRegister,CONST BOOL* pConstantData,UINT BoolCount) // individual bool count! { GL_BATCH_PERF_CALL_TIMER; GL_PUBLIC_ENTRYPOINT_CHECKS( this ); TOGL_NULL_DEVICE_CHECK; m_ctx->SetProgramParametersB( kGLMVertexProgram, StartRegister, (int *)pConstantData, BoolCount ); return S_OK; } HRESULT IDirect3DDevice9::SetVertexShaderConstantINonInline(UINT StartRegister,CONST int* pConstantData,UINT Vector4iCount) // groups of 4 ints! { GL_BATCH_PERF_CALL_TIMER; GL_PUBLIC_ENTRYPOINT_CHECKS( this ); TOGL_NULL_DEVICE_CHECK; m_ctx->SetProgramParametersI( kGLMVertexProgram, StartRegister, (int *)pConstantData, Vector4iCount ); return S_OK; } #ifdef OSX #pragma mark ----- Shader Pairs - (IDirect3DDevice9) #endif // callers need to ifdef POSIX this, because this method does not exist on real DX9 HRESULT IDirect3DDevice9::LinkShaderPair( IDirect3DVertexShader9* vs, IDirect3DPixelShader9* ps ) { GL_BATCH_PERF_CALL_TIMER; // these are really GLSL "shaders" not "programs" but the old reference to "program" persists due to the assembler heritage if (vs->m_vtxProgram && ps->m_pixProgram) { m_ctx->LinkShaderPair( vs->m_vtxProgram, ps->m_pixProgram ); } return S_OK; } HRESULT IDirect3DDevice9::ValidateShaderPair( IDirect3DVertexShader9* vs, IDirect3DPixelShader9* ps ) { GL_BATCH_PERF_CALL_TIMER; // these are really GLSL "shaders" not "programs" but the old reference to "program" persists due to the assembler heritage if (vs->m_vtxProgram && ps->m_pixProgram) { m_ctx->ValidateShaderPair( vs->m_vtxProgram, ps->m_pixProgram ); } return S_OK; } // callers need to ifdef POSIX this, because this method does not exist on real DX9 // HRESULT IDirect3DDevice9::QueryShaderPair( int index, GLMShaderPairInfo *infoOut ) { GL_BATCH_PERF_CALL_TIMER; // these are really GLSL "shaders" not "programs" ... m_ctx->QueryShaderPair( index, infoOut ); return S_OK; } #ifdef OSX #pragma mark ----- Vertex Buffers and Vertex Declarations - (IDirect3DDevice9) #endif HRESULT IDirect3DDevice9::CreateVertexDeclaration(CONST D3DVERTEXELEMENT9* pVertexElements,IDirect3DVertexDeclaration9** ppDecl) { GL_BATCH_PERF_CALL_TIMER; GL_PUBLIC_ENTRYPOINT_CHECKS( this ); *ppDecl = NULL; // the goal here is to arrive at something which lets us quickly generate GLMVertexSetups. // the information we don't have, that must be inferred from the decls, is: // -> how many unique streams (buffers) are used - pure curiosity // -> what the stride and offset is for each decl. Size you can figure out on the spot, stride requires surveying all the components in each stream first. // so init an array of per-stream offsets to 0. // each one is a cursor that gets bumped by decls. uint streamOffsets[ D3D_MAX_STREAMS ]; uint streamCount = 0; uint attribMap[16]; uint attribMapIndex = 0; memset( attribMap, 0xFF, sizeof( attribMap ) ); memset( streamOffsets, 0, sizeof( streamOffsets ) ); m_ObjectStats.m_nTotalVertexDecls++; IDirect3DVertexDeclaration9 *decl9 = new IDirect3DVertexDeclaration9; decl9->m_device = this; decl9->m_elemCount = 0; for (const D3DVERTEXELEMENT9 *src = pVertexElements; (src->Stream != 0xFF); src++) { // element D3DVERTEXELEMENT9_GL *elem = &decl9->m_elements[ decl9->m_elemCount++ ]; // copy the D3D decl wholesale. elem->m_dxdecl = *src; // latch current offset in this stream. elem->m_gldecl.m_offset = streamOffsets[ elem->m_dxdecl.Stream ]; // figure out size of this attr and move the cursor // if cursor was on zero, bump the active stream count if (!streamOffsets[ elem->m_dxdecl.Stream ]) streamCount++; int bytes = 0; switch( elem->m_dxdecl.Type ) { case D3DDECLTYPE_FLOAT1: elem->m_gldecl.m_nCompCount = 1; elem->m_gldecl.m_datatype = GL_FLOAT; elem->m_gldecl.m_normalized=0; bytes = 4; break; case D3DDECLTYPE_FLOAT2: elem->m_gldecl.m_nCompCount = 2; elem->m_gldecl.m_datatype = GL_FLOAT; elem->m_gldecl.m_normalized=0; bytes = 8; break; //case D3DVSDT_FLOAT3: case D3DDECLTYPE_FLOAT3: elem->m_gldecl.m_nCompCount = 3; elem->m_gldecl.m_datatype = GL_FLOAT; elem->m_gldecl.m_normalized=0; bytes = 12; break; //case D3DVSDT_FLOAT4: case D3DDECLTYPE_FLOAT4: elem->m_gldecl.m_nCompCount = 4; elem->m_gldecl.m_datatype = GL_FLOAT; elem->m_gldecl.m_normalized=0; bytes = 16; break; // case D3DVSDT_UBYTE4: case D3DDECLTYPE_D3DCOLOR: case D3DDECLTYPE_UBYTE4: case D3DDECLTYPE_UBYTE4N: // Force this path since we're on 10.6.2 and can't rely on EXT_vertex_array_bgra if ( 1 ) { // pass 4 UB's but we know this is out of order compared to D3DCOLOR data elem->m_gldecl.m_nCompCount = 4; elem->m_gldecl.m_datatype = GL_UNSIGNED_BYTE; } else { // pass a GL BGRA color courtesy of http://www.opengl.org/registry/specs/ARB/vertex_array_bgra.txt elem->m_gldecl.m_nCompCount = GL_BGRA; elem->m_gldecl.m_datatype = GL_UNSIGNED_BYTE; } elem->m_gldecl.m_normalized = ( (elem->m_dxdecl.Type == D3DDECLTYPE_D3DCOLOR) || (elem->m_dxdecl.Type == D3DDECLTYPE_UBYTE4N) ); bytes = 4; break; case D3DDECLTYPE_SHORT2: // pass 2 US's but we know this is out of order compared to D3DCOLOR data elem->m_gldecl.m_nCompCount = 2; elem->m_gldecl.m_datatype = GL_SHORT; elem->m_gldecl.m_normalized = 0; bytes = 4; break; default: DXABSTRACT_BREAK_ON_ERROR(); return D3DERR_INVALIDCALL; break; /* typedef enum _D3DDECLTYPE { D3DDECLTYPE_FLOAT1 = 0, // 1D float expanded to (value, 0., 0., 1.) D3DDECLTYPE_FLOAT2 = 1, // 2D float expanded to (value, value, 0., 1.) D3DDECLTYPE_FLOAT3 = 2, // 3D float expanded to (value, value, value, 1.) D3DDECLTYPE_FLOAT4 = 3, // 4D float D3DDECLTYPE_D3DCOLOR = 4, // 4D packed unsigned bytes mapped to 0. to 1. range // Input is in D3DCOLOR format (ARGB) expanded to (R, G, B, A) D3DDECLTYPE_UBYTE4 = 5, // 4D unsigned byte D3DDECLTYPE_SHORT2 = 6, // 2D signed short expanded to (value, value, 0., 1.) D3DDECLTYPE_SHORT4 = 7, // 4D signed short // The following types are valid only with vertex shaders >= 2.0 D3DDECLTYPE_UBYTE4N = 8, // Each of 4 bytes is normalized by dividing to 255.0 D3DDECLTYPE_SHORT2N = 9, // 2D signed short normalized (v[0]/32767.0,v[1]/32767.0,0,1) D3DDECLTYPE_SHORT4N = 10, // 4D signed short normalized (v[0]/32767.0,v[1]/32767.0,v[2]/32767.0,v[3]/32767.0) D3DDECLTYPE_USHORT2N = 11, // 2D unsigned short normalized (v[0]/65535.0,v[1]/65535.0,0,1) D3DDECLTYPE_USHORT4N = 12, // 4D unsigned short normalized (v[0]/65535.0,v[1]/65535.0,v[2]/65535.0,v[3]/65535.0) D3DDECLTYPE_UDEC3 = 13, // 3D unsigned 10 10 10 format expanded to (value, value, value, 1) D3DDECLTYPE_DEC3N = 14, // 3D signed 10 10 10 format normalized and expanded to (v[0]/511.0, v[1]/511.0, v[2]/511.0, 1) D3DDECLTYPE_FLOAT16_2 = 15, // Two 16-bit floating point values, expanded to (value, value, 0, 1) D3DDECLTYPE_FLOAT16_4 = 16, // Four 16-bit floating point values D3DDECLTYPE_UNUSED = 17, // When the type field in a decl is unused. } D3DDECLTYPE; */ } // write the offset and move the cursor elem->m_gldecl.m_offset = streamOffsets[elem->m_dxdecl.Stream]; streamOffsets[ elem->m_dxdecl.Stream ] += bytes; // cannot write m_stride yet, so zero it elem->m_gldecl.m_stride = 0; elem->m_gldecl.m_pBuffer = NULL; // must be filled in at draw time.. // elem count was already bumped. // update attrib map attribMap[ attribMapIndex++ ] = (elem->m_dxdecl.Usage << 4) | (elem->m_dxdecl.UsageIndex); } // the loop is done, we now know how many active streams there are, how many atribs are active in the declaration, // and how big each one is in terms of stride. // all that is left is to go back and write the strides - the stride comes from the stream offset cursors accumulated earlier. for( uint j=0; j< decl9->m_elemCount; j++) { D3DVERTEXELEMENT9_GL *elem = &decl9->m_elements[ j ]; elem->m_gldecl.m_stride = streamOffsets[ elem->m_dxdecl.Stream ]; } memset( decl9->m_VertexAttribDescToStreamIndex, 0xFF, sizeof( decl9->m_VertexAttribDescToStreamIndex ) ); D3DVERTEXELEMENT9_GL *pDeclElem = decl9->m_elements; for( uint j = 0; j < decl9->m_elemCount; j++, pDeclElem++) { uint nPackedVertexAttribDesc = ( pDeclElem->m_dxdecl.Usage << 4 ) | pDeclElem->m_dxdecl.UsageIndex; if ( nPackedVertexAttribDesc == 0xBB ) { // 0xBB is a reserved packed vertex attrib value - shouldn't encounter in practice DXABSTRACT_BREAK_ON_ERROR(); } decl9->m_VertexAttribDescToStreamIndex[ nPackedVertexAttribDesc ] = j; } *ppDecl = decl9; return S_OK; } IDirect3DVertexDeclaration9::~IDirect3DVertexDeclaration9() { GL_BATCH_PERF_CALL_TIMER; GL_PUBLIC_ENTRYPOINT_CHECKS( m_device ); GLMPRINTF(("-A- ~IDirect3DVertexDeclaration9 signpost")); m_device->ReleasedVertexDeclaration( this ); } HRESULT IDirect3DDevice9::SetVertexDeclarationNonInline(IDirect3DVertexDeclaration9* pDecl) { GL_BATCH_PERF_CALL_TIMER; GL_PUBLIC_ENTRYPOINT_CHECKS( this ); m_pVertDecl = pDecl; return S_OK; } HRESULT IDirect3DDevice9::SetFVF(DWORD FVF) { GL_BATCH_PERF_CALL_TIMER; GL_PUBLIC_ENTRYPOINT_CHECKS( this ); DXABSTRACT_BREAK_ON_ERROR(); return S_OK; } HRESULT IDirect3DDevice9::GetFVF(DWORD* pFVF) { GL_BATCH_PERF_CALL_TIMER; GL_PUBLIC_ENTRYPOINT_CHECKS( this ); DXABSTRACT_BREAK_ON_ERROR(); return S_OK; } #ifdef OSX #pragma mark ----- Vertex Buffers and Streams - (IDirect3DDevice9) #pragma mark ----- Create function moved to be adjacent to other buffer methods #endif HRESULT IDirect3DDevice9::SetStreamSourceNonInline(UINT StreamNumber,IDirect3DVertexBuffer9* pStreamData,UINT OffsetInBytes,UINT Stride) { GL_BATCH_PERF_CALL_TIMER; GL_PUBLIC_ENTRYPOINT_CHECKS( this ); Assert( StreamNumber < D3D_MAX_STREAMS ); Assert( ( Stride & 3 ) == 0 ); // we support non-DWORD aligned strides, but on some drivers (like AMD's) perf goes off a cliff // perfectly legal to see a vertex buffer of NULL get passed in here. // so we need an array to track these. // OK, we are being given the stride, we don't need to calc it.. GLMPRINTF(("-X- IDirect3DDevice9::SetStreamSource setting stream #%d to D3D buf %p (GL name %d); offset %d, stride %d", StreamNumber, pStreamData, (pStreamData) ? pStreamData->m_vtxBuffer->m_nHandle: -1, OffsetInBytes, Stride)); if ( !pStreamData ) { OffsetInBytes = 0; Stride = 0; m_vtx_buffers[ StreamNumber ] = m_pDummy_vtx_buffer; } else { // We do not support strides of 0 Assert( Stride > 0 ); m_vtx_buffers[ StreamNumber ] = pStreamData->m_vtxBuffer; } m_streams[ StreamNumber ].m_vtxBuffer = pStreamData; m_streams[ StreamNumber ].m_offset = OffsetInBytes; m_streams[ StreamNumber ].m_stride = Stride; return S_OK; } #ifdef OSX #pragma mark ----- Index Buffers - (IDirect3DDevice9) #pragma mark ----- Creatue function relocated to be adjacent to the rest of the index buffer methods #endif HRESULT IDirect3DDevice9::SetIndicesNonInline(IDirect3DIndexBuffer9* pIndexData) { GL_BATCH_PERF_CALL_TIMER; GL_PUBLIC_ENTRYPOINT_CHECKS( this ); // just latch it. m_indices.m_idxBuffer = pIndexData; return S_OK; } #ifdef OSX #pragma mark ----- Release Handlers - (IDirect3DDevice9) #endif void IDirect3DDevice9::ReleasedVertexDeclaration( IDirect3DVertexDeclaration9 *pDecl ) { m_ctx->ClearCurAttribs(); Assert( m_ObjectStats.m_nTotalVertexDecls >= 1 ); m_ObjectStats.m_nTotalVertexDecls--; } void IDirect3DDevice9::ReleasedTexture( IDirect3DBaseTexture9 *baseTex ) { GL_BATCH_PERF_CALL_TIMER; TOGL_NULL_DEVICE_CHECK_RET_VOID; // see if this texture is referenced in any of the texture units and scrub it if so. for( int i=0; i< GLM_SAMPLER_COUNT; i++) { if (m_textures[i] == baseTex) { m_textures[i] = NULL; m_ctx->SetSamplerTex( i, NULL ); // texture sets go straight through to GLM, no dirty bit } } } void IDirect3DDevice9::ReleasedCGLMTex( CGLMTex *pTex) { GL_BATCH_PERF_CALL_TIMER; TOGL_NULL_DEVICE_CHECK_RET_VOID; ScrubFBOMap( pTex ); if ( pTex->m_layout ) { if ( pTex->m_layout->m_key.m_texFlags & kGLMTexRenderable ) { Assert( m_ObjectStats.m_nTotalRenderTargets >= 1 ); m_ObjectStats.m_nTotalRenderTargets--; } } } void IDirect3DDevice9::ReleasedSurface( IDirect3DSurface9 *pSurface ) { for( int i = 0; i < 4; i++ ) { if ( m_pRenderTargets[i] == pSurface ) { // this was a surprise release... scrub it m_pRenderTargets[i] = NULL; m_bFBODirty = true; GLMPRINTF(( "-A- Scrubbed pSurface %08x from m_pRenderTargets[%d]", pSurface, i )); } } if ( m_pDepthStencil == pSurface ) { m_pDepthStencil = NULL; m_bFBODirty = true; GLMPRINTF(( "-A- Scrubbed pSurface %08x from m_pDepthStencil", pSurface )); } if ( m_pDefaultColorSurface == pSurface ) { m_pDefaultColorSurface = NULL; GLMPRINTF(( "-A- Scrubbed pSurface %08x from m_pDefaultColorSurface", pSurface )); } if ( m_pDefaultDepthStencilSurface == pSurface ) { m_pDefaultDepthStencilSurface = NULL; GLMPRINTF(( "-A- Scrubbed pSurface %08x from m_pDefaultDepthStencilSurface", pSurface )); } Assert( m_ObjectStats.m_nTotalSurfaces >= 1 ); m_ObjectStats.m_nTotalSurfaces--; } void IDirect3DDevice9::ReleasedPixelShader( IDirect3DPixelShader9 *pixelShader ) { if ( m_pixelShader == pixelShader ) { m_pixelShader = NULL; GLMPRINTF(( "-A- Scrubbed pixel shader %08x from m_pixelShader", pixelShader )); } m_ctx->ReleasedShader(); Assert( m_ObjectStats.m_nTotalPixelShaders >= 1 ); m_ObjectStats.m_nTotalPixelShaders--; } void IDirect3DDevice9::ReleasedVertexShader( IDirect3DVertexShader9 *vertexShader ) { if ( m_vertexShader == vertexShader ) { m_vertexShader = NULL; GLMPRINTF(( "-A- Scrubbed vertex shader %08x from m_vertexShader", vertexShader )); } m_ctx->ClearCurAttribs(); m_ctx->ReleasedShader(); Assert( m_ObjectStats.m_nTotalVertexShaders >= 1 ); m_ObjectStats.m_nTotalVertexShaders--; } void IDirect3DDevice9::ReleasedVertexBuffer( IDirect3DVertexBuffer9 *vertexBuffer ) { for (int i=0; i< D3D_MAX_STREAMS; i++) { if ( m_streams[i].m_vtxBuffer == vertexBuffer ) { m_streams[i].m_vtxBuffer = NULL; m_vtx_buffers[i] = m_pDummy_vtx_buffer; GLMPRINTF(( "-A- Scrubbed vertex buffer %08x from m_streams[%d]", vertexBuffer, i )); } } m_ctx->ClearCurAttribs(); Assert( m_ObjectStats.m_nTotalVertexBuffers >= 1 ); m_ObjectStats.m_nTotalVertexBuffers--; } void IDirect3DDevice9::ReleasedIndexBuffer( IDirect3DIndexBuffer9 *indexBuffer ) { if ( m_indices.m_idxBuffer == indexBuffer ) { m_indices.m_idxBuffer = NULL; GLMPRINTF(( "-A- Scrubbed index buffer %08x from m_indices", indexBuffer )); } Assert( m_ObjectStats.m_nTotalIndexBuffers >= 1 ); m_ObjectStats.m_nTotalIndexBuffers--; } void IDirect3DDevice9::ReleasedQuery( IDirect3DQuery9 *query ) { Assert( m_ObjectStats.m_nTotalQueries >= 1 ); m_ObjectStats.m_nTotalQueries--; } #ifdef OSX #pragma mark ----- Queries - (IDirect3DDevice9) #endif // note that detection of whether queries are supported is done by trying to create one. // so for GL, be observant here of whether we have that capability or not. // pretty much have this everywhere but i950. HRESULT IDirect3DDevice9::CreateQuery(D3DQUERYTYPE Type,IDirect3DQuery9** ppQuery) { GL_BATCH_PERF_CALL_TIMER; GL_PUBLIC_ENTRYPOINT_CHECKS( this ); if (m_ctx->Caps().m_hasOcclusionQuery) { m_ObjectStats.m_nTotalQueries++; IDirect3DQuery9 *newquery = new IDirect3DQuery9; newquery->m_device = this; newquery->m_type = Type; newquery->m_ctx = m_ctx; newquery->m_nIssueStartThreadID = 0; newquery->m_nIssueEndThreadID = 0; newquery->m_nIssueStartDrawCallIndex = 0; newquery->m_nIssueEndDrawCallIndex = 0; GLMQueryParams params; memset( ¶ms, 0, sizeof(params) ); //bool known = false; switch(newquery->m_type) { case D3DQUERYTYPE_OCCLUSION: /* D3DISSUE_BEGIN, D3DISSUE_END */ // create an occlusion query params.m_type = EOcclusion; break; case D3DQUERYTYPE_EVENT: /* D3DISSUE_END */ params.m_type = EFence; break; case D3DQUERYTYPE_RESOURCEMANAGER: /* D3DISSUE_END */ case D3DQUERYTYPE_TIMESTAMP: /* D3DISSUE_END */ case D3DQUERYTYPE_TIMESTAMPFREQ: /* D3DISSUE_END */ case D3DQUERYTYPE_INTERFACETIMINGS: /* D3DISSUE_BEGIN, D3DISSUE_END */ case D3DQUERYTYPE_PIXELTIMINGS: /* D3DISSUE_BEGIN, D3DISSUE_END */ case D3DQUERYTYPE_CACHEUTILIZATION: /* D3DISSUE_BEGIN, D3DISSUE_END */ Assert( !"Un-implemented query type" ); break; default: Assert( !"Unknown query type" ); break; } newquery->m_query = m_ctx->NewQuery( ¶ms ); *ppQuery = newquery; return S_OK; } else { *ppQuery = NULL; return -1; // failed } } IDirect3DQuery9::~IDirect3DQuery9() { GL_BATCH_PERF_CALL_TIMER; GL_PUBLIC_ENTRYPOINT_CHECKS( m_device ); GLMPRINTF((">-A- ~IDirect3DQuery9")); if (m_device) { m_device->ReleasedQuery( this ); if (m_query) { GLMPRINTF((">-A- ~IDirect3DQuery9 freeing m_query")); m_query->m_ctx->DelQuery( m_query ); m_query = NULL; GLMPRINTF(("<-A- ~IDirect3DQuery9 freeing m_query done")); } m_device = NULL; } GLMPRINTF(("<-A- ~IDirect3DQuery9")); } #ifdef OSX #pragma mark ----- Render States - (IDirect3DDevice9) #endif #define D3DRS_VALUE_LIMIT 210 struct D3D_RSINFO { int m_class; D3DRENDERSTATETYPE m_state; DWORD m_defval; // m_class runs 0-3. // 3 = must implement - fully general - "obey" // 2 = implement setup to the default value (it has a GL effect but does not change later) "obey once" // 1 = "fake implement" setup to the default value no GL effect, debug break if anything but default value comes through - "ignore" // 0 = game never ever sets this one, break if someone even tries. "complain" }; D3D_RSINFO g_D3DRS_INFO_unpacked[ D3DRS_VALUE_LIMIT+1 ]; #ifdef D3D_RSI #error macro collision... rename this #else #define D3D_RSI(nclass,nstate,ndefval) { nclass, nstate, ndefval } #endif // FP conversions to hex courtesy of http://babbage.cs.qc.cuny.edu/IEEE-754/Decimal.html #define CONST_DZERO 0x00000000 #define CONST_DONE 0x3F800000 #define CONST_D64 0x42800000 #define DONT_KNOW_YET 0x31415926 // see http://www.toymaker.info/Games/html/render_states.html D3D_RSINFO g_D3DRS_INFO_packed[] = { // these do not have to be in any particular order. they get unpacked into the empty array above for direct indexing. D3D_RSI( 3, D3DRS_ZENABLE, DONT_KNOW_YET ), // enable Z test (or W buffering) D3D_RSI( 3, D3DRS_ZWRITEENABLE, DONT_KNOW_YET ), // enable Z write D3D_RSI( 3, D3DRS_ZFUNC, DONT_KNOW_YET ), // select Z func D3D_RSI( 3, D3DRS_COLORWRITEENABLE, TRUE ), // see transitiontable.cpp "APPLY_RENDER_STATE_FUNC( D3DRS_COLORWRITEENABLE, ColorWriteEnable )" D3D_RSI( 3, D3DRS_CULLMODE, D3DCULL_CCW ), // backface cull control D3D_RSI( 3, D3DRS_ALPHABLENDENABLE, DONT_KNOW_YET ), // ->CTransitionTable::ApplySeparateAlphaBlend and ApplyAlphaBlend D3D_RSI( 3, D3DRS_BLENDOP, D3DBLENDOP_ADD ), D3D_RSI( 3, D3DRS_SRCBLEND, DONT_KNOW_YET ), D3D_RSI( 3, D3DRS_DESTBLEND, DONT_KNOW_YET ), D3D_RSI( 1, D3DRS_SEPARATEALPHABLENDENABLE, FALSE ), // hit in CTransitionTable::ApplySeparateAlphaBlend D3D_RSI( 1, D3DRS_SRCBLENDALPHA, D3DBLEND_ONE ), // going to demote these to class 1 until I figure out if they are implementable D3D_RSI( 1, D3DRS_DESTBLENDALPHA, D3DBLEND_ZERO ), D3D_RSI( 1, D3DRS_BLENDOPALPHA, D3DBLENDOP_ADD ), // what is the deal with alpha test... looks like it is inited to off. D3D_RSI( 3, D3DRS_ALPHATESTENABLE, 0 ), D3D_RSI( 3, D3DRS_ALPHAREF, 0 ), D3D_RSI( 3, D3DRS_ALPHAFUNC, D3DCMP_GREATEREQUAL ), D3D_RSI( 3, D3DRS_STENCILENABLE, FALSE ), D3D_RSI( 3, D3DRS_STENCILFAIL, D3DSTENCILOP_KEEP ), D3D_RSI( 3, D3DRS_STENCILZFAIL, D3DSTENCILOP_KEEP ), D3D_RSI( 3, D3DRS_STENCILPASS, D3DSTENCILOP_KEEP ), D3D_RSI( 3, D3DRS_STENCILFUNC, D3DCMP_ALWAYS ), D3D_RSI( 3, D3DRS_STENCILREF, 0 ), D3D_RSI( 3, D3DRS_STENCILMASK, 0xFFFFFFFF ), D3D_RSI( 3, D3DRS_STENCILWRITEMASK, 0xFFFFFFFF ), D3D_RSI( 3, D3DRS_TWOSIDEDSTENCILMODE, FALSE ), D3D_RSI( 3, D3DRS_CCW_STENCILFAIL, D3DSTENCILOP_KEEP ), D3D_RSI( 3, D3DRS_CCW_STENCILZFAIL, D3DSTENCILOP_KEEP ), D3D_RSI( 3, D3DRS_CCW_STENCILPASS, D3DSTENCILOP_KEEP ), D3D_RSI( 3, D3DRS_CCW_STENCILFUNC, D3DCMP_ALWAYS ), D3D_RSI( 3, D3DRS_FOGENABLE, FALSE ), // see CShaderAPIDx8::FogMode and friends - be ready to do the ARB fog linear option madness D3D_RSI( 3, D3DRS_FOGCOLOR, 0 ), D3D_RSI( 3, D3DRS_FOGTABLEMODE, D3DFOG_NONE ), D3D_RSI( 3, D3DRS_FOGSTART, CONST_DZERO ), D3D_RSI( 3, D3DRS_FOGEND, CONST_DONE ), D3D_RSI( 3, D3DRS_FOGDENSITY, CONST_DZERO ), D3D_RSI( 3, D3DRS_RANGEFOGENABLE, FALSE ), D3D_RSI( 3, D3DRS_FOGVERTEXMODE, D3DFOG_NONE ), // watch out for CShaderAPIDx8::CommitPerPassFogMode.... D3D_RSI( 3, D3DRS_MULTISAMPLEANTIALIAS, TRUE ), D3D_RSI( 3, D3DRS_MULTISAMPLEMASK, 0xFFFFFFFF ), D3D_RSI( 3, D3DRS_SCISSORTESTENABLE, FALSE ), // heed IDirect3DDevice9::SetScissorRect D3D_RSI( 3, D3DRS_DEPTHBIAS, CONST_DZERO ), D3D_RSI( 3, D3DRS_SLOPESCALEDEPTHBIAS, CONST_DZERO ), D3D_RSI( 3, D3DRS_COLORWRITEENABLE1, 0x0000000f ), D3D_RSI( 3, D3DRS_COLORWRITEENABLE2, 0x0000000f ), D3D_RSI( 3, D3DRS_COLORWRITEENABLE3, 0x0000000f ), D3D_RSI( 3, D3DRS_SRGBWRITEENABLE, 0 ), // heeded but ignored.. D3D_RSI( 2, D3DRS_CLIPPING, TRUE ), // um, yeah, clipping is enabled (?) D3D_RSI( 3, D3DRS_CLIPPLANEENABLE, 0 ), // mask 1<m_class >= 0; packed++ ) { if ( (packed->m_state <0) || (packed->m_state >= D3DRS_VALUE_LIMIT) ) { // bad DXABSTRACT_BREAK_ON_ERROR(); } else { // dispatch it to the unpacked array g_D3DRS_INFO_unpacked[ packed->m_state ] = *packed; } } } // convenience functions #ifdef OSX #pragma mark ----- Sampler States - (IDirect3DDevice9) #endif void IDirect3DDevice9::FlushClipPlaneEquation() { for( int x=0; xCaps().m_hasNativeClipVertexMode ) { // hacked coeffs = { src->x, -src->y, 0.5f * src->z, src->w + (0.5f * src->z) }; // Antonio's trick - so we can use gl_Position as the clippee, not gl_ClipVertex. GLClipPlaneEquation_t *equ = &gl.m_ClipPlaneEquation[x]; ///////////////// temp1 temp1.x = equ->x; temp1.y = equ->y * -1.0; temp1.z = equ->z * 0.5; temp1.w = equ->w + (equ->z * 0.5); //////////////// temp2 VMatrix mat1( 1, 0, 0, 0, 0, -1, 0, 0, 0, 0, 2, -1, 0, 0, 0, 1 ); //mat1 = mat1.Transpose(); VMatrix mat2; bool success = mat1.InverseGeneral( mat2 ); if (success) { VMatrix mat3; mat3 = mat2.Transpose(); VPlane origPlane( Vector( equ->x, equ->y, equ->z ), equ->w ); VPlane newPlane; newPlane = mat3 * origPlane /* * mat3 */; VPlane finalPlane = newPlane; temp2.x = newPlane.m_Normal.x; temp2.y = newPlane.m_Normal.y; temp2.z = newPlane.m_Normal.z; temp2.w = newPlane.m_Dist; } else { temp2.x = 0; temp2.y = 0; temp2.z = 0; temp2.w = 0; } } else { temp1 = temp2 = gl.m_ClipPlaneEquation[x]; } if (1) //GLMKnob("caps-key",NULL)==0.0) { m_ctx->WriteClipPlaneEquation( &temp1, x ); // no caps lock = Antonio or classic /* if (x<1) { GLMPRINTF(( " plane %d √vers1[ %5.2f %5.2f %5.2f %5.2f ] vers2[ %5.2f %5.2f %5.2f %5.2f ]", x, temp1.x,temp1.y,temp1.z,temp1.w, temp2.x,temp2.y,temp2.z,temp2.w )); } */ } else { m_ctx->WriteClipPlaneEquation( &temp2, x ); // caps = our way or classic /* if (x<1) { GLMPRINTF(( " plane %d vers1[ %5.2f %5.2f %5.2f %5.2f ] √vers2[ %5.2f %5.2f %5.2f %5.2f ]", x, temp1.x,temp1.y,temp1.z,temp1.w, temp2.x,temp2.y,temp2.z,temp2.w )); } */ } } } void IDirect3DDevice9::InitStates() { m_ctx->m_AlphaTestEnable.Read( &gl.m_AlphaTestEnable, 0 ); m_ctx->m_AlphaTestFunc.Read( &gl.m_AlphaTestFunc, 0 ); m_ctx->m_CullFaceEnable.Read( &gl.m_CullFaceEnable, 0 ); m_ctx->m_DepthBias.Read( &gl.m_DepthBias, 0 ); m_ctx->m_ScissorEnable.Read( &gl.m_ScissorEnable, 0 ); m_ctx->m_ScissorBox.Read( &gl.m_ScissorBox, 0 ); m_ctx->m_ViewportBox.Read( &gl.m_ViewportBox, 0 ); m_ctx->m_ViewportDepthRange.Read( &gl.m_ViewportDepthRange, 0 ); for( int x=0; xm_ClipPlaneEnable.ReadIndex( &gl.m_ClipPlaneEnable[x], x, 0 ); m_ctx->m_PolygonMode.Read( &gl.m_PolygonMode, 0 ); m_ctx->m_CullFrontFace.Read( &gl.m_CullFrontFace, 0 ); m_ctx->m_AlphaToCoverageEnable.Read( &gl.m_AlphaToCoverageEnable, 0 ); m_ctx->m_BlendEquation.Read( &gl.m_BlendEquation, 0 ); m_ctx->m_BlendColor.Read( &gl.m_BlendColor, 0 ); for( int x=0; xm_ClipPlaneEquation.ReadIndex( &gl.m_ClipPlaneEquation[x], x, 0 ); m_ctx->m_ColorMaskSingle.Read( &gl.m_ColorMaskSingle, 0 ); m_ctx->m_BlendEnable.Read( &gl.m_BlendEnable, 0 ); m_ctx->m_BlendFactor.Read( &gl.m_BlendFactor, 0 ); m_ctx->m_BlendEnableSRGB.Read( &gl.m_BlendEnableSRGB, 0 ); m_ctx->m_DepthTestEnable.Read( &gl.m_DepthTestEnable, 0 ); m_ctx->m_DepthFunc.Read( &gl.m_DepthFunc, 0 ); m_ctx->m_DepthMask.Read( &gl.m_DepthMask, 0 ); m_ctx->m_StencilTestEnable.Read( &gl.m_StencilTestEnable, 0 ); m_ctx->m_StencilFunc.Read( &gl.m_StencilFunc, 0 ); m_ctx->m_StencilOp.ReadIndex( &gl.m_StencilOp, 0, 0 ); m_ctx->m_StencilOp.ReadIndex( &gl.m_StencilOp, 1, 0 ); m_ctx->m_StencilWriteMask.Read( &gl.m_StencilWriteMask, 0 ); m_ctx->m_ClearColor.Read( &gl.m_ClearColor, 0 ); m_ctx->m_ClearDepth.Read( &gl.m_ClearDepth, 0 ); m_ctx->m_ClearStencil.Read( &gl.m_ClearStencil, 0 ); } void IDirect3DDevice9::FullFlushStates() { m_ctx->WriteAlphaTestEnable( &gl.m_AlphaTestEnable ); m_ctx->WriteAlphaTestFunc( &gl.m_AlphaTestFunc ); m_ctx->WriteCullFaceEnable( &gl.m_CullFaceEnable ); m_ctx->WriteDepthBias( &gl.m_DepthBias ); m_ctx->WriteScissorEnable( &gl.m_ScissorEnable ); m_ctx->WriteScissorBox( &gl.m_ScissorBox ); m_ctx->WriteViewportBox( &gl.m_ViewportBox ); m_ctx->WriteViewportDepthRange( &gl.m_ViewportDepthRange ); for( int x=0; xWriteClipPlaneEnable( &gl.m_ClipPlaneEnable[x], x ); m_ctx->WritePolygonMode( &gl.m_PolygonMode ); m_ctx->WriteCullFrontFace( &gl.m_CullFrontFace ); m_ctx->WriteAlphaToCoverageEnable( &gl.m_AlphaToCoverageEnable ); m_ctx->WriteBlendEquation( &gl.m_BlendEquation ); m_ctx->WriteBlendColor( &gl.m_BlendColor ); FlushClipPlaneEquation(); m_ctx->WriteColorMaskSingle( &gl.m_ColorMaskSingle ); m_ctx->WriteBlendEnable( &gl.m_BlendEnable ); m_ctx->WriteBlendFactor( &gl.m_BlendFactor ); m_ctx->WriteBlendEnableSRGB( &gl.m_BlendEnableSRGB ); m_ctx->WriteDepthTestEnable( &gl.m_DepthTestEnable ); m_ctx->WriteDepthFunc( &gl.m_DepthFunc ); m_ctx->WriteDepthMask( &gl.m_DepthMask ); m_ctx->WriteStencilTestEnable( &gl.m_StencilTestEnable ); m_ctx->WriteStencilFunc( &gl.m_StencilFunc ); m_ctx->WriteStencilOp( &gl.m_StencilOp,0 ); m_ctx->WriteStencilOp( &gl.m_StencilOp,1 ); // ********* need to recheck this m_ctx->WriteStencilWriteMask( &gl.m_StencilWriteMask ); m_ctx->WriteClearColor( &gl.m_ClearColor ); m_ctx->WriteClearDepth( &gl.m_ClearDepth ); m_ctx->WriteClearStencil( &gl.m_ClearStencil ); } HRESULT IDirect3DDevice9::DrawPrimitive(D3DPRIMITIVETYPE PrimitiveType,UINT StartVertex,UINT PrimitiveCount) { GL_BATCH_PERF_CALL_TIMER; GL_PUBLIC_ENTRYPOINT_CHECKS( this ); DXABSTRACT_BREAK_ON_ERROR(); return S_OK; } // 7LS - TODO #ifndef DX_TO_GL_ABSTRACTION HRESULT IDirect3DDevice9::DrawPrimitiveUP(D3DPRIMITIVETYPE PrimitiveType,UINT PrimitiveCountx,CONST void* pVertexStreamZeroData,UINT VertexStreamZeroStride) { // 7LS GL_BATCH_PERF_CALL_TIMER; GL_PUBLIC_ENTRYPOINT_CHECKS( this ); DXABSTRACT_BREAK_ON_ERROR(); return S_OK; } #endif // Type // [in] Member of the D3DPRIMITIVETYPE enumerated type, describing the type of primitive to render. D3DPT_POINTLIST is not supported with this method. See Remarks. // BaseVertexIndex // [in] Offset from the start of the vertex buffer to the first vertex. See Scenario 4. // MinIndex // [in] Minimum vertex index for vertices used during this call. This is a zero based index relative to BaseVertexIndex. // NumVertices // [in] Number of vertices used during this call. The first vertex is located at index: BaseVertexIndex + MinIndex. // StartIndex // [in] Index of the first index to use when accessing the index buffer. // PrimitiveCount // [in] Number of primitives to render. The number of vertices used is a function of the primitive count and the primitive type. The maximum number of primitives allowed is determined by checking the MaxPrimitiveCount member of the D3DCAPS9 structure. // BE VERY CAREFUL what you do in this function. It's extremely hot, and calling the wrong GL API's in here will crush perf. on NVidia threaded drivers. #ifndef OSX HRESULT IDirect3DDevice9::DrawIndexedPrimitive( D3DPRIMITIVETYPE Type, INT BaseVertexIndex, UINT MinVertexIndex, UINT NumVertices, UINT startIndex, UINT primCount ) { tmZone( TELEMETRY_LEVEL2, TMZF_NONE, "%s", __FUNCTION__ ); Assert( m_ctx->m_nCurOwnerThreadId == ThreadGetCurrentId() ); TOGL_NULL_DEVICE_CHECK; if ( m_bFBODirty ) { UpdateBoundFBO(); } g_nTotalDrawsOrClears++; #if GL_BATCH_PERF_ANALYSIS m_nTotalPrims += primCount; CFastTimer tm; CFlushDrawStatesStats& flushStats = m_ctx->m_FlushStats; tm.Start(); flushStats.Clear(); #endif #if GLMDEBUG if ( gl.m_FogEnable ) { GLMPRINTF(("-D- IDirect3DDevice9::DrawIndexedPrimitive is seeing enabled fog...")); } #endif if ( ( !m_indices.m_idxBuffer ) || ( !m_vertexShader ) ) goto draw_failed; { GL_BATCH_PERF_CALL_TIMER; m_ctx->FlushDrawStates( MinVertexIndex, MinVertexIndex + NumVertices - 1, BaseVertexIndex ); { #if !GL_TELEMETRY_ZONES && GL_BATCH_TELEMETRY_ZONES tmZone( TELEMETRY_LEVEL2, TMZF_NONE, "glDrawRangeElements %u", primCount ); #endif Assert( ( D3DPT_LINELIST == 2 ) && ( D3DPT_TRIANGLELIST == 4 ) && ( D3DPT_TRIANGLESTRIP == 5 ) ); static const struct prim_t { GLenum m_nType; uint m_nPrimMul; uint m_nPrimAdd; } s_primTypes[6] = { { 0, 0, 0 }, // 0 { 0, 0, 0 }, // 1 { GL_LINES, 2, 0 }, // 2 D3DPT_LINELIST { 0, 0, 0 }, // 3 { GL_TRIANGLES, 3, 0 }, // 4 D3DPT_TRIANGLELIST { GL_TRIANGLE_STRIP, 1, 2 } // 5 D3DPT_TRIANGLESTRIP }; if ( Type <= D3DPT_TRIANGLESTRIP ) { const prim_t& p = s_primTypes[Type]; Assert( p.m_nType ); Assert( NumVertices >= 1 ); m_ctx->DrawRangeElements( p.m_nType, (GLuint)MinVertexIndex, (GLuint)( MinVertexIndex + NumVertices - 1 ), (GLsizei)p.m_nPrimAdd + primCount * p.m_nPrimMul, (GLenum)GL_UNSIGNED_SHORT, (const GLvoid *)( startIndex * sizeof(short) ), BaseVertexIndex, m_indices.m_idxBuffer->m_idxBuffer ); } } } #if GL_BATCH_PERF_ANALYSIS if ( s_rdtsc_to_ms == 0.0f ) { TmU64 t0 = Plat_Rdtsc(); double d0 = Plat_FloatTime(); ThreadSleep( 1000 ); TmU64 t1 = Plat_Rdtsc(); double d1 = Plat_FloatTime(); s_rdtsc_to_ms = ( 1000.0f * ( d1 - d0 ) ) / ( t1 - t0 ); } #if GL_BATCH_PERF_ANALYSIS_WRITE_PNGS if ( m_pBatch_vis_bitmap && m_pBatch_vis_bitmap->is_valid() ) { double t = tm.GetDurationInProgress().GetMillisecondsF(); uint h = 1; if ( gl_batch_vis_y_scale.GetFloat() > 0.0f) { h = ceil( t / gl_batch_vis_y_scale.GetFloat() ); h = MAX(h, 1); } // Total time spent inside any and all our "D3D9" calls double flTotalD3DTime = g_nTotalD3DCycles * s_rdtsc_to_ms; m_pBatch_vis_bitmap->fill_box(0, m_nBatchVisY, (uint)(.5f + flTotalD3DTime / gl_batch_vis_abs_scale.GetFloat() * m_pBatch_vis_bitmap->width()), h, 150, 150, 150); // Total total spent processing just DrawIndexedPrimitive() for this batch. m_pBatch_vis_bitmap->fill_box(0, m_nBatchVisY, (uint)(.5f + t / gl_batch_vis_abs_scale.GetFloat() * m_pBatch_vis_bitmap->width()), h, 70, 70, 70); double flTotalGLMS = gGL->m_nTotalGLCycles * s_rdtsc_to_ms; // Total time spent inside of all OpenGL calls m_pBatch_vis_bitmap->additive_fill_box(0, m_nBatchVisY, (uint)(.5f + flTotalGLMS / gl_batch_vis_abs_scale.GetFloat() * m_pBatch_vis_bitmap->width()), h, 0, 0, 64); if (flushStats.m_nNewVS) m_pBatch_vis_bitmap->additive_fill_box(80-16, m_nBatchVisY, 8, h, 0, 110, 0); if (flushStats.m_nNewPS) m_pBatch_vis_bitmap->additive_fill_box(80-8, m_nBatchVisY, 8, h, 110, 0, 110); int lm = 80; m_pBatch_vis_bitmap->fill_box(lm+0+flushStats.m_nFirstVSConstant, m_nBatchVisY, flushStats.m_nNumVSConstants, h, 64, 255, 255); m_pBatch_vis_bitmap->fill_box(lm+64, m_nBatchVisY, flushStats.m_nNumVSBoneConstants, h, 255, 64, 64); m_pBatch_vis_bitmap->fill_box(lm+64+256+flushStats.m_nFirstPSConstant, m_nBatchVisY, flushStats.m_nNumPSConstants, h, 64, 64, 255); m_pBatch_vis_bitmap->fill_box(lm+64+256+32, m_nBatchVisY, flushStats.m_nNumChangedSamplers, h, 255, 255, 255); m_pBatch_vis_bitmap->fill_box(lm+64+256+32+16, m_nBatchVisY, flushStats.m_nNumSamplingParamsChanged, h, 92, 128, 255); if ( flushStats.m_nVertexBufferChanged ) m_pBatch_vis_bitmap->fill_box(lm+64+256+32+16+64, m_nBatchVisY, 16, h, 128, 128, 128); if ( flushStats.m_nIndexBufferChanged ) m_pBatch_vis_bitmap->fill_box(lm+64+256+32+16+64+16, m_nBatchVisY, 16, h, 128, 128, 255); m_pBatch_vis_bitmap->fill_box(lm+64+256+32+16+64+16+16, m_nBatchVisY, ( ( g_nTotalVBLockBytes + g_nTotalIBLockBytes ) * 64 + 2047 ) / 2048, h, 120, 120, 120 ); m_pBatch_vis_bitmap->additive_fill_box(lm+64+256+32+16+64+16+16, m_nBatchVisY, ( g_nTotalVBLockBytes * 64 + 2047 ) / 2048, h, 120, 0, 0); m_nBatchVisY += h; } #endif m_nNumProgramChanges += ((flushStats.m_nNewVS + flushStats.m_nNewPS) != 0); m_flTotalD3DTime += g_nTotalD3DCycles * s_rdtsc_to_ms; m_nTotalD3DCalls += g_nTotalD3DCalls; g_nTotalD3DCycles = 0; g_nTotalD3DCalls = 0; m_flTotalGLTime += gGL->m_nTotalGLCycles * s_rdtsc_to_ms; m_nTotalGLCalls += gGL->m_nTotalGLCalls; gGL->m_nTotalGLCycles = 0; gGL->m_nTotalGLCalls = 0; g_nTotalVBLockBytes = 0; g_nTotalIBLockBytes = 0; #endif return S_OK; draw_failed: Assert( 0 ); return E_FAIL; } #else // OSX 10.6 support HRESULT IDirect3DDevice9::FlushIndexBindings( void ) { // push index buffer state m_ctx->SetIndexBuffer( m_indices.m_idxBuffer->m_idxBuffer ); return S_OK; } HRESULT IDirect3DDevice9::FlushVertexBindings( uint baseVertexIndex ) { // push vertex buffer state for the current vertex decl // in this variant we just walk the attrib map in the VS and do a pull for each one. // if we can't find a match in the vertex decl, we may fall back to the secret 'dummy' VBO that GLM maintains GLMVertexSetup setup; memset( &setup, 0, sizeof( setup ) ); IDirect3DVertexDeclaration9 *vxdecl = m_pVertDecl; unsigned char *vshAttribMap = m_vertexShader->m_vtxAttribMap; // this loop could be tightened if we knew the number of live entries in the shader attrib map. // which of course would be easy to do in the create shader function or even in the translator. GLMVertexAttributeDesc *dstAttr = setup.m_attrs; for( int i=0; i<16; i++,dstAttr++ ) { unsigned char vshattrib = vshAttribMap[ i ]; if (vshattrib != 0xBB) { // try to find the match in the decl. // idea: put some inverse table in the decl which could accelerate this search. D3DVERTEXELEMENT9_GL *elem = m_pVertDecl->m_elements; for( int j=0; j< m_pVertDecl->m_elemCount; j++,elem++) { // if it matches, install it, change vshattrib so the code below does not trigger, then end the loop if ( ((vshattrib>>4) == elem->m_dxdecl.Usage) && ((vshattrib & 0x0F) == elem->m_dxdecl.UsageIndex) ) { // targeting attribute #i in the setup with element data #j from the decl *dstAttr = elem->m_gldecl; // then fix buffer, stride, offset - note that we honor the base vertex index here by fiddling the offset int streamIndex = elem->m_dxdecl.Stream; dstAttr->m_pBuffer = m_streams[ streamIndex ].m_vtxBuffer->m_vtxBuffer; dstAttr->m_stride = m_streams[ streamIndex ].m_stride; dstAttr->m_offset += m_streams[ streamIndex ].m_offset + (baseVertexIndex * dstAttr->m_stride); // set mask setup.m_attrMask |= (1 << i); // end loop vshattrib = 0xBB; j = 999; } } // if vshattrib is not 0xBB here, that means we could not find a source in the decl for it if (vshattrib != 0xBB) { // fill out attr the same way as usual, we just pass NULL for the buffer and ask GLM to have mercy on us dstAttr->m_pBuffer = NULL; dstAttr->m_stride = 0; dstAttr->m_offset = 0; // only implement certain usages... if we haven't seen it before, stop. switch (vshattrib >> 4) // aka usage { case D3DDECLUSAGE_POSITION: case D3DDECLUSAGE_BLENDWEIGHT: case D3DDECLUSAGE_BLENDINDICES: Debugger(); break; case D3DDECLUSAGE_NORMAL: dstAttr->m_nCompCount = 3; dstAttr->m_datatype = GL_FLOAT; dstAttr->m_normalized = false; break; case D3DDECLUSAGE_PSIZE: Debugger(); break; case D3DDECLUSAGE_TEXCOORD: dstAttr->m_nCompCount = 3; dstAttr->m_datatype = GL_FLOAT; dstAttr->m_normalized = false; break; case D3DDECLUSAGE_TANGENT: case D3DDECLUSAGE_BINORMAL: case D3DDECLUSAGE_TESSFACTOR: case D3DDECLUSAGE_PLUGH: Debugger(); break; case D3DDECLUSAGE_COLOR: dstAttr->m_nCompCount = 4; dstAttr->m_datatype = GL_UNSIGNED_BYTE; dstAttr->m_normalized = true; break; case D3DDECLUSAGE_FOG: case D3DDECLUSAGE_DEPTH: case D3DDECLUSAGE_SAMPLE: Debugger(); break; } } } } // copy active program's vertex attrib map into the vert setup info memcpy(&setup.m_vtxAttribMap, m_vertexShader->m_vtxAttribMap, sizeof(m_vertexShader->m_vtxAttribMap)); m_ctx->SetVertexAttributes(&setup); return S_OK; } // OSX path offering support for 10.6 (we do not have support for glDrawRangeElementsBaseVertex) HRESULT IDirect3DDevice9::DrawIndexedPrimitive( D3DPRIMITIVETYPE Type,INT BaseVertexIndex,UINT MinVertexIndex,UINT NumVertices,UINT startIndex,UINT primCount ) { Assert( m_ctx->m_nCurOwnerThreadId == ThreadGetCurrentId() ); TOGL_NULL_DEVICE_CHECK; if ( m_bFBODirty ) { UpdateBoundFBO(); } g_nTotalDrawsOrClears++; #if GL_BATCH_PERF_ANALYSIS m_nTotalPrims += primCount; CFastTimer tm; CFlushDrawStatesStats& flushStats = m_ctx->m_FlushStats; tm.Start(); flushStats.Clear(); #endif #if GLMDEBUG if ( gl.m_FogEnable ) { GLMPRINTF(("-D- IDirect3DDevice9::DrawIndexedPrimitive is seeing enabled fog...")); } #endif if ( ( !m_indices.m_idxBuffer ) || ( !m_vertexShader ) ) goto draw_failed; this->FlushIndexBindings( ); this->FlushVertexBindings( BaseVertexIndex ); m_ctx->FlushDrawStates( MinVertexIndex, MinVertexIndex + NumVertices - 1, 0 ); if (gl.m_FogEnable) { GLMPRINTF(("-D- IDirect3DDevice9::DrawIndexedPrimitive is seeing enabled fog...")); } switch(Type) { case D3DPT_POINTLIST: Debugger(); break; case D3DPT_LINELIST: GLMPRINTF(("-X- IDirect3DDevice9::DrawIndexedPrimitive( D3DPT_LINELIST ) - ignored.")); // Debugger(); m_ctx->DrawRangeElements( (GLenum)GL_LINES, (GLuint)MinVertexIndex, (GLuint)(MinVertexIndex + NumVertices), (GLsizei)primCount*2, (GLenum)GL_UNSIGNED_SHORT, (const GLvoid *)(startIndex * sizeof(short)), m_indices.m_idxBuffer->m_idxBuffer ); break; case D3DPT_TRIANGLELIST: m_ctx->DrawRangeElements(GL_TRIANGLES, (GLuint)MinVertexIndex, (GLuint)(MinVertexIndex + NumVertices), (GLsizei)primCount*3, (GLenum)GL_UNSIGNED_SHORT, (const GLvoid *)(startIndex * sizeof(short)), m_indices.m_idxBuffer->m_idxBuffer ); break; case D3DPT_TRIANGLESTRIP: // enabled... Debugger(); m_ctx->DrawRangeElements(GL_TRIANGLE_STRIP, (GLuint)MinVertexIndex, (GLuint)(MinVertexIndex + NumVertices), (GLsizei)(2+primCount), (GLenum)GL_UNSIGNED_SHORT, (const GLvoid *)(startIndex * sizeof(short)), m_indices.m_idxBuffer->m_idxBuffer ); break; } return S_OK; draw_failed: Assert( 0 ); return E_FAIL; } #endif // #ifndef OSX HRESULT IDirect3DDevice9::DrawIndexedPrimitiveUP(D3DPRIMITIVETYPE PrimitiveType,UINT MinVertexIndex,UINT NumVertices,UINT PrimitiveCount,CONST void* pIndexData,D3DFORMAT IndexDataFormat,CONST void* pVertexStreamZeroData,UINT VertexStreamZeroStride) { GL_BATCH_PERF_CALL_TIMER; GL_PUBLIC_ENTRYPOINT_CHECKS( this ); DXABSTRACT_BREAK_ON_ERROR(); return S_OK; } BOOL IDirect3DDevice9::ShowCursor(BOOL bShow) { // FIXME NOP //DXABSTRACT_BREAK_ON_ERROR(); return TRUE; } void d3drect_to_glmbox( D3DRECT *src, GLScissorBox_t *dst ) { // to convert from a d3d rect to a GL rect you have to fix up the vertical axis, since D3D Y=0 is the top, but GL Y=0 is the bottom. // you can't fix it without knowing the height. dst->width = src->x2 - src->x1; dst->x = src->x1; // left edge dst->height = src->y2 - src->y1; dst->y = src->y1; // bottom edge - take large Y from d3d and subtract from surf height. } HRESULT IDirect3DDevice9::Clear(DWORD Count,CONST D3DRECT* pRects,DWORD Flags,D3DCOLOR Color,float Z,DWORD Stencil) { GL_BATCH_PERF_CALL_TIMER; if ( m_bFBODirty ) { UpdateBoundFBO(); } g_nTotalDrawsOrClears++; m_ctx->FlushDrawStatesNoShaders(); //debugging Color = (rand() | 0xFF0000FF) & 0xFF3F3FFF; if (!Count) { // run clear with no added rectangle m_ctx->Clear( (Flags&D3DCLEAR_TARGET)!=0, Color, (Flags&D3DCLEAR_ZBUFFER)!=0, Z, (Flags&D3DCLEAR_STENCIL)!=0, Stencil, NULL ); } else { GLScissorBox_t tempbox; // do the rects one by one and convert each one to GL form for( uint i=0; iClear( (Flags&D3DCLEAR_TARGET)!=0, Color, (Flags&D3DCLEAR_ZBUFFER)!=0, Z, (Flags&D3DCLEAR_STENCIL)!=0, Stencil, &tempbox ); } } return S_OK; } HRESULT IDirect3DDevice9::SetTransform(D3DTRANSFORMSTATETYPE State,CONST D3DMATRIX* pMatrix) { GL_BATCH_PERF_CALL_TIMER; GL_PUBLIC_ENTRYPOINT_CHECKS( this ); DXABSTRACT_BREAK_ON_ERROR(); return S_OK; } HRESULT IDirect3DDevice9::SetTextureStageState(DWORD Stage,D3DTEXTURESTAGESTATETYPE Type,DWORD Value) { GL_BATCH_PERF_CALL_TIMER; GL_PUBLIC_ENTRYPOINT_CHECKS( this ); DXABSTRACT_BREAK_ON_ERROR(); return S_OK; } HRESULT IDirect3DDevice9::ValidateDevice(DWORD* pNumPasses) { GL_BATCH_PERF_CALL_TIMER; GL_PUBLIC_ENTRYPOINT_CHECKS( this ); DXABSTRACT_BREAK_ON_ERROR(); return S_OK; } HRESULT IDirect3DDevice9::SetMaterial(CONST D3DMATERIAL9* pMaterial) { GL_BATCH_PERF_CALL_TIMER; GL_PUBLIC_ENTRYPOINT_CHECKS( this ); GLMPRINTF(("-X- IDirect3DDevice9::SetMaterial - ignored.")); // DXABSTRACT_BREAK_ON_ERROR(); return S_OK; } HRESULT IDirect3DDevice9::LightEnable(DWORD Index,BOOL Enable) { GL_BATCH_PERF_CALL_TIMER; GL_PUBLIC_ENTRYPOINT_CHECKS( this ); DXABSTRACT_BREAK_ON_ERROR(); return S_OK; } HRESULT IDirect3DDevice9::SetScissorRect(CONST RECT* pRect) { GL_BATCH_PERF_CALL_TIMER; GL_PUBLIC_ENTRYPOINT_CHECKS( this ); //int nSurfaceHeight = m_ctx->m_drawingFBO->m_attach[ kAttColor0 ].m_tex->m_layout->m_key.m_ySize; GLScissorBox_t newScissorBox = { pRect->left, pRect->top, pRect->right - pRect->left, pRect->bottom - pRect->top }; gl.m_ScissorBox = newScissorBox; m_ctx->WriteScissorBox( &gl.m_ScissorBox ); return S_OK; } HRESULT IDirect3DDevice9::GetDeviceCaps(D3DCAPS9* pCaps) { GL_BATCH_PERF_CALL_TIMER; GL_PUBLIC_ENTRYPOINT_CHECKS( this ); // "Adapter" is used to index amongst the set of fake-adapters maintained in the display DB GLMDisplayDB *db = GetDisplayDB(); int glmRendererIndex = -1; int glmDisplayIndex = -1; GLMRendererInfoFields glmRendererInfo; GLMDisplayInfoFields glmDisplayInfo; bool result = db->GetFakeAdapterInfo( m_params.m_adapter, &glmRendererIndex, &glmDisplayIndex, &glmRendererInfo, &glmDisplayInfo ); (void)result; Assert (!result); // just leave glmRendererInfo filled out for subsequent code to look at as needed. FillD3DCaps9( glmRendererInfo, pCaps ); return S_OK; } HRESULT IDirect3DDevice9::TestCooperativeLevel() { GL_BATCH_PERF_CALL_TIMER; GL_PUBLIC_ENTRYPOINT_CHECKS( this ); // game calls this to see if device was lost. // last I checked the device was still attached to the computer. // so, return OK. return S_OK; } HRESULT IDirect3DDevice9::SetClipPlane(DWORD Index,CONST float* pPlane) { GL_BATCH_PERF_CALL_TIMER; GL_PUBLIC_ENTRYPOINT_CHECKS( this ); Assert(Index<2); // We actually push the clip plane coeffs to two places // - into a shader param for ARB mode // - and into the API defined clip plane slots for GLSL mode. // if ARB mode... THIS NEEDS TO GO... it's messing up the dirty ranges.. { // this->SetVertexShaderConstantF( DXABSTRACT_VS_CLIP_PLANE_BASE+Index, pPlane, 1 ); // stash the clip plane values into shader param - translator knows where to look } // if GLSL mode... latch it and let FlushStates push it out { GLClipPlaneEquation_t peq; peq.x = pPlane[0]; peq.y = pPlane[1]; peq.z = pPlane[2]; peq.w = pPlane[3]; gl.m_ClipPlaneEquation[ Index ] = peq; FlushClipPlaneEquation(); // m_ctx->WriteClipPlaneEquation( &peq, Index ); } return S_OK; } HRESULT IDirect3DDevice9::EvictManagedResources() { GL_BATCH_PERF_CALL_TIMER; GL_PUBLIC_ENTRYPOINT_CHECKS( this ); GLMPRINTF(("-X- IDirect3DDevice9::EvictManagedResources --> IGNORED")); return S_OK; } HRESULT IDirect3DDevice9::SetLight(DWORD Index,CONST D3DLIGHT9*) { GL_BATCH_PERF_CALL_TIMER; GL_PUBLIC_ENTRYPOINT_CHECKS( this ); DXABSTRACT_BREAK_ON_ERROR(); return S_OK; } void IDirect3DDevice9::SetGammaRamp(UINT iSwapChain,DWORD Flags,CONST D3DGAMMARAMP* pRamp) { GL_BATCH_PERF_CALL_TIMER; Assert( GetCurrentOwnerThreadId() == ThreadGetCurrentId() ); if ( g_pLauncherMgr ) { g_pLauncherMgr->SetGammaRamp( pRamp->red, pRamp->green, pRamp->blue ); } } void TOGLMETHODCALLTYPE IDirect3DDevice9::SaveGLState() { } void TOGLMETHODCALLTYPE IDirect3DDevice9::RestoreGLState() { m_ctx->ForceFlushStates(); m_bFBODirty = true; } void IDirect3DDevice9::AcquireThreadOwnership( ) { GL_BATCH_PERF_CALL_TIMER; m_ctx->MakeCurrent( true ); } void IDirect3DDevice9::ReleaseThreadOwnership( ) { GL_BATCH_PERF_CALL_TIMER; m_ctx->ReleaseCurrent( true ); } void IDirect3DDevice9::SetMaxUsedVertexShaderConstantsHintNonInline( uint nMaxReg ) { GL_BATCH_PERF_CALL_TIMER; m_ctx->SetMaxUsedVertexShaderConstantsHint( nMaxReg ); } HRESULT IDirect3DDevice9::SetRenderState( D3DRENDERSTATETYPE State, DWORD Value ) { GL_BATCH_PERF_CALL_TIMER; GL_PUBLIC_ENTRYPOINT_CHECKS( this ); TOGL_NULL_DEVICE_CHECK; #if GLMDEBUG // None of this is needed normally. char rsSpew = 1; char ignored = 0; if (State >= D3DRS_VALUE_LIMIT) { DXABSTRACT_BREAK_ON_ERROR(); // bad return S_OK; } const D3D_RSINFO *info = &g_D3DRS_INFO_unpacked[ State ]; if (info->m_state != State) { DXABSTRACT_BREAK_ON_ERROR(); // bad - we never set up that state in our list return S_OK; } if (rsSpew) { GLMPRINTF(("-X- IDirect3DDevice9::SetRenderState: set %s(%d) to %d(0x%08x) ( class %d, defval is %d(0x%08x) )", GLMDecode( eD3D_RSTATE,State),State, Value,Value, info->m_class, info->m_defval,info->m_defval )); } switch( info->m_class ) { case 0: // just ignore quietly. example: D3DRS_LIGHTING ignored = 1; break; case 1: { // no GL response - and no error as long as the write value matches the default if (Value != info->m_defval) { static char stop_here_1 = 0; if (stop_here_1) DXABSTRACT_BREAK_ON_ERROR(); } break; } case 2: { // provide GL response, but only support known default value if (Value != info->m_defval) { static char stop_here_2 = 0; if (stop_here_2) DXABSTRACT_BREAK_ON_ERROR(); } // fall through to mode 3 } case 3: // normal case - the switch statement below will handle this break; } #endif switch (State) { case D3DRS_ZENABLE: // kGLDepthTestEnable { gl.m_DepthTestEnable.enable = Value; m_ctx->WriteDepthTestEnable( &gl.m_DepthTestEnable ); break; } case D3DRS_ZWRITEENABLE: // kGLDepthMask { gl.m_DepthMask.mask = Value; m_ctx->WriteDepthMask( &gl.m_DepthMask ); break; } case D3DRS_ZFUNC: { // kGLDepthFunc GLenum func = D3DCompareFuncToGL( Value ); gl.m_DepthFunc.func = func; m_ctx->WriteDepthFunc( &gl.m_DepthFunc ); break; } case D3DRS_COLORWRITEENABLE: // kGLColorMaskSingle { gl.m_ColorMaskSingle.r = ((Value & D3DCOLORWRITEENABLE_RED) != 0) ? 0xFF : 0x00; gl.m_ColorMaskSingle.g = ((Value & D3DCOLORWRITEENABLE_GREEN)!= 0) ? 0xFF : 0x00; gl.m_ColorMaskSingle.b = ((Value & D3DCOLORWRITEENABLE_BLUE) != 0) ? 0xFF : 0x00; gl.m_ColorMaskSingle.a = ((Value & D3DCOLORWRITEENABLE_ALPHA)!= 0) ? 0xFF : 0x00; m_ctx->WriteColorMaskSingle( &gl.m_ColorMaskSingle ); break; } case D3DRS_CULLMODE: // kGLCullFaceEnable / kGLCullFrontFace { switch (Value) { case D3DCULL_NONE: { gl.m_CullFaceEnable.enable = false; gl.m_CullFrontFace.value = GL_CCW; //doesn't matter m_ctx->WriteCullFaceEnable( &gl.m_CullFaceEnable ); m_ctx->WriteCullFrontFace( &gl.m_CullFrontFace ); break; } case D3DCULL_CW: { gl.m_CullFaceEnable.enable = true; gl.m_CullFrontFace.value = GL_CW; //origGL_CCW; m_ctx->WriteCullFaceEnable( &gl.m_CullFaceEnable ); m_ctx->WriteCullFrontFace( &gl.m_CullFrontFace ); break; } case D3DCULL_CCW: { gl.m_CullFaceEnable.enable = true; gl.m_CullFrontFace.value = GL_CCW; //origGL_CW; m_ctx->WriteCullFaceEnable( &gl.m_CullFaceEnable ); m_ctx->WriteCullFrontFace( &gl.m_CullFrontFace ); break; } default: { DXABSTRACT_BREAK_ON_ERROR(); break; } } break; } //-------------------------------------------------------------------------------------------- alphablend stuff case D3DRS_ALPHABLENDENABLE: // kGLBlendEnable { gl.m_BlendEnable.enable = Value; m_ctx->WriteBlendEnable( &gl.m_BlendEnable ); break; } case D3DRS_BLENDOP: // kGLBlendEquation // D3D blend-op ==> GL blend equation { GLenum equation = D3DBlendOperationToGL( Value ); gl.m_BlendEquation.equation = equation; m_ctx->WriteBlendEquation( &gl.m_BlendEquation ); break; } case D3DRS_SRCBLEND: // kGLBlendFactor // D3D blend-factor ==> GL blend factor case D3DRS_DESTBLEND: // kGLBlendFactor { GLenum factor = D3DBlendFactorToGL( Value ); if (State==D3DRS_SRCBLEND) { gl.m_BlendFactor.srcfactor = factor; } else { gl.m_BlendFactor.dstfactor = factor; } m_ctx->WriteBlendFactor( &gl.m_BlendFactor ); break; } case D3DRS_SRGBWRITEENABLE: // kGLBlendEnableSRGB { gl.m_BlendEnableSRGB.enable = Value; m_ctx->WriteBlendEnableSRGB( &gl.m_BlendEnableSRGB ); break; } //-------------------------------------------------------------------------------------------- alphatest stuff case D3DRS_ALPHATESTENABLE: { gl.m_AlphaTestEnable.enable = Value; m_ctx->WriteAlphaTestEnable( &gl.m_AlphaTestEnable ); break; } case D3DRS_ALPHAREF: { gl.m_AlphaTestFunc.ref = Value / 255.0f; m_ctx->WriteAlphaTestFunc( &gl.m_AlphaTestFunc ); break; } case D3DRS_ALPHAFUNC: { GLenum func = D3DCompareFuncToGL( Value );; gl.m_AlphaTestFunc.func = func; m_ctx->WriteAlphaTestFunc( &gl.m_AlphaTestFunc ); break; } //-------------------------------------------------------------------------------------------- stencil stuff case D3DRS_STENCILENABLE: // GLStencilTestEnable_t { gl.m_StencilTestEnable.enable = Value; m_ctx->WriteStencilTestEnable( &gl.m_StencilTestEnable ); break; } case D3DRS_STENCILFAIL: // GLStencilOp_t "what do you do if stencil test fails" { GLenum stencilop = D3DStencilOpToGL( Value ); gl.m_StencilOp.sfail = stencilop; m_ctx->WriteStencilOp( &gl.m_StencilOp,0 ); m_ctx->WriteStencilOp( &gl.m_StencilOp,1 ); // ********* need to recheck this break; } case D3DRS_STENCILZFAIL: // GLStencilOp_t "what do you do if stencil test passes *but* depth test fails, if depth test happened" { GLenum stencilop = D3DStencilOpToGL( Value ); gl.m_StencilOp.dpfail = stencilop; m_ctx->WriteStencilOp( &gl.m_StencilOp,0 ); m_ctx->WriteStencilOp( &gl.m_StencilOp,1 ); // ********* need to recheck this break; } case D3DRS_STENCILPASS: // GLStencilOp_t "what do you do if stencil test and depth test both pass" { GLenum stencilop = D3DStencilOpToGL( Value ); gl.m_StencilOp.dppass = stencilop; m_ctx->WriteStencilOp( &gl.m_StencilOp,0 ); m_ctx->WriteStencilOp( &gl.m_StencilOp,1 ); // ********* need to recheck this break; } case D3DRS_STENCILFUNC: // GLStencilFunc_t { GLenum stencilfunc = D3DCompareFuncToGL( Value ); gl.m_StencilFunc.frontfunc = gl.m_StencilFunc.backfunc = stencilfunc; m_ctx->WriteStencilFunc( &gl.m_StencilFunc ); break; } case D3DRS_STENCILREF: // GLStencilFunc_t { gl.m_StencilFunc.ref = Value; m_ctx->WriteStencilFunc( &gl.m_StencilFunc ); break; } case D3DRS_STENCILMASK: // GLStencilFunc_t { gl.m_StencilFunc.mask = Value; m_ctx->WriteStencilFunc( &gl.m_StencilFunc ); break; } case D3DRS_STENCILWRITEMASK: // GLStencilWriteMask_t { gl.m_StencilWriteMask.mask = Value; m_ctx->WriteStencilWriteMask( &gl.m_StencilWriteMask ); break; } case D3DRS_FOGENABLE: // none of these are implemented yet... erk { gl.m_FogEnable = (Value != 0); GLMPRINTF(("-D- fogenable = %d",Value )); break; } case D3DRS_SCISSORTESTENABLE: // kGLScissorEnable { gl.m_ScissorEnable.enable = Value; m_ctx->WriteScissorEnable( &gl.m_ScissorEnable ); break; } case D3DRS_DEPTHBIAS: // kGLDepthBias { // the value in the dword is actually a float float fvalue = *(float*)&Value; gl.m_DepthBias.units = fvalue; m_ctx->WriteDepthBias( &gl.m_DepthBias ); break; } // good ref on these: http://aras-p.info/blog/2008/06/12/depth-bias-and-the-power-of-deceiving-yourself/ case D3DRS_SLOPESCALEDEPTHBIAS: { // the value in the dword is actually a float float fvalue = *(float*)&Value; gl.m_DepthBias.factor = fvalue; m_ctx->WriteDepthBias( &gl.m_DepthBias ); break; } // Alpha to coverage case D3DRS_ADAPTIVETESS_Y: { gl.m_AlphaToCoverageEnable.enable = Value; m_ctx->WriteAlphaToCoverageEnable( &gl.m_AlphaToCoverageEnable ); break; } case D3DRS_CLIPPLANEENABLE: // kGLClipPlaneEnable { // d3d packs all the enables into one word. // we break that out so we don't do N glEnable calls to sync - // GLM is tracking one unique enable per plane. for( int i=0; iWriteClipPlaneEnable( &gl.m_ClipPlaneEnable[x], x ); break; } //-------------------------------------------------------------------------------------------- polygon/fill mode case D3DRS_FILLMODE: { GLuint mode = 0; switch(Value) { case D3DFILL_POINT: mode = GL_POINT; break; case D3DFILL_WIREFRAME: mode = GL_LINE; break; case D3DFILL_SOLID: mode = GL_FILL; break; default: DXABSTRACT_BREAK_ON_ERROR(); break; } gl.m_PolygonMode.values[0] = gl.m_PolygonMode.values[1] = mode; m_ctx->WritePolygonMode( &gl.m_PolygonMode ); break; } #if GLMDEBUG case D3DRS_MULTISAMPLEANTIALIAS: case D3DRS_MULTISAMPLEMASK: case D3DRS_FOGCOLOR: case D3DRS_FOGTABLEMODE: case D3DRS_FOGSTART: case D3DRS_FOGEND: case D3DRS_FOGDENSITY: case D3DRS_RANGEFOGENABLE: case D3DRS_FOGVERTEXMODE: case D3DRS_COLORWRITEENABLE1: // kGLColorMaskMultiple case D3DRS_COLORWRITEENABLE2: // kGLColorMaskMultiple case D3DRS_COLORWRITEENABLE3: // kGLColorMaskMultiple case D3DRS_SEPARATEALPHABLENDENABLE: case D3DRS_BLENDOPALPHA: case D3DRS_SRCBLENDALPHA: case D3DRS_DESTBLENDALPHA: case D3DRS_TWOSIDEDSTENCILMODE: // -> GL_STENCIL_TEST_TWO_SIDE_EXT... not yet implemented ? case D3DRS_CCW_STENCILFAIL: // GLStencilOp_t case D3DRS_CCW_STENCILZFAIL: // GLStencilOp_t case D3DRS_CCW_STENCILPASS: // GLStencilOp_t case D3DRS_CCW_STENCILFUNC: // GLStencilFunc_t case D3DRS_CLIPPING: // ???? is clipping ever turned off ?? case D3DRS_LASTPIXEL: case D3DRS_DITHERENABLE: case D3DRS_SHADEMODE: default: ignored = 1; break; #endif } #if GLMDEBUG if (rsSpew && ignored) { GLMPRINTF(("-X- (ignored)")); } #endif return S_OK; } HRESULT IDirect3DDevice9::SetSamplerStateNonInline( DWORD Sampler, D3DSAMPLERSTATETYPE Type, DWORD Value ) { GL_BATCH_PERF_CALL_TIMER; GL_PUBLIC_ENTRYPOINT_CHECKS( this ); Assert( Sampler < GLM_SAMPLER_COUNT ); m_ctx->SetSamplerDirty( Sampler ); switch( Type ) { case D3DSAMP_ADDRESSU: m_ctx->SetSamplerAddressU( Sampler, Value ); break; case D3DSAMP_ADDRESSV: m_ctx->SetSamplerAddressV( Sampler, Value ); break; case D3DSAMP_ADDRESSW: m_ctx->SetSamplerAddressW( Sampler, Value ); break; case D3DSAMP_BORDERCOLOR: m_ctx->SetSamplerBorderColor( Sampler, Value ); break; case D3DSAMP_MAGFILTER: m_ctx->SetSamplerMagFilter( Sampler, Value ); break; case D3DSAMP_MIPFILTER: m_ctx->SetSamplerMipFilter( Sampler, Value ); break; case D3DSAMP_MINFILTER: m_ctx->SetSamplerMinFilter( Sampler, Value ); break; case D3DSAMP_MIPMAPLODBIAS: m_ctx->SetSamplerMipMapLODBias( Sampler, Value ); break; case D3DSAMP_MAXMIPLEVEL: m_ctx->SetSamplerMaxMipLevel( Sampler, Value); break; case D3DSAMP_MAXANISOTROPY: m_ctx->SetSamplerMaxAnisotropy( Sampler, Value); break; case D3DSAMP_SRGBTEXTURE: //m_samplers[ Sampler ].m_srgb = Value; m_ctx->SetSamplerSRGBTexture(Sampler, Value); break; case D3DSAMP_SHADOWFILTER: m_ctx->SetShadowFilter(Sampler, Value); break; default: DXABSTRACT_BREAK_ON_ERROR(); break; } return S_OK; } void IDirect3DDevice9::SetSamplerStatesNonInline( DWORD Sampler, DWORD AddressU, DWORD AddressV, DWORD AddressW, DWORD MinFilter, DWORD MagFilter, DWORD MipFilter, DWORD MinLod, float LodBias) { GL_BATCH_PERF_CALL_TIMER; GL_PUBLIC_ENTRYPOINT_CHECKS( this ); Assert( Sampler < GLM_SAMPLER_COUNT); m_ctx->SetSamplerDirty( Sampler ); m_ctx->SetSamplerStates( Sampler, AddressU, AddressV, AddressW, MinFilter, MagFilter, MipFilter, MinLod, LodBias ); } HRESULT IDirect3DDevice9::SetTextureNonInline(DWORD Stage,IDirect3DBaseTexture9* pTexture) { GL_BATCH_PERF_CALL_TIMER; GL_PUBLIC_ENTRYPOINT_CHECKS( this ); Assert( Stage < GLM_SAMPLER_COUNT ); m_textures[Stage] = pTexture; m_ctx->SetSamplerTex( Stage, pTexture ? pTexture->m_tex : NULL ); return S_OK; } // ------------------------------------------------------------------------------------------------------------------------------ // void* ID3DXBuffer::GetBufferPointer() { GL_BATCH_PERF_CALL_TIMER; DXABSTRACT_BREAK_ON_ERROR(); return NULL; } DWORD ID3DXBuffer::GetBufferSize() { GL_BATCH_PERF_CALL_TIMER; DXABSTRACT_BREAK_ON_ERROR(); return 0; } #ifndef _MSC_VER #pragma mark ----- More D3DX stuff #endif // ------------------------------------------------------------------------------------------------------------------------------ // // D3DX stuff. // ------------------------------------------------------------------------------------------------------------------------------ // // matrix stack... HRESULT D3DXCreateMatrixStack( DWORD Flags, LPD3DXMATRIXSTACK* ppStack) { *ppStack = new ID3DXMatrixStack; (*ppStack)->Create(); return S_OK; } ID3DXMatrixStack::ID3DXMatrixStack( void ) { m_refcount[0] = 1; m_refcount[1] = 0; }; void ID3DXMatrixStack::AddRef( int which, char *comment ) { Assert( which >= 0 ); Assert( which < 2 ); m_refcount[which]++; }; ULONG ID3DXMatrixStack::Release( int which, char *comment ) { Assert( which >= 0 ); Assert( which < 2 ); bool deleting = false; m_refcount[which]--; if ( (!m_refcount[0]) && (!m_refcount[1]) ) { deleting = true; } if (deleting) { if (m_mark) { GLMPRINTF(("")) ; // place to hang a breakpoint } delete this; return 0; } else { return m_refcount[0]; } }; HRESULT ID3DXMatrixStack::Create() { m_stack.EnsureCapacity( 16 ); // 1KB ish m_stack.AddToTail(); m_stackTop = 0; // top of stack is at index 0 currently LoadIdentity(); return S_OK; } D3DXMATRIX* ID3DXMatrixStack::GetTop() { return (D3DXMATRIX*)&m_stack[ m_stackTop ]; } void ID3DXMatrixStack::Push() { D3DMATRIX temp = m_stack[ m_stackTop ]; m_stack.AddToTail( temp ); m_stackTop ++; } void ID3DXMatrixStack::Pop() { int elem = m_stackTop--; m_stack.Remove( elem ); } void ID3DXMatrixStack::LoadIdentity() { D3DXMATRIX *mat = GetTop(); D3DXMatrixIdentity( mat ); } void ID3DXMatrixStack::LoadMatrix( const D3DXMATRIX *pMat ) { *(GetTop()) = *pMat; } void ID3DXMatrixStack::MultMatrix( const D3DXMATRIX *pMat ) { // http://msdn.microsoft.com/en-us/library/bb174057(VS.85).aspx // This method right-multiplies the given matrix to the current matrix // (transformation is about the current world origin). // m_pstack[m_currentPos] = m_pstack[m_currentPos] * (*pMat); // This method does not add an item to the stack, it replaces the current // matrix with the product of the current matrix and the given matrix. DXABSTRACT_BREAK_ON_ERROR(); } void ID3DXMatrixStack::MultMatrixLocal( const D3DXMATRIX *pMat ) { // http://msdn.microsoft.com/en-us/library/bb174058(VS.85).aspx // This method left-multiplies the given matrix to the current matrix // (transformation is about the local origin of the object). // m_pstack[m_currentPos] = (*pMat) * m_pstack[m_currentPos]; // This method does not add an item to the stack, it replaces the current // matrix with the product of the given matrix and the current matrix. DXABSTRACT_BREAK_ON_ERROR(); } HRESULT ID3DXMatrixStack::ScaleLocal(FLOAT x, FLOAT y, FLOAT z) { // http://msdn.microsoft.com/en-us/library/bb174066(VS.85).aspx // Scale the current matrix about the object origin. // This method left-multiplies the current matrix with the computed // scale matrix. The transformation is about the local origin of the object. // // D3DXMATRIX tmp; // D3DXMatrixScaling(&tmp, x, y, z); // m_stack[m_currentPos] = tmp * m_stack[m_currentPos]; DXABSTRACT_BREAK_ON_ERROR(); return S_OK; } HRESULT ID3DXMatrixStack::RotateAxisLocal(CONST D3DXVECTOR3* pV, FLOAT Angle) { // http://msdn.microsoft.com/en-us/library/bb174062(VS.85).aspx // Left multiply the current matrix with the computed rotation // matrix, counterclockwise about the given axis with the given angle. // (rotation is about the local origin of the object) // D3DXMATRIX tmp; // D3DXMatrixRotationAxis( &tmp, pV, angle ); // m_stack[m_currentPos] = tmp * m_stack[m_currentPos]; // Because the rotation is left-multiplied to the matrix stack, the rotation // is relative to the object's local coordinate space. DXABSTRACT_BREAK_ON_ERROR(); return S_OK; } HRESULT ID3DXMatrixStack::TranslateLocal(FLOAT x, FLOAT y, FLOAT z) { // http://msdn.microsoft.com/en-us/library/bb174068(VS.85).aspx // Left multiply the current matrix with the computed translation // matrix. (transformation is about the local origin of the object) // D3DXMATRIX tmp; // D3DXMatrixTranslation( &tmp, x, y, z ); // m_stack[m_currentPos] = tmp * m_stack[m_currentPos]; DXABSTRACT_BREAK_ON_ERROR(); return S_OK; } const char* D3DXGetPixelShaderProfile( IDirect3DDevice9 *pDevice ) { DXABSTRACT_BREAK_ON_ERROR(); return ""; } #ifdef _MSC_VER #pragma warning (push) #pragma warning (disable:4701) // potentially uninitialized local variable 'temp' used #endif D3DXMATRIX* D3DXMatrixMultiply( D3DXMATRIX *pOut, CONST D3DXMATRIX *pM1, CONST D3DXMATRIX *pM2 ) { D3DXMATRIX temp; for( int i=0; i<4; i++) { for( int j=0; j<4; j++) { temp.m[i][j] = (pM1->m[ i ][ 0 ] * pM2->m[ 0 ][ j ]) + (pM1->m[ i ][ 1 ] * pM2->m[ 1 ][ j ]) + (pM1->m[ i ][ 2 ] * pM2->m[ 2 ][ j ]) + (pM1->m[ i ][ 3 ] * pM2->m[ 3 ][ j ]); } } *pOut = temp; return pOut; } #ifdef _MSC_VER #pragma warning(pop) #endif // Transform a 3D vector by a given matrix, projecting the result back into w = 1 // http://msdn.microsoft.com/en-us/library/ee417622(VS.85).aspx D3DXVECTOR3* D3DXVec3TransformCoord(D3DXVECTOR3 *pOut, CONST D3DXVECTOR3 *pV, CONST D3DXMATRIX *pM) { D3DXVECTOR3 vOut; float norm = (pM->m[0][3] * pV->x) + (pM->m[1][3] * pV->y) + (pM->m[2][3] *pV->z) + pM->m[3][3]; if ( norm ) { float norm_inv = 1.0f / norm; vOut.x = (pM->m[0][0] * pV->x + pM->m[1][0] * pV->y + pM->m[2][0] * pV->z + pM->m[3][0]) * norm_inv; vOut.y = (pM->m[0][1] * pV->x + pM->m[1][1] * pV->y + pM->m[2][1] * pV->z + pM->m[3][1]) * norm_inv; vOut.z = (pM->m[0][2] * pV->x + pM->m[1][2] * pV->y + pM->m[2][2] * pV->z + pM->m[3][2]) * norm_inv; } else { vOut.x = vOut.y = vOut.z = 0.0f; } *pOut = vOut; return pOut; } void D3DXMatrixIdentity( D3DXMATRIX *mat ) { for( int i=0; i<4; i++) { for( int j=0; j<4; j++) { mat->m[i][j] = (i==j) ? 1.0f : 0.0f; // 1's on the diagonal. } } } D3DXMATRIX* D3DXMatrixTranslation( D3DXMATRIX *pOut, FLOAT x, FLOAT y, FLOAT z ) { D3DXMatrixIdentity( pOut ); pOut->m[3][0] = x; pOut->m[3][1] = y; pOut->m[3][2] = z; return pOut; } D3DXMATRIX* D3DXMatrixInverse( D3DXMATRIX *pOut, FLOAT *pDeterminant, CONST D3DXMATRIX *pM ) { Assert( sizeof( D3DXMATRIX ) == (16 * sizeof(float) ) ); Assert( sizeof( VMatrix ) == (16 * sizeof(float) ) ); Assert( pDeterminant == NULL ); // homey don't play that VMatrix *origM = (VMatrix*)pM; VMatrix *destM = (VMatrix*)pOut; bool success = MatrixInverseGeneral( *origM, *destM ); (void)success; Assert( success ); return pOut; } D3DXMATRIX* D3DXMatrixTranspose( D3DXMATRIX *pOut, CONST D3DXMATRIX *pM ) { if (pOut != pM) { for( int i=0; i<4; i++) { for( int j=0; j<4; j++) { pOut->m[i][j] = pM->m[j][i]; } } } else { D3DXMATRIX temp = *pM; D3DXMatrixTranspose( pOut, &temp ); } return NULL; } D3DXPLANE* D3DXPlaneNormalize( D3DXPLANE *pOut, CONST D3DXPLANE *pP) { // not very different from normalizing a vector. // figure out the square root of the sum-of-squares of the x,y,z components // make sure that's non zero // then divide all four components by that value // or return some dummy plane like 0,0,1,0 if it fails float len = sqrt( (pP->a * pP->a) + (pP->b * pP->b) + (pP->c * pP->c) ); if (len > 1e-10) //FIXME need a real epsilon here ? { pOut->a = pP->a / len; pOut->b = pP->b / len; pOut->c = pP->c / len; pOut->d = pP->d / len; } else { pOut->a = 0.0f; pOut->b = 0.0f; pOut->c = 1.0f; pOut->d = 0.0f; } return pOut; } D3DXVECTOR4* D3DXVec4Transform( D3DXVECTOR4 *pOut, CONST D3DXVECTOR4 *pV, CONST D3DXMATRIX *pM ) { VMatrix *mat = (VMatrix*)pM; Vector4D *vIn = (Vector4D*)pV; Vector4D *vOut = (Vector4D*)pOut; Vector4DMultiplyTranspose( *mat, *vIn, *vOut ); return pOut; } D3DXVECTOR4* D3DXVec4Normalize( D3DXVECTOR4 *pOut, CONST D3DXVECTOR4 *pV ) { Vector4D *vIn = (Vector4D*) pV; Vector4D *vOut = (Vector4D*) pOut; *vOut = *vIn; Vector4DNormalize( *vOut ); return pOut; } D3DXMATRIX* D3DXMatrixOrthoOffCenterRH( D3DXMATRIX *pOut, FLOAT l, FLOAT r, FLOAT b, FLOAT t, FLOAT zn,FLOAT zf ) { DXABSTRACT_BREAK_ON_ERROR(); return NULL; } D3DXMATRIX* D3DXMatrixPerspectiveRH( D3DXMATRIX *pOut, FLOAT w, FLOAT h, FLOAT zn, FLOAT zf ) { DXABSTRACT_BREAK_ON_ERROR(); return NULL; } D3DXMATRIX* D3DXMatrixPerspectiveOffCenterRH( D3DXMATRIX *pOut, FLOAT l, FLOAT r, FLOAT b, FLOAT t, FLOAT zn, FLOAT zf ) { DXABSTRACT_BREAK_ON_ERROR(); return NULL; } D3DXPLANE* D3DXPlaneTransform( D3DXPLANE *pOut, CONST D3DXPLANE *pP, CONST D3DXMATRIX *pM ) { float *out = &pOut->a; // dot dot dot for( int x=0; x<4; x++ ) { out[x] = (pM->m[0][x] * pP->a) + (pM->m[1][x] * pP->b) + (pM->m[2][x] * pP->c) + (pM->m[3][x] * pP->d); } return pOut; } // ------------------------------------------------------------------------------------------------------------------------------ // IDirect3D9 *Direct3DCreate9(UINT SDKVersion) { GLMPRINTF(( "-X- Direct3DCreate9: %d", SDKVersion )); return new IDirect3D9; } // ------------------------------------------------------------------------------------------------------------------------------ // void D3DPERF_SetOptions( DWORD dwOptions ) { } HRESULT D3DXCompileShader( LPCSTR pSrcData, UINT SrcDataLen, CONST D3DXMACRO* pDefines, LPD3DXINCLUDE pInclude, LPCSTR pFunctionName, LPCSTR pProfile, DWORD Flags, LPD3DXBUFFER* ppShader, LPD3DXBUFFER* ppErrorMsgs, LPD3DXCONSTANTTABLE* ppConstantTable) { DXABSTRACT_BREAK_ON_ERROR(); // is anyone calling this ? return S_OK; } #if defined(DX_TO_GL_ABSTRACTION) void toglGetClientRect( void *hWnd, RECT *destRect ) { // the only useful answer this call can offer, is the size of the canvas. // actually getting the window bounds is not useful. // so, see if a D3D device is up and running, and if so, // dig in and find out its backbuffer size and use that. uint width, height; g_pLauncherMgr->RenderedSize( width, height, false ); // false = get them, don't set them Assert( width!=0 && height!=0 ); destRect->left = 0; destRect->top = 0; destRect->right = width; destRect->bottom = height; //GLMPRINTF(( "-D- GetClientRect returning rect of (0,0, %d,%d)",width,height )); return; } #endif #endif