//================ Copyright (c) 1996-2009 Valve Corporation. All Rights Reserved. ================= // // // //================================================================================================== #include "dxabstract.h" #include "tier0/platform.h" #include "tier0/dbg.h" #include "tier0/threadtools.h" #include #include "tier1/strtools.h" #include "tier1/utlbuffer.h" //#include "dx9asmtogl.h" #include "dx9asmtogl2.h" #include "mathlib/vmatrix.h" #ifdef OSX #include "glmgr/glmgr.h" #include "appframework/icocoamgr.h" extern ICocoaMgr *g_extCocoaMgr; #include #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 // ------------------------------------------------------------------------------------------------------------------------------ // bool g_useASMTranslations = true; //static D3DToGL_ASM g_D3DToOpenGLTranslatorASM; // old translator retired static D3DToGL g_D3DToOpenGLTranslatorASM; // same class as the GLSL one, just invoked with different options bool g_useGLSLTranslations = true; static D3DToGL g_D3DToOpenGLTranslatorGLSL; bool g_bUseControlFlow = false; // ------------------------------------------------------------------------------------------------------------------------------ // void GlobalMemoryStatus( MEMORYSTATUS *pOut ) { //cheese: return 2GB physical pOut->dwTotalPhys = (1<<31); } void Sleep( unsigned int ms ) { Debugger(); ThreadSleep( ms ); } bool IsIconic( VD3DHWND hWnd ) { // FIXME for now just act non-minimized all the time //Debugger(); return false; } void GetClientRect( 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_extCocoaMgr->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; } BOOL ClientToScreen( VD3DHWND hWnd, LPPOINT pPoint ) { Debugger(); return true; } void* GetCurrentThread() { Debugger(); return 0; } void SetThreadAffinityMask( void *hThread, int nMask ) { Debugger(); } bool GUID::operator==( const struct _GUID &other ) const { Debugger(); return memcmp( this, &other, sizeof( GUID ) ) == 0; } // ------------------------------------------------------------------------------------------------------------------------------ // #pragma mark ----- D3DXMATRIX operators 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]; } // ------------------------------------------------------------------------------------------------------------------------------ // #pragma mark ----- D3DXPLANE operators 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; } // ------------------------------------------------------------------------------------------------------------------------------ // #pragma mark ----- D3DXVECTOR2 operators D3DXVECTOR2::operator FLOAT* () { return (float*)this; } D3DXVECTOR2::operator CONST FLOAT* () const { return (const float*)this; } // ------------------------------------------------------------------------------------------------------------------------------ // #pragma mark ----- D3DXVECTOR3 operators 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; } // ------------------------------------------------------------------------------------------------------------------------------ // #pragma mark ----- D3DXVECTOR4 operators D3DXVECTOR4::D3DXVECTOR4( float a, float b, float c, float d ) { x = a; y = b; z = c; w = d; } // ------------------------------------------------------------------------------------------------------------------------------ // DWORD IDirect3DResource9::SetPriority(DWORD PriorityNew) { // Debugger(); // GLMPRINTF(( "-X- SetPriority" )); // no-op city return 0; } // ------------------------------------------------------------------------------------------------------------------------------ // #pragma mark ----- IDirect3DBaseTexture9 IDirect3DBaseTexture9::~IDirect3DBaseTexture9() { GLMPRINTF(( ">-A- ~IDirect3DBaseTexture9" )); if (m_device) { 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_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() { return m_restype; //D3DRTYPE_TEXTURE; } DWORD IDirect3DBaseTexture9::GetLevelCount() { return m_tex->m_layout->m_mipCount; } HRESULT IDirect3DBaseTexture9::GetLevelDesc(UINT Level,D3DSURFACE_DESC *pDesc) { Assert (Level < 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; } // ------------------------------------------------------------------------------------------------------------------------------ // #pragma mark ----- IDirect3DTexture9 HRESULT IDirect3DDevice9::CreateTexture(UINT Width,UINT Height,UINT Levels,DWORD Usage,D3DFORMAT Format,D3DPOOL Pool,IDirect3DTexture9** ppTexture,VD3DHANDLE* pSharedHandle, char *pDebugLabel ) { 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 ) { GLMDebugger(); } 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) ); key.m_texFlags |= kGLMTexRenderable; 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; } } key.m_xSize = Width; key.m_ySize = Height; key.m_zSize = 1; CGLMTex *tex = m_ctx->NewTex( &key, pDebugLabel ); if ( !tex ) { GLMDebugger(); } dxtex->m_tex = tex; dxtex->m_srgbFlipCount = 0; 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() { 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)" ); 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) { Debugger(); return S_OK; } HRESULT IDirect3DTexture9::UnlockRect(UINT Level) { Debugger(); return S_OK; } HRESULT IDirect3DTexture9::GetSurfaceLevel(UINT Level,IDirect3DSurface9** ppSurfaceLevel) { // we create and pass back a surface, and the client is on the hook to release it. tidy. 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? // 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; } // ------------------------------------------------------------------------------------------------------------------------------ // #pragma mark ----- IDirect3DCubeTexture9 HRESULT IDirect3DDevice9::CreateCubeTexture(UINT EdgeLength,UINT Levels,DWORD Usage,D3DFORMAT Format,D3DPOOL Pool,IDirect3DCubeTexture9** ppCubeTexture,VD3DHANDLE* pSharedHandle, char *pDebugLabel) { GLMPRINTF((">-A- IDirect3DDevice9::CreateCubeTexture")); 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 ) { GLMDebugger(); } if (Usage & D3DUSAGE_AUTOGENMIPMAP) { key.m_texFlags |= kGLMTexMipped | kGLMTexMippedAuto; } if (Usage & D3DUSAGE_RENDERTARGET) { key.m_texFlags |= kGLMTexRenderable; } 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, pDebugLabel ); if (!tex) { GLMDebugger(); } dxtex->m_tex = tex; dxtex->m_srgbFlipCount = 0; for( int face = 0; face < 6; face ++) { 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() { 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) { // we create and pass back a surface, and the client is on the hook to release it... 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) { Assert (Level < 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; } // ------------------------------------------------------------------------------------------------------------------------------ // #pragma mark ----- IDirect3DVolumeTexture9 HRESULT IDirect3DDevice9::CreateVolumeTexture(UINT Width,UINT Height,UINT Depth,UINT Levels,DWORD Usage,D3DFORMAT Format,D3DPOOL Pool,IDirect3DVolumeTexture9** ppVolumeTexture,VD3DHANDLE* pSharedHandle, char *pDebugLabel) { GLMPRINTF((">-A- IDirect3DDevice9::CreateVolumeTexture")); // set dxtex->m_restype to D3DRTYPE_VOLUMETEXTURE... 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 ) { Debugger(); } if (Usage & D3DUSAGE_AUTOGENMIPMAP) { key.m_texFlags |= kGLMTexMipped | kGLMTexMippedAuto; } if (Usage & D3DUSAGE_RENDERTARGET) { key.m_texFlags |= kGLMTexRenderable; } 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, pDebugLabel ); if (!tex) { Debugger(); } dxtex->m_tex = tex; dxtex->m_srgbFlipCount = 0; 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() { 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)" ); 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) { 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) { 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 ) { if (Level > m_tex->m_layout->m_mipCount) { Debugger(); } 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; } // ------------------------------------------------------------------------------------------------------------------------------ // #pragma mark ----- IDirect3DSurface9 IDirect3DSurface9::~IDirect3DSurface9() { // 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_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) { 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() { 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) { *pDesc = m_desc; return S_OK; } // ------------------------------------------------------------------------------------------------------------------------------ // #pragma mark ----- IDirect3D9 ------------------------------------------------------- IDirect3D9::~IDirect3D9() { GLMPRINTF(("-A- ~IDirect3D9 - signpost")); } UINT IDirect3D9::GetAdapterCount() { GLMgr::NewGLMgr(); // init GL manager GLMDisplayDB *db = g_extCocoaMgr->GetDisplayDB(); int dxAdapterCount = db->GetFakeAdapterCount(); return dxAdapterCount; } HRESULT IDirect3D9::GetDeviceCaps(UINT Adapter,D3DDEVTYPE DeviceType,D3DCAPS9* pCaps) { // 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 = g_extCocoaMgr->GetDisplayDB(); int glmRendererIndex = -1; int glmDisplayIndex = -1; GLMRendererInfoFields glmRendererInfo; GLMDisplayInfoFields glmDisplayInfo; bool result = db->GetFakeAdapterInfo( Adapter, &glmRendererIndex, &glmDisplayIndex, &glmRendererInfo, &glmDisplayInfo ); Assert (!result); // just leave glmRendererInfo filled out for subsequent code to look at as needed. // 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; } g_bUseControlFlow = CommandLine()->CheckParm( "-glslcontrolflow" ); // are we ARB mode and not forcing GLSL control flow mode? if ( CommandLine()->CheckParm( "-arbmode" ) && !g_bUseControlFlow ) { pCaps->MaxUserClipPlanes = 0; } pCaps->MaxVertexBlendMatrices = 0; // see if anyone cares pCaps->MaxVertexBlendMatrixIndex = 0; // see if anyone cares pCaps->MaxPrimitiveCount = 32768; // guess pCaps->MaxStreams = 4; // guess pCaps->VertexShaderVersion = 0x200; // model 2.0 pCaps->MaxVertexShaderConst = DXABSTRACT_VS_PARAM_SLOTS; // number of vertex shader constant registers pCaps->PixelShaderVersion = 0x200; // model 2.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 ( defined ( POSIX ) && !defined( _PS3 ) ) pCaps->FakeSRGBWrite = !glmRendererInfo.m_hasGammaWrites; pCaps->CanDoSRGBReadFromRTs = !glmRendererInfo.m_cantAttachSRGB; pCaps->MixedSizeTargets = glmRendererInfo.m_hasMixedAttachmentSizes; pCaps->SRGBDecode = glmRendererInfo.m_hasSRGBDecode; #endif return S_OK; } HRESULT IDirect3D9::GetAdapterIdentifier( UINT Adapter, DWORD Flags, D3DADAPTER_IDENTIFIER9* pIdentifier ) { // 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 = g_extCocoaMgr->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 ); Assert (!result); Q_snprintf( pIdentifier->Driver, sizeof(pIdentifier->Driver), "OpenGL %s (%08x)", GLMDecode( eGL_RENDERER, glmRendererInfo.m_rendererID & 0x00FFFF00 ), glmRendererInfo.m_rendererID ); Q_snprintf( pIdentifier->Description, sizeof(pIdentifier->Description), "%s - %dx%d - %dMB VRAM", GLMDecode( eGL_RENDERER, glmRendererInfo.m_rendererID & 0x00FFFF00 ), glmDisplayInfo.m_displayPixelWidth, glmDisplayInfo.m_displayPixelHeight, glmRendererInfo.m_vidMemory >> 20 ); 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) { 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; // 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 = g_extCocoaMgr->GetDisplayDB(); int glmRendererIndex = -1; int glmDisplayIndex = -1; GLMRendererInfoFields glmRendererInfo; GLMDisplayInfoFields glmDisplayInfo; bool dbresult = db->GetFakeAdapterInfo( Adapter, &glmRendererIndex, &glmDisplayIndex, &glmRendererInfo, &glmDisplayInfo ); 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; 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 GLMPRINTF(( "-X- --> NOT OK: flags %8x:%s", unsatBits,GLMDecodeMask( eD3D_USAGE, unsatBits ) )); result = D3DERR_NOTAVAILABLE; } break; case D3DRTYPE_SURFACE: switch( 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) { GLMPRINTF(( "-X- IDirect3D9::GetAdapterModeCount: Adapter=%d || Format=%8x:%s", Adapter, Format, GLMDecode(eD3D_FORMAT, Format) )); uint modeCount=0; GLMDisplayDB *db = g_extCocoaMgr->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 ); Assert (!result); modeCount = db->GetModeCount( glmRendererIndex, glmDisplayIndex ); GLMPRINTF(( "-X- --> result is %d", modeCount )); return modeCount + 1; // Add one on for 800 x 500, which we'll tack on as mode 0 below } HRESULT IDirect3D9::EnumAdapterModes( UINT Adapter, D3DFORMAT Format, UINT Mode, D3DDISPLAYMODE* pMode ) { GLMPRINTF(( "-X- IDirect3D9::EnumAdapterModes: Adapter=%d || Format=%8x:%s || Mode=%d", Adapter, Format, GLMDecode(eD3D_FORMAT, Format), Mode )); Assert(Format==D3DFMT_X8R8G8B8); GLMDisplayDB *db = g_extCocoaMgr->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 == 0 ? 0 : Mode - 1, &glmModeInfo ); // End up asking Cocoa for mode zero twice Assert( !result2 ); if ( result2 ) return D3DERR_NOTAVAILABLE; pMode->Width = Mode == 0 ? 800 : glmModeInfo.m_modePixelWidth; // substitute in width of 800 on first "Mode zero" pMode->Height = Mode == 0 ? 500 : glmModeInfo.m_modePixelHeight; // substitute in height of 500 on first "Mode zero" 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) { //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) { // asking what the current mode is GLMPRINTF(("-X- IDirect3D9::GetAdapterDisplayMode: Adapter=%d", Adapter )); GLMDisplayDB *db = g_extCocoaMgr->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) { 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, Debugger() 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 ) { GLMDisplayDB *db = g_extCocoaMgr->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) { // 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) //{ // Debugger(); // 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 ) //{ // Debugger(); // result = D3DERR_NOTAVAILABLE; //} if ( pPresentationParameters->AutoDepthStencilFormat != D3DFMT_D24S8 ) { Debugger(); 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; } } return result; } // ------------------------------------------------------------------------------------------------------------------------------ // #pragma mark ----- IDirect3DQuery9 HRESULT IDirect3DQuery9::Issue(DWORD dwIssueFlags) { // 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. if (dwIssueFlags & D3DISSUE_BEGIN) { 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) { switch( m_type ) { case D3DQUERYTYPE_OCCLUSION: m_query->Stop(); // drop "end counter" call into stream break; case D3DQUERYTYPE_EVENT: // 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) { HRESULT result = -1; // 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_query->IsStarted() ) { Assert(!"Can't GetData before issue/start"); printf("\n** IDirect3DQuery9::GetData: can't GetData before issue/start"); result = -1; } else if ( !m_query->IsStopped() ) { Assert(!"Can't GetData before issue-end/stop"); printf("\n** IDirect3DQuery9::GetData: can't GetData before issue-end/stop"); result = -1; } else { switch( m_type ) { case D3DQUERYTYPE_OCCLUSION: { // expectation - caller already did an issue begin (start) and an issue end (stop). // we can probe using IsDone. if (flush && (!m_ctx->Caps().m_hasPerfPackage1) ) { glFlush(); } do { 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; } } while( flush && (!done) ); } break; case D3DQUERYTYPE_EVENT: { // expectation - caller already did an issue end (for fence => start) but has not done anything that would call Stop. // that's ok because Stop is a no-op for fences. if (flush && (!m_ctx->Caps().m_hasPerfPackage1) ) { glFlush(); } done = m_query->IsDone(); if (done) { m_query->Complete(NULL); // this will block on pre-SLGU *(uint*)pData = 0; result = S_OK; } else { result = S_FALSE; } } break; } } return result; } // ------------------------------------------------------------------------------------------------------------------------------ // #pragma mark ----- IDirect3DVertexBuffer9 HRESULT IDirect3DDevice9::CreateVertexBuffer(UINT Length,DWORD Usage,DWORD FVF,D3DPOOL Pool,IDirect3DVertexBuffer9** ppVertexBuffer,VD3DHANDLE* pSharedHandle) { GLMPRINTF(( ">-A- IDirect3DDevice9::CreateVertexBuffer" )); 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() { 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) { // 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_offset = OffsetToLock; lockreq.m_size = SizeToLock; lockreq.m_discard = (Flags & D3DLOCK_DISCARD) != 0; lockreq.m_nonblocking = ( (Flags & D3DLOCK_NOOVERWRITE) != 0 ) || lockreq.m_discard; 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_name, OffsetToLock, SizeToLock, *ppbData)); return S_OK; } HRESULT IDirect3DVertexBuffer9::Unlock() { m_vtxBuffer->Unlock(); return S_OK; } // ------------------------------------------------------------------------------------------------------------------------------ // #pragma mark ----- IDirect3DIndexBuffer9 HRESULT IDirect3DDevice9::CreateIndexBuffer(UINT Length,DWORD Usage,D3DFORMAT Format,D3DPOOL Pool,IDirect3DIndexBuffer9** ppIndexBuffer,VD3DHANDLE* pSharedHandle) { GLMPRINTF(( ">-A- IDirect3DDevice9::CreateIndexBuffer" )); // it is important to save all the create info, since GetDesc could get called later to query it 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() { 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) { // FIXME would be good to have "can't lock twice" logic GLMBuffLockParams lockreq; lockreq.m_offset = OffsetToLock; lockreq.m_size = SizeToLock; lockreq.m_discard = (Flags & D3DLOCK_DISCARD) != 0; lockreq.m_nonblocking = ( (Flags & D3DLOCK_NOOVERWRITE) != 0 ) || lockreq.m_discard; m_idxBuffer->Lock( &lockreq, (char**)ppbData ); return S_OK; } HRESULT IDirect3DIndexBuffer9::Unlock() { m_idxBuffer->Unlock(); return S_OK; } HRESULT IDirect3DIndexBuffer9::GetDesc(D3DINDEXBUFFER_DESC *pDesc) { *pDesc = m_idxDesc; return S_OK; } // ------------------------------------------------------------------------------------------------------------------------------ // #pragma mark ----- IDirect3DDevice9 ------------------------------------------------- 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; 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; } HRESULT IDirect3DDevice9::Create( IDirect3DDevice9Params *params ) { 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; m_drawableFBO = NULL; memset( m_rtSurfaces, 0, sizeof(m_rtSurfaces) ); m_dsSurface = NULL; m_defaultColorSurface = NULL; m_defaultDepthStencilSurface = NULL; memset( m_streams, 0, sizeof(m_streams) ); memset( m_textures, 0, sizeof(m_textures) ); memset( m_samplers, 0, sizeof(m_samplers) ); //============================================================================ // 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( &glmParams ); if (!m_ctx) { GLMPRINTF(("<-X- IDirect3DDevice9::Create (error out)")); return (HRESULT) -1; } // make an FBO to draw into and activate it. m_drawableFBO = m_ctx->NewFBO(); m_ctx->SetDrawingFBO( m_drawableFBO ); // bind it to context. will receive attachments shortly. m_ctx->BindFBOToCtx( m_drawableFBO, GL_READ_FRAMEBUFFER_EXT ); m_ctx->BindFBOToCtx( m_drawableFBO, GL_DRAW_FRAMEBUFFER_EXT ); // 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_defaultColorSurface, // ppSurface NULL // shared handle ); 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_defaultColorSurface )); GLMPRINTF(("-X- IDirect3DDevice9::Create setting color render target...")); result = this->SetRenderTarget(0, m_defaultColorSurface); 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_defaultDepthStencilSurface, // 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_defaultDepthStencilSurface)); GLMPRINTF(("-X- Direct3DDevice9::Create setting depth-stencil render target...")); result = this->SetDepthStencilSurface(m_defaultDepthStencilSurface); if (result != S_OK) { GLMDebugger(); GLMPRINTF(("<-X- IDirect3DDevice9::Create (error out)")); return result; } GLMPRINTF(("-X- IDirect3DDevice9::Create setting depth-stencil render target complete.")); bool ready = m_drawableFBO->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 ) ); GLScissorEnable_t defScissorEnable = { true }; GLScissorBox_t defScissorBox = { 0,0, m_params.m_presentationParameters.BackBufferWidth,m_params.m_presentationParameters.BackBufferHeight }; GLViewportBox_t defViewportBox = { 0,0, m_params.m_presentationParameters.BackBufferWidth,m_params.m_presentationParameters.BackBufferHeight }; 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; gl.m_stateDirtyMask = (1<RenderedSize( m_params.m_presentationParameters.BackBufferWidth, m_params.m_presentationParameters.BackBufferHeight, true ); // true = set return result; } IDirect3DDevice9::~IDirect3DDevice9() { GLMPRINTF(( "-D- IDirect3DDevice9::~IDirect3DDevice9 signpost" )); // want to know when this is called, if ever } #pragma mark ----- Basics - (IDirect3DDevice9) HRESULT IDirect3DDevice9::Reset(D3DPRESENT_PARAMETERS* pPresentationParameters) { 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. //------------------------------------------------------------------------------- absorb new presentation params.. m_params.m_presentationParameters = *pPresentationParameters; //------------------------------------------------------------------------------- color buffer.. // release old color surface if it's there.. if (m_defaultColorSurface) { ULONG refc = m_defaultColorSurface->Release( 0, "IDirect3DDevice9::Reset public release color surface" ); Assert( !refc ); m_defaultColorSurface = 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_defaultColorSurface, // 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_defaultColorSurface )); GLMPRINTF(("-X- IDirect3DDevice9::Reset setting color render target...")); result = this->SetRenderTarget(0, m_defaultColorSurface); 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_defaultDepthStencilSurface) { ULONG refc = m_defaultDepthStencilSurface->Release( 0, "IDirect3DDevice9::Reset public release depthstencil surface" ); Assert(!refc); m_defaultDepthStencilSurface = 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_defaultDepthStencilSurface, // 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_defaultDepthStencilSurface)); GLMPRINTF(("-X- IDirect3DDevice9::Reset setting depth-stencil render target...")); result = this->SetDepthStencilSurface(m_defaultDepthStencilSurface); if (result != S_OK) { GLMPRINTF(("<-X- IDirect3DDevice9::Reset (error out)")); return result; } GLMPRINTF(("-X- IDirect3DDevice9::Reset setting depth-stencil render target complete.")); bool ready = m_drawableFBO->IsReady(); if (!ready) { GLMPRINTF(("<-X- IDirect3DDevice9::Reset (error out)")); return D3DERR_DEVICELOST; } //-------------------------------------------------------------------------------zap viewport and scissor to new backbuffer size GLScissorEnable_t defScissorEnable = { true }; GLScissorBox_t defScissorBox = { 0,0, m_params.m_presentationParameters.BackBufferWidth,m_params.m_presentationParameters.BackBufferHeight }; GLViewportBox_t defViewportBox = { 0,0, m_params.m_presentationParameters.BackBufferWidth,m_params.m_presentationParameters.BackBufferHeight }; 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; gl.m_stateDirtyMask |= (1<m_displayParams.m_focusWindow; Assert( glmParams.m_focusWindow != NULL ); // so GetClientRect can return sane answers uint width, height; g_extCocoaMgr->RenderedSize( pPresentationParameters->BackBufferWidth, pPresentationParameters->BackBufferHeight, true ); // true = set m_ctx->SetDisplayParams( &glmParams ); return S_OK; } HRESULT IDirect3DDevice9::SetViewport(CONST D3DVIEWPORT9* pViewport) { 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_stateDirtyMask |= (1<MinZ; gl.m_ViewportDepthRange.far = pViewport->MaxZ; gl.m_stateDirtyMask |= (1<BeginFrame(); return S_OK; } HRESULT IDirect3DDevice9::EndScene() { 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 }; 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) { // 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. if (dxa_nullrefresh_capslock.GetInt()) { if (GetCurrentKeyModifiers() & EalphaLock ) { return S_OK; } } // no explicit ResolveTex call first - that got pushed down into GLMContext::Present m_ctx->Present( m_defaultColorSurface->m_tex ); return S_OK; } // Sanity-chedk the results /* // TODO, something like the following: // safe because of early exit on NULL above m_rtSurfaces[ RenderTargetIndex ]->AddRef(); *ppRenderTarget = m_rtSurfaces[ RenderTargetIndex ]; */ #pragma mark ----- Textures - (IDirect3DDevice9) #pragma mark ( create functions for each texture are now adjacent to the rest of the methods for each texture class) HRESULT IDirect3DDevice9::SetTexture(DWORD Stage,IDirect3DBaseTexture9* pTexture) { // texture sets are sent through immediately to GLM // but we also latch the value so we know which TMU's are active. // whuch can help FlushSamplers do less work. // place new tex m_textures[Stage] = pTexture; if (!pTexture) { m_ctx->SetSamplerTex( Stage, NULL ); } else { m_ctx->SetSamplerTex( Stage, pTexture->m_tex ); } return S_OK; } HRESULT IDirect3DDevice9::GetTexture(DWORD Stage,IDirect3DBaseTexture9** ppTexture) { // if implemented, should it increase the ref count ?? GLMDebugger(); return S_OK; } #pragma mark ----- RT's and Surfaces - (IDirect3DDevice9) HRESULT IDirect3DDevice9::CreateRenderTarget(UINT Width,UINT Height,D3DFORMAT Format,D3DMULTISAMPLE_TYPE MultiSample,DWORD MultisampleQuality,BOOL Lockable,IDirect3DSurface9** ppSurface,VD3DHANDLE* pSharedHandle, char *pDebugLabel) { HRESULT result = S_OK; 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, 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; } HRESULT IDirect3DDevice9::SetRenderTarget(DWORD RenderTargetIndex,IDirect3DSurface9* pRenderTarget) { 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_rtSurfaces[RenderTargetIndex]; if (pRenderTarget == oldTarget) { GLMPRINTF(("-F- --> no change",RenderTargetIndex)); return S_OK; } // 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. if (m_rtSurfaces[RenderTargetIndex]) { m_rtSurfaces[RenderTargetIndex]->Release( 1, "-A SetRenderTarget private release" ); // note this is the private refcount being lowered } if (pRenderTarget) { pRenderTarget->AddRef( 1, "+A SetRenderTarget private addref" ); // again, private refcount being raised } m_rtSurfaces[RenderTargetIndex] = pRenderTarget; // emplace it whether NULL or not 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) ); } return result; } HRESULT IDirect3DDevice9::GetRenderTarget(DWORD RenderTargetIndex,IDirect3DSurface9** ppRenderTarget) { if ( !m_rtSurfaces[ RenderTargetIndex ] ) return D3DERR_NOTFOUND; if ( ( RenderTargetIndex < 0 ) || ( RenderTargetIndex > 4 ) || !ppRenderTarget ) return D3DERR_INVALIDCALL; // safe because of early exit on NULL above m_rtSurfaces[ RenderTargetIndex ]->AddRef(0, "+B GetRenderTarget public addref"); // per http://msdn.microsoft.com/en-us/library/bb174404(VS.85).aspx *ppRenderTarget = m_rtSurfaces[ RenderTargetIndex ]; return S_OK; } HRESULT IDirect3DDevice9::CreateOffscreenPlainSurface(UINT Width,UINT Height,D3DFORMAT Format,D3DPOOL Pool,IDirect3DSurface9** ppSurface,VD3DHANDLE* pSharedHandle) { // set surf->m_restype to D3DRTYPE_SURFACE... // this is almost identical to CreateRenderTarget.. HRESULT result = S_OK; 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, "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) { HRESULT result = S_OK; 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 | 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, "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) { 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) { pNewZStencil->AddRef(1, "+A SetDepthStencilSurface private addref"); } if (m_dsSurface) { m_dsSurface->Release(1, "-A SetDepthStencilSurface private release"); // do not do a Release here.. } if (m_dsSurface != pNewZStencil) { GLMPRINTF(("-F- --> attaching depthstencil %8x on drawing FBO (%8x)", pNewZStencil, m_drawableFBO)); m_dsSurface = pNewZStencil; // aka FBO attach GLMFBOTexAttachParams depthParams; memset( &depthParams, 0, sizeof(depthParams) ); // NULL is OK - it means unbind the depth buffer depthParams.m_tex = (pNewZStencil) ? pNewZStencil->m_tex : NULL; depthParams.m_face = 0; depthParams.m_mip = 0; depthParams.m_zslice= 0; // brute force baby // clear old attachments in all D/S categories m_drawableFBO->TexDetach( kAttStencil ); m_drawableFBO->TexDetach( kAttDepth ); m_drawableFBO->TexDetach( kAttDepthStencil ); // select dest for new attachment if (depthParams.m_tex!=NULL) { EGLMFBOAttachment destAttach = (depthParams.m_tex->m_layout->m_format->m_glDataFormat != 34041) ? kAttDepth : kAttDepthStencil; m_drawableFBO->TexAttach( &depthParams, destAttach ); // attach(NULL) is allowed to mean "detach". } } else { GLMPRINTF(("-F- --> no change")); } return result; } HRESULT IDirect3DDevice9::GetDepthStencilSurface(IDirect3DSurface9** ppZStencilSurface) { if ( !ppZStencilSurface ) { return D3DERR_INVALIDCALL; } if ( !m_dsSurface ) { *ppZStencilSurface = NULL; return D3DERR_NOTFOUND; } m_dsSurface->AddRef(0, "+B GetDepthStencilSurface public addref"); // per http://msdn.microsoft.com/en-us/library/bb174384(VS.85).aspx *ppZStencilSurface = m_dsSurface; return S_OK; } HRESULT IDirect3DDevice9::GetRenderTargetData(IDirect3DSurface9* pRenderTarget,IDirect3DSurface9* pDestSurface) { // 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) { Debugger(); return S_OK; } HRESULT IDirect3DDevice9::StretchRect(IDirect3DSurface9* pSourceSurface,CONST RECT* pSourceRect,IDirect3DSurface9* pDestSurface,CONST RECT* pDestRect,D3DTEXTUREFILTERTYPE Filter) { // find relevant slices in GLM tex 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 ) { if ( !pName ) return 0; if ( bPixelShader ) { if ( V_stristr( pName, "lightmappedpaint_ps" ) || V_stristr( pName, "lightmappedgeneric_ps" ) || V_stristr( pName, "flashlight_ps" ) ) { return (0x01 << 1); // iterator 1 } else if ( V_stristr( pName, "shadow_ps" ) ) { return (0x01 << 1) | (0x01 << 2) | (0x01 << 3); // iterators 1, 2 and 3 } else if ( V_stristr( pName, "water_ps" ) ) { return (0x01 << 5) | (0x01 << 6) | (0x01 << 7); // iterators 5, 6 and 7 } } else // vertex shader { // Vertex shaders also if ( V_stristr( pName, "lightmappedgeneric_vs" ) || V_stristr( pName, "flashlight_vs" ) ) { return (0x01 << 1); // iterator 1 } else if ( V_stristr( pName, "shadow_vs" ) ) { return (0x01 << 1) | (0x01 << 2) | (0x01 << 3); // iterators 1, 2 and 3 } else if ( V_stristr( pName, "water_vs" ) ) { return (0x01 << 5) | (0x01 << 6) | (0x01 << 7); // iterators 5, 6 and 7 } } // 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 ShadowDepthSamplerFromName( const char *pName ) { if ( !pName ) return -1; if ( V_stristr( pName, "water_ps" ) ) { return 7; } else if ( V_stristr( pName, "phong_ps" ) ) { return 4; } else if ( V_stristr( pName, "vertexlit_and_unlit_generic_bump_ps" ) ) { return 8; } else if ( V_stristr( pName, "vertexlit_and_unlit_generic_ps" ) ) { return 8; } else if ( V_stristr( pName, "eye_refract_ps" ) ) { return 6; } else if ( V_stristr( pName, "eyes_flashlight_ps" ) ) { return 4; } else if ( V_stristr( pName, "worldtwotextureblend_ps" ) ) { return 7; } else if ( V_stristr( pName, "teeth_flashlight_ps" ) ) { return 2; } else if ( V_stristr( pName, "flashlight_ps" ) ) // substring of above, make sure this comes last!! { return 7; } // This shader doesn't have a shadow depth map sampler return -1; } #pragma mark ----- Pixel Shaders - (IDirect3DDevice9) HRESULT IDirect3DDevice9::CreatePixelShader(CONST DWORD* pFunction,IDirect3DPixelShader9** ppShader, const char *pShaderName, char *pDebugLabel) { HRESULT result = D3DERR_INVALIDCALL; *ppShader = NULL; int nShadowDepthSampler = ShadowDepthSamplerFromName( pShaderName ); uint32 nCentroidMask = CentroidMaskFromName( true, pShaderName ); if ( g_bUseControlFlow || !m_ctx->Caps().m_hasDualShaders ) { // either having control-flow 'on' or -glmdualshaders 'off' disqualifies ARB assembler mode g_useASMTranslations = false; } if ( ! (g_useASMTranslations || g_useGLSLTranslations) ) { Assert(!"Must set at least one translation option.."); *ppShader = NULL; return -1; } else { int numTranslations = (g_useASMTranslations!=0) + (g_useGLSLTranslations!=0); 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 ); if ( g_useASMTranslations ) { // no extra tag needed for ARBfp, just use the !!ARBfp marker tempbuf.EnsureCapacity( maxTranslationSize ); g_D3DToOpenGLTranslatorASM.TranslateShader( (uint32 *) pFunction, &tempbuf, &bVertexShader, D3DToGL_OptionUseEnvParams, nShadowDepthSampler, 0, pDebugLabel ); // grow to encompass... transbuf.PutString ( (char*)tempbuf.Base() ); transbuf.PutString( "\n\n" ); // whitespace } if ( g_useGLSLTranslations ) { 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_OptionGLSL | D3DToGL_OptionUseEnvParams; // 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; } } } if (m_ctx->Caps().m_hasBindableUniforms) { glslPixelShaderOptions |= D3DToGL_OptionUseBindableUniforms; } g_D3DToOpenGLTranslatorGLSL.TranslateShader( (uint32 *) pFunction, &tempbuf, &bVertexShader, glslPixelShaderOptions, nShadowDepthSampler, 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 { IDirect3DPixelShader9 *newprog = new IDirect3DPixelShader9; newprog->m_pixProgram = m_ctx->NewProgram( kGLMFragmentProgram, (char *)transbuf.Base() ) ; 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 } else { Assert(!"couldn't find sampler map in pixel shader"); } *ppShader = newprog; result = S_OK; } } return result; } IDirect3DPixelShader9::~IDirect3DPixelShader9() { 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::SetPixelShader(IDirect3DPixelShader9* pShader) { if (pShader) { m_ctx->SetDrawingProgram( kGLMFragmentProgram, pShader->m_pixProgram ); } else { m_ctx->SetDrawingProgram( kGLMFragmentProgram, NULL ); } m_pixelShader = pShader; return S_OK; } HRESULT IDirect3DDevice9::SetPixelShaderConstantF(UINT StartRegister,CONST float* pConstantData,UINT Vector4fCount) { m_ctx->SetProgramParametersF( kGLMFragmentProgram, StartRegister, (float *)pConstantData, Vector4fCount ); return S_OK; } HRESULT IDirect3DDevice9::SetPixelShaderConstantB(UINT StartRegister,CONST BOOL* pConstantData,UINT BoolCount) { GLMPRINTF(("-X- Ignoring IDirect3DDevice9::SetPixelShaderConstantB call, count was %d", BoolCount )); // actually no way to do this yet. // m_ctx->SetProgramParametersB( kGLMFragmentProgram, StartRegister, pConstantData, BoolCount ); return S_OK; } HRESULT IDirect3DDevice9::SetPixelShaderConstantI(UINT StartRegister,CONST int* pConstantData,UINT Vector4iCount) { GLMPRINTF(("-X- Ignoring IDirect3DDevice9::SetPixelShaderConstantI call, count was %d", Vector4iCount )); // m_ctx->SetProgramParametersI( kGLMFragmentProgram, StartRegister, pConstantData, Vector4iCount ); return S_OK; } #pragma mark ----- Vertex Shaders - (IDirect3DDevice9) HRESULT IDirect3DDevice9::CreateVertexShader(CONST DWORD* pFunction, IDirect3DVertexShader9** ppShader, const char *pShaderName, char *pDebugLabel) { HRESULT result = D3DERR_INVALIDCALL; *ppShader = NULL; uint32 nCentroidMask = CentroidMaskFromName( false, pShaderName ); if ( g_bUseControlFlow || !m_ctx->Caps().m_hasDualShaders ) { // either having control-flow 'on' or -glmdualshaders 'off' disqualifies ARB assembler mode g_useASMTranslations = false; } if ( ! (g_useASMTranslations || g_useGLSLTranslations) ) { Assert(!"Must set at least one translation option.."); *ppShader = NULL; return -1; } else { int numTranslations = (g_useASMTranslations!=0) + (g_useGLSLTranslations!=0); 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 ); if ( g_useASMTranslations ) { // no extra tag needed for ARBvp, just use the !!ARBvp marker tempbuf.EnsureCapacity( maxTranslationSize ); uint asmTransOptions = D3DToGL_OptionUseEnvParams | D3DToGL_OptionDoFixupZ | D3DToGL_OptionDoFixupY; // D3DToGL_OptionDoUserClipPlanes not being set for asm yet, it generates NV VP 2.. g_D3DToOpenGLTranslatorASM.TranslateShader( (uint32 *) pFunction, &tempbuf, &bVertexShader, asmTransOptions, -1, 0, pDebugLabel ); // grow to encompass... transbuf.PutString ( (char*)tempbuf.Base() ); transbuf.PutString( "\n\n" ); // whitespace } if ( g_useGLSLTranslations ) { 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_OptionGLSL | D3DToGL_OptionUseEnvParams | D3DToGL_OptionDoFixupZ | D3DToGL_OptionDoFixupY; if ( g_bUseControlFlow ) { glslVertexShaderOptions |= D3DToGL_OptionAllowStaticControlFlow; } 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 (m_ctx->Caps().m_hasBindableUniforms) { glslVertexShaderOptions |= D3DToGL_OptionUseBindableUniforms; } 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 { IDirect3DVertexShader9 *newprog = new IDirect3DVertexShader9; newprog->m_device = this; newprog->m_vtxProgram = m_ctx->NewProgram( kGLMVertexProgram, (char *)transbuf.Base() ) ; // 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"); } // 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 ); 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) { Debugger(); } } else { // probably an 'xx'... check if ( (dataItem[0] != 'x') || (dataItem[1] != 'x') ) { Debugger(); // bad news } else { value = 0xBB; // not likely to see one of these... "fog with usage index 11" } } newprog->m_vtxAttribMap[i] = value; } } else { Debugger(); // that's bad... } *ppShader = newprog; result = S_OK; } } return result; } IDirect3DVertexShader9::~IDirect3DVertexShader9() { 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::SetVertexShader(IDirect3DVertexShader9* pShader) { if (pShader) { m_ctx->SetDrawingProgram( kGLMVertexProgram, pShader->m_vtxProgram ); } else { m_ctx->SetDrawingProgram( kGLMVertexProgram, NULL ); } m_vertexShader = pShader; return S_OK; } HRESULT IDirect3DDevice9::SetVertexShaderConstantF(UINT StartRegister,CONST float* pConstantData,UINT Vector4fCount) // groups of 4 floats! { m_ctx->SetProgramParametersF( kGLMVertexProgram, StartRegister, (float *)pConstantData, Vector4fCount ); return S_OK; } HRESULT IDirect3DDevice9::SetVertexShaderConstantB(UINT StartRegister,CONST BOOL* pConstantData,UINT BoolCount) // individual bool count! { m_ctx->SetProgramParametersB( kGLMVertexProgram, StartRegister, (int *)pConstantData, BoolCount ); return S_OK; } HRESULT IDirect3DDevice9::SetVertexShaderConstantI(UINT StartRegister,CONST int* pConstantData,UINT Vector4iCount) // groups of 4 ints! { m_ctx->SetProgramParametersI( kGLMVertexProgram, StartRegister, (int *)pConstantData, Vector4iCount ); return S_OK; } #pragma mark ----- Shader Pairs - (IDirect3DDevice9) // callers need to ifdef POSIX this, because this method does not exist on real DX9 HRESULT IDirect3DDevice9::LinkShaderPair( IDirect3DVertexShader9* vs, IDirect3DPixelShader9* ps ) { // 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; } // callers need to ifdef POSIX this, because this method does not exist on real DX9 // HRESULT IDirect3DDevice9::QueryShaderPair( int index, GLMShaderPairInfo *infoOut ) { // these are really GLSL "shaders" not "programs" ... m_ctx->QueryShaderPair( index, infoOut ); return S_OK; } #pragma mark ----- Vertex Buffers and Vertex Declarations - (IDirect3DDevice9) HRESULT IDirect3DDevice9::CreateVertexDeclaration(CONST D3DVERTEXELEMENT9* pVertexElements,IDirect3DVertexDeclaration9** ppDecl) { *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 ) ); IDirect3DVertexDeclaration9 *decl9 = new IDirect3DVertexDeclaration9; 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_datasize = 1; elem->m_gldecl.m_datatype = GL_FLOAT; elem->m_gldecl.m_normalized=0; bytes = 4; break; case D3DDECLTYPE_FLOAT2: elem->m_gldecl.m_datasize = 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_datasize = 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_datasize = 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: // 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_datasize = 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_datasize = GL_BGRA; elem->m_gldecl.m_datatype = GL_UNSIGNED_BYTE; } elem->m_gldecl.m_normalized = (elem->m_dxdecl.Type == D3DDECLTYPE_D3DCOLOR); 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_datasize = 2; elem->m_gldecl.m_datatype = GL_UNSIGNED_SHORT; elem->m_gldecl.m_normalized = 0; bytes = 4; break; default: Debugger(); 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_buffer = 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( int j=0; j< decl9->m_elemCount; j++) { D3DVERTEXELEMENT9_GL *elem = &decl9->m_elements[ j ]; elem->m_gldecl.m_stride = streamOffsets[ elem->m_dxdecl.Stream ]; } *ppDecl = decl9; return S_OK; } IDirect3DVertexDeclaration9::~IDirect3DVertexDeclaration9() { GLMPRINTF(("-A- ~IDirect3DVertexDeclaration9 signpost")); } HRESULT IDirect3DDevice9::SetVertexDeclaration(IDirect3DVertexDeclaration9* pDecl) { // we just latch it. At draw time we combine the current vertex decl with the current stream set and generate a vertex setup for GLM. // GLM can see what the differences are and act accordingly to adjust vert attrib bindings. m_vertDecl = pDecl; return S_OK; } HRESULT IDirect3DDevice9::SetFVF(DWORD FVF) { Debugger(); } HRESULT IDirect3DDevice9::GetFVF(DWORD* pFVF) { Debugger(); } #pragma mark ----- Vertex Buffers and Streams - (IDirect3DDevice9) #pragma mark ----- Create function moved to be adjacent to other buffer methods HRESULT IDirect3DDevice9::SetStreamSource(UINT StreamNumber,IDirect3DVertexBuffer9* pStreamData,UINT OffsetInBytes,UINT Stride) { // 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_name: -1, OffsetInBytes, Stride)); if (pStreamData) { m_streams[ StreamNumber ].m_vtxBuffer = pStreamData; m_streams[ StreamNumber ].m_offset = OffsetInBytes; m_streams[ StreamNumber ].m_stride = Stride; } else { m_streams[ StreamNumber ].m_vtxBuffer = NULL; m_streams[ StreamNumber ].m_offset = 0; m_streams[ StreamNumber ].m_stride = 0; } } #pragma mark ----- Index Buffers - (IDirect3DDevice9) #pragma mark ----- Creatue function relocated to be adjacent to the rest of the index buffer methods HRESULT IDirect3DDevice9::SetIndices(IDirect3DIndexBuffer9* pIndexData) { // just latch it. m_indices.m_idxBuffer = pIndexData; } #pragma mark ----- Release Handlers - (IDirect3DDevice9) void IDirect3DDevice9::ReleasedTexture( IDirect3DBaseTexture9 *baseTex ) { // see if this texture is referenced in any of the texture units and scrub it if so. for( int i=0; i<16; 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::ReleasedSurface( IDirect3DSurface9 *surface ) { for( int i=0; i<16; i++) { if (m_rtSurfaces[i]==surface) { // this was a surprise release... scrub it m_rtSurfaces[i] = NULL; GLMPRINTF(( "-A- Scrubbed surface %08x from m_rtSurfaces[%d]", surface, i )); } } if( m_dsSurface == surface ) { m_dsSurface = NULL; GLMPRINTF(( "-A- Scrubbed surface %08x from m_dsSurface", surface )); } if ( m_defaultColorSurface == surface ) { m_defaultColorSurface = NULL; GLMPRINTF(( "-A- Scrubbed surface %08x from m_defaultColorSurface", surface )); } if ( m_defaultDepthStencilSurface == surface ) { m_defaultDepthStencilSurface = NULL; GLMPRINTF(( "-A- Scrubbed surface %08x from m_defaultDepthStencilSurface", surface )); } } void IDirect3DDevice9::ReleasedPixelShader( IDirect3DPixelShader9 *pixelShader ) { if ( m_pixelShader == pixelShader ) { m_pixelShader = NULL; GLMPRINTF(( "-A- Scrubbed pixel shader %08x from m_pixelShader", pixelShader )); } } void IDirect3DDevice9::ReleasedVertexShader( IDirect3DVertexShader9 *vertexShader ) { if ( m_vertexShader == vertexShader ) { m_vertexShader = NULL; GLMPRINTF(( "-A- Scrubbed vertex shader %08x from m_vertexShader", vertexShader )); } } 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; GLMPRINTF(( "-A- Scrubbed vertex buffer %08x from m_streams[%d]", vertexBuffer, i )); } } } 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 )); } } void IDirect3DDevice9::ReleasedQuery( IDirect3DQuery9 *query ) { // nothing to do yet.. } #pragma mark ----- Queries - (IDirect3DDevice9) // 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) { if (m_ctx->Caps().m_hasOcclusionQuery) { IDirect3DQuery9 *newquery = new IDirect3DQuery9; newquery->m_device = this; newquery->m_type = Type; newquery->m_ctx = m_ctx; 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() { 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")); } #pragma mark ----- Render States - (IDirect3DDevice9) 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" }; #define D3DRS_VALUE_LIMIT 210 bool g_D3DRS_INFO_unpacked_ready = false; // set to true after unpack 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 Debugger(); } else { // dispatch it to the unpacked array g_D3DRS_INFO_unpacked[ packed->m_state ] = *packed; } } } // convenience functions GLenum D3DCompareFuncToGL( DWORD function ) { switch ( function ) { case D3DCMP_NEVER : return GL_NEVER; // Always fail the test. case D3DCMP_LESS : return GL_LESS; // Accept the new pixel if its value is less than the value of the current pixel. case D3DCMP_EQUAL : return GL_EQUAL; // Accept the new pixel if its value equals the value of the current pixel. case D3DCMP_LESSEQUAL : return GL_LEQUAL; // Accept the new pixel if its value is less than or equal to the value of the current pixel. ** case D3DCMP_GREATER : return GL_GREATER; // Accept the new pixel if its value is greater than the value of the current pixel. case D3DCMP_NOTEQUAL : return GL_NOTEQUAL; // Accept the new pixel if its value does not equal the value of the current pixel. case D3DCMP_GREATEREQUAL: return GL_GEQUAL; // Accept the new pixel if its value is greater than or equal to the value of the current pixel. case D3DCMP_ALWAYS : return GL_ALWAYS; // Always pass the test. default : Debugger(); return 0xFFFFFFFF; } } static GLenum D3DStencilOpToGL( DWORD operation ) { switch( operation ) { case D3DSTENCILOP_KEEP : return GL_KEEP; case D3DSTENCILOP_ZERO : return GL_ZERO; case D3DSTENCILOP_REPLACE : return GL_REPLACE; case D3DSTENCILOP_INCRSAT : return GL_INCR; case D3DSTENCILOP_DECRSAT : return GL_DECR; case D3DSTENCILOP_INVERT : return GL_INVERT; case D3DSTENCILOP_INCR : return GL_INCR_WRAP_EXT; case D3DSTENCILOP_DECR : return GL_DECR_WRAP_EXT; default : Debugger(); return 0xFFFFFFFF; } } static GLenum D3DBlendFactorToGL( DWORD equation ) { switch (equation) { case D3DBLEND_ZERO : return GL_ZERO; // Blend factor is (0, 0, 0, 0). case D3DBLEND_ONE : return GL_ONE; // Blend factor is (1, 1, 1, 1). case D3DBLEND_SRCCOLOR : return GL_SRC_COLOR; // Blend factor is (Rs, Gs, Bs, As). case D3DBLEND_INVSRCCOLOR : return GL_ONE_MINUS_SRC_COLOR; // Blend factor is (1 - Rs, 1 - Gs, 1 - Bs, 1 - As). case D3DBLEND_SRCALPHA : return GL_SRC_ALPHA; // Blend factor is (As, As, As, As). case D3DBLEND_INVSRCALPHA : return GL_ONE_MINUS_SRC_ALPHA; // Blend factor is ( 1 - As, 1 - As, 1 - As, 1 - As). case D3DBLEND_DESTALPHA : return GL_DST_ALPHA; // Blend factor is (Ad Ad Ad Ad). case D3DBLEND_INVDESTALPHA : return GL_ONE_MINUS_DST_ALPHA; // Blend factor is (1 - Ad 1 - Ad 1 - Ad 1 - Ad). case D3DBLEND_DESTCOLOR : return GL_DST_COLOR; // Blend factor is (Rd, Gd, Bd, Ad). case D3DBLEND_INVDESTCOLOR : return GL_ONE_MINUS_DST_COLOR; // Blend factor is (1 - Rd, 1 - Gd, 1 - Bd, 1 - Ad). case D3DBLEND_SRCALPHASAT : return GL_SRC_ALPHA_SATURATE; // Blend factor is (f, f, f, 1); where f = min(As, 1 - Ad). /* // these are weird.... break if we hit them case D3DBLEND_BOTHSRCALPHA : Assert(0); return GL_ZERO; // Obsolete. Starting with DirectX 6, you can achieve the same effect by setting the source and destination blend factors to D3DBLEND_SRCALPHA and D3DBLEND_INVSRCALPHA in separate calls. case D3DBLEND_BOTHINVSRCALPHA: Assert(0); return GL_ZERO; // Source blend factor is (1 - As, 1 - As, 1 - As, 1 - As), and destination blend factor is (As, As, As, As); the destination blend selection is overridden. This blend mode is supported only for the D3DRS_SRCBLEND render state. case D3DBLEND_BLENDFACTOR : Assert(0); return GL_ZERO; // Constant color blending factor used by the frame-buffer blender. This blend mode is supported only if D3DPBLENDCAPS_BLENDFACTOR is set in the SrcBlendCaps or DestBlendCaps members of D3DCAPS9. dxabstract.h has not heard of these, so let them hit the debugger if they come through case D3DBLEND_INVBLENDFACTOR: //Inverted constant color-blending factor used by the frame-buffer blender. This blend mode is supported only if the D3DPBLENDCAPS_BLENDFACTOR bit is set in the SrcBlendCaps or DestBlendCaps members of D3DCAPS9. case D3DBLEND_SRCCOLOR2: // Blend factor is (PSOutColor[1]r, PSOutColor[1]g, PSOutColor[1]b, not used). This flag is available in Direct3D 9Ex only. case D3DBLEND_INVSRCCOLOR2: // Blend factor is (1 - PSOutColor[1]r, 1 - PSOutColor[1]g, 1 - PSOutColor[1]b, not used)). This flag is available in Direct3D 9Ex only. */ default: Debugger(); return 0xFFFFFFFF; break; } } static GLenum D3DBlendOperationToGL( DWORD operation ) { switch (operation) { case D3DBLENDOP_ADD : return GL_FUNC_ADD; // The result is the destination added to the source. Result = Source + Destination /* not covered by dxabstract.h.. case D3DBLENDOP_SUBTRACT : return GL_FUNC_SUBTRACT; // The result is the destination subtracted from to the source. Result = Source - Destination case D3DBLENDOP_REVSUBTRACT : return GL_FUNC_REVERSE_SUBTRACT; // The result is the source subtracted from the destination. Result = Destination - Source case D3DBLENDOP_MIN : return GL_MIN; // The result is the minimum of the source and destination. Result = MIN(Source, Destination) case D3DBLENDOP_MAX : return GL_MAX; // The result is the maximum of the source and destination. Result = MAX(Source, Destination) */ default: Debugger(); return 0xFFFFFFFF; break; } } HRESULT IDirect3DDevice9::SetRenderState( D3DRENDERSTATETYPE State, DWORD Value ) { char rsSpew = 1; char ignored = 0; if (!g_D3DRS_INFO_unpacked_ready) { UnpackD3DRSITable(); g_D3DRS_INFO_unpacked_ready = true; } if (State >= D3DRS_VALUE_LIMIT) { Debugger(); // bad } else { D3D_RSINFO *info = &g_D3DRS_INFO_unpacked[ State ]; if (info->m_state != State) { Debugger(); // bad - we never set up that state in our list } 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) Debugger(); } } 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) Debugger(); } // fall through to mode 3 case 3: // full GL response, support any legal value // note we're handling the class-2's as well. switch(State) { case D3DRS_ZENABLE: // kGLDepthTestEnable gl.m_DepthTestEnable.enable = Value; gl.m_stateDirtyMask |= (1< GL blend equation { GLenum equation = D3DBlendOperationToGL( Value ); gl.m_BlendEquation.equation = equation; gl.m_stateDirtyMask |= (1< 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; } gl.m_stateDirtyMask |= (1< 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 ignored = 1; break; case D3DRS_FOGENABLE: // none of these are implemented yet... erk gl.m_FogEnable = (Value != 0); GLMPRINTF(("-D- fogenable = %d",Value )); //ignored = 1; break; case D3DRS_FOGCOLOR: case D3DRS_FOGTABLEMODE: case D3DRS_FOGSTART: case D3DRS_FOGEND: case D3DRS_FOGDENSITY: case D3DRS_RANGEFOGENABLE: case D3DRS_FOGVERTEXMODE: ignored = 1; break; case D3DRS_MULTISAMPLEANTIALIAS: case D3DRS_MULTISAMPLEMASK: ignored = 1; break; case D3DRS_SCISSORTESTENABLE: // kGLScissorEnable { gl.m_ScissorEnable.enable = Value; gl.m_stateDirtyMask |= (1<m_addressModes[ Type - (int)D3DSAMP_ADDRESSU ] = (D3DTEXTUREADDRESS)Value; break; case D3DSAMP_BORDERCOLOR: samp->m_borderColor = Value; break; case D3DSAMP_MAGFILTER: samp->m_magFilter = (D3DTEXTUREFILTERTYPE)Value; break; case D3DSAMP_MINFILTER: samp->m_minFilter = (D3DTEXTUREFILTERTYPE)Value; break; case D3DSAMP_MIPFILTER: samp->m_mipFilter = (D3DTEXTUREFILTERTYPE)Value; break; case D3DSAMP_MIPMAPLODBIAS: samp->m_mipmapBias = Value; break; // float in sheep's clothing - check this one out case D3DSAMP_MAXMIPLEVEL: samp->m_maxMipLevel = Value; break; case D3DSAMP_MAXANISOTROPY: samp->m_maxAniso = Value; break; case D3DSAMP_SRGBTEXTURE: samp->m_srgb = Value; break; case D3DSAMP_SHADOWFILTER: samp->m_shadowFilter = Value; break; default: Assert(!"Unknown sampler parameter"); break; } gl.m_samplerDirtyMask |= (1<WriteAlphaTestEnable( &gl.m_AlphaTestEnable ); if ( stateHitMask & (1<WriteAlphaTestFunc( &gl.m_AlphaTestFunc ); if ( stateHitMask & (1<WriteCullFaceEnable( &gl.m_CullFaceEnable ); if ( stateHitMask & (1<WriteCullFrontFace( &gl.m_CullFrontFace ); if ( stateHitMask & (1<WritePolygonMode( &gl.m_PolygonMode ); if ( stateHitMask & (1<WriteDepthBias( &gl.m_DepthBias ); if ( stateHitMask & (1<WriteScissorEnable( &gl.m_ScissorEnable ); if ( stateHitMask & (1<WriteScissorBox( &gl.m_ScissorBox ); if ( stateHitMask & (1<WriteViewportBox( &gl.m_ViewportBox ); if ( stateHitMask & (1<WriteViewportDepthRange( &gl.m_ViewportDepthRange ); if ( stateHitMask & (1<WriteClipPlaneEnable( &gl.m_ClipPlaneEnable[x], x ); } } if ( stateHitMask & (1<Caps().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 )); } */ } } } if ( stateHitMask & (1<WriteColorMaskSingle( &gl.m_ColorMaskSingle ); // if ( stateHitMask & (1<WriteColorMaskMultiple( &gl.m_ColorMaskMultiple ); // ???????????? hmmmmmmmm if ( stateHitMask & (1<WriteBlendEnable( &gl.m_BlendEnable ); if ( stateHitMask & (1<WriteBlendFactor( &gl.m_BlendFactor ); if ( stateHitMask & (1<WriteBlendEquation( &gl.m_BlendEquation ); if ( stateHitMask & (1<WriteBlendColor( &gl.m_BlendColor ); if ( stateHitMask & (1<WriteBlendEnableSRGB( &gl.m_BlendEnableSRGB ); if ( stateHitMask & (1<WriteDepthTestEnable( &gl.m_DepthTestEnable ); if ( stateHitMask & (1<WriteDepthFunc( &gl.m_DepthFunc ); if ( stateHitMask & (1<WriteDepthMask( &gl.m_DepthMask ); if ( stateHitMask & (1<WriteStencilTestEnable( &gl.m_StencilTestEnable ); if ( stateHitMask & (1<WriteStencilFunc( &gl.m_StencilFunc ); if ( stateHitMask & (1<WriteStencilOp( &gl.m_StencilOp,0 ); m_ctx->WriteStencilOp( &gl.m_StencilOp,1 ); // ********* need to recheck this } if ( stateHitMask & (1<WriteStencilWriteMask( &gl.m_StencilWriteMask ); if ( stateHitMask & (1<WriteClearColor( &gl.m_ClearColor ); if ( stateHitMask & (1<WriteClearDepth( &gl.m_ClearDepth ); if ( stateHitMask & (1<WriteClearStencil( &gl.m_ClearStencil ); gl.m_stateDirtyMask &= (~stateHitMask); } // addressing modes // 1 D3DTADDRESS_WRAP Tile the texture at every integer junction. // D3DTADDRESS_MIRROR Similar to D3DTADDRESS_WRAP, except that the texture is flipped at every integer junction. // 3 D3DTADDRESS_CLAMP Texture coordinates outside the range [0.0, 1.0] are set to the texture color at 0.0 or 1.0, respectively. // 4 D3DTADDRESS_BORDER Texture coordinates outside the range [0.0, 1.0] are set to the border color. // D3DTADDRESS_MIRRORONCE Similar to D3DTADDRESS_MIRROR and D3DTADDRESS_CLAMP. // Takes the absolute value of the texture coordinate (thus, mirroring around 0), // and then clamps to the maximum value. The most common usage is for volume textures, // where support for the full D3DTADDRESS_MIRRORONCE texture-addressing mode is not // necessary, but the data is symmetric around the one axis. static GLenum dxtogl_addressMode[] = { (GLenum)-1, // no zero entry GL_REPEAT, // from D3DTADDRESS_WRAP (GLenum)-1, // no D3DTADDRESS_MIRROR support GL_CLAMP_TO_EDGE, // from D3DTADDRESS_CLAMP GL_CLAMP, // from D3DTADDRESS_BORDER (GLenum)-1, // no D3DTADDRESS_MIRRORONCE support }; /* _D3DTEXTUREFILTERTYPE: D3DTEXF_NONE = 0, // filtering disabled (valid for mip filter only) D3DTEXF_POINT = 1, // nearest D3DTEXF_LINEAR = 2, // linear interpolation D3DTEXF_ANISOTROPIC = 3, // anisotropic */ static GLenum dxtogl_magFilter[4] = // indexed by _D3DTEXTUREFILTERTYPE { GL_NEAREST, // D3DTEXF_NONE not applicable to mag filter but we handle it like POINT (mat_showmiplevels hits this) GL_NEAREST, // D3DTEXF_POINT GL_LINEAR, // D3DTEXF_LINEAR GL_LINEAR, // D3DTEXF_ANISOTROPIC (aniso will be driven by setting maxAniso, not by a GL filter mode) }; static GLenum dxtogl_minFilter[4][4] = // indexed by _D3DTEXTUREFILTERTYPE on both axes: [row is min filter][col is mip filter]. { /* mip filter ---------------> D3DTEXF_NONE D3DTEXF_POINT D3DTEXF_LINEAR (D3DTEXF_ANISOTROPIC not applicable to mip filter) /* min = D3DTEXF_NONE */ { GL_NEAREST, GL_NEAREST_MIPMAP_NEAREST, GL_NEAREST_MIPMAP_LINEAR, (GLenum)-1 }, // D3DTEXF_NONE we just treat like POINT /* min = D3DTEXF_POINT */ { GL_NEAREST, GL_NEAREST_MIPMAP_NEAREST, GL_NEAREST_MIPMAP_LINEAR, (GLenum)-1 }, /* min = D3DTEXF_LINEAR */ { GL_LINEAR, GL_LINEAR_MIPMAP_NEAREST, GL_LINEAR_MIPMAP_LINEAR, (GLenum)-1 }, /* min = D3DTEXF_ANISOTROPIC */ { GL_LINEAR, GL_LINEAR_MIPMAP_NEAREST, GL_LINEAR_MIPMAP_LINEAR, (GLenum)-1 }, // no diff from prior row, set maxAniso to effect the sampling }; HRESULT IDirect3DDevice9::FlushSamplers() { // a minor optimization we could do here would be to only write sampler state for // TMU's that are active (i.e. consult m_textures) uint activeSamplerMask = m_pixelShader ? m_pixelShader->m_pixSamplerMask : 0; // if no pixel shader bound at time of draw, act like it references no samplers // (and avoid an access violation while yer at it) // ho, we're not clearing the dirty mask for samplers as we go... need to do that... uint samplerHitMask = gl.m_samplerDirtyMask; for( int index = 0; (index < 16) && (samplerHitMask !=0); index++) { uint nCurrentSamplerMask = 1<m_addressModes[0] = dxtogl_addressMode[ dxsamp->m_addressModes[0] ]; glsamp->m_addressModes[1] = dxtogl_addressMode[ dxsamp->m_addressModes[1] ]; glsamp->m_addressModes[2] = dxtogl_addressMode[ dxsamp->m_addressModes[2] ]; // Border color uint dxcolor = dxsamp->m_borderColor; glsamp->m_borderColor[0] = ((dxcolor >> 16) & 0xFF) / 255.0f; //R glsamp->m_borderColor[1] = ((dxcolor >> 8) & 0xFF) / 255.0f; //G glsamp->m_borderColor[2] = ((dxcolor ) & 0xFF) / 255.0f; //B glsamp->m_borderColor[3] = ((dxcolor >> 24) & 0xFF) / 255.0f; //A // mag filter - pretty easy Assert( dxsamp->m_magFilter <= D3DTEXF_ANISOTROPIC ); Assert( dxsamp->m_magFilter >= D3DTEXF_POINT ); glsamp->m_magFilter = dxtogl_magFilter[ dxsamp->m_magFilter ]; // min filter - more involved Assert( dxsamp->m_minFilter <= D3DTEXF_ANISOTROPIC ); Assert( dxsamp->m_minFilter >= D3DTEXF_POINT ); Assert( dxsamp->m_mipFilter <= D3DTEXF_LINEAR ); Assert( dxsamp->m_mipFilter >= D3DTEXF_NONE ); D3DTEXTUREFILTERTYPE mipFilterLimit = D3DTEXF_LINEAR; /* if (GLMKnob("caps-key",NULL) > 0.0) { if (dxsamp->m_mipFilter > D3DTEXF_NONE) { // evil hack glsamp->m_magFilter = GL_LINEAR_MIPMAP_NEAREST; } } if (GLMKnob("option-key",NULL) > 0.0) { // limit to point mipFilterLimit = D3DTEXF_POINT; } if (GLMKnob("control-key",NULL) > 0.0) { // limit to none mipFilterLimit = D3DTEXF_NONE; } */ D3DTEXTUREFILTERTYPE mipFilterChoice = MIN( dxsamp->m_mipFilter, mipFilterLimit ); glsamp->m_minFilter = dxtogl_minFilter[ dxsamp->m_minFilter ][ mipFilterChoice ]; // should we check for mip filtering being requested on unmipped textures ? does it matter ? // mipmap bias glsamp->m_mipmapBias = dxsamp->m_mipmapBias; // d3d "MAX MIP LEVEL" means the *largest size* MIP that will be selected. (max size) // this is the same as GL's "MIN LOD level" which means the GL_TEXTURE_MIN_LOD level. (min index) int texMipCount = m_textures[index]->m_tex->m_layout->m_mipCount; Assert( texMipCount >=1 ); glsamp->m_minMipLevel = dxsamp->m_maxMipLevel; // it says gl_minMipLevel because we're setting GL's "GL_TEXTURE_MIN_LOD" aka d3d's "maximum mip size index". if (glsamp->m_minMipLevel >= texMipCount) { // clamp - you can't have the GL base tex level be higher than the index of the last mip glsamp->m_minMipLevel = texMipCount - 1; } // d3d has no idea of a "MIN MIP LEVEL" i.e. smallest size allowed. // this would be expressed in GL by setting the GL_TEXTURE_MIN_LOD meaning largest index to select. // for now, just set it to the index of the last mip. glsamp->m_maxMipLevel = texMipCount-1; // d3d has no value for constraining how small we can sample. // however we may need to set this more intelligently if textures are not being fully submitted. // On OpenGL, GL_TEXTURE_MAX_ANISOTROPY_EXT needs to be 1 if we don't want aniso glsamp->m_maxAniso = (dxsamp->m_minFilter != D3DTEXF_ANISOTROPIC) ? 1 : dxsamp->m_maxAniso; // SRGB glsamp->m_srgb = dxsamp->m_srgb != 0; // Shadow compare mode glsamp->m_compareMode = dxsamp->m_shadowFilter ? GL_COMPARE_R_TO_TEXTURE_ARB : GL_NONE; // write that sampler. m_ctx->SetSamplerParams( index, glsamp ); samplerHitMask ^= nCurrentSamplerMask; //turn bit off // finally, if the SRGB state of the sampler does not match the SRGB format of the underlying texture... // ... and the tex is not a renderable... // ... and it is possible to re-submit the tex in an sRGB format... // ******** AND THE TEX IS ACTUALLY REFERENCED BY THE ACTIVE PIXEL SHADER ******* // fix it. // else complain ? if ( nCurrentSamplerMask & activeSamplerMask ) // don't do SRGB check on unreferenced textures. { bool bTexSRGB = (m_textures[index]->m_tex->m_layout->m_key.m_texFlags & kGLMTexSRGB) != 0; bool bMismatch = (bTexSRGB != glsamp->m_srgb); bool bSRGBCapableTex = false; // not yet known bool bRenderableTex = false; // not yet known. if ( bMismatch ) { bSRGBCapableTex = m_textures[index]->m_tex->m_layout->m_format->m_glIntFormatSRGB != 0; bRenderableTex = (m_textures[index]->m_tex->m_layout->m_key.m_texFlags & kGLMTexRenderable) != 0; // we can fix it if it's not a renderable, and an sRGB enabled format variation is available. if ( bSRGBCapableTex && !bRenderableTex ) { char *pTexName = m_textures[index]->m_tex->m_debugLabel; if (!pTexName) pTexName = "-"; m_textures[index]->m_srgbFlipCount++; static bool bCheckedCommandline = false; static bool bPrintAllflips = false; static bool bPrintFirstflips = false; static bool bPrintFreqflips = false; static bool bPrintCrawls = false; static bool bPrintMaxCrawls = false; if ( !bCheckedCommandline ) { bPrintAllflips = CommandLine()->FindParm( "-glmspewallsrgbflips" ); bPrintFirstflips = CommandLine()->FindParm( "-glmspewfirstsrgbflips" ); bPrintFreqflips = CommandLine()->FindParm( "-glmspewfreqsrgbflips" ); bPrintCrawls = CommandLine()->FindParm( "-glmspewsrgbcrawls" ); bPrintMaxCrawls = CommandLine()->FindParm( "-glmspewsrgbmaxcrawls" ); bCheckedCommandline = true; } bool bPrintIt = bPrintAllflips; if ( bPrintFirstflips ) // report on first flip { bPrintIt = bPrintIt || m_textures[index]->m_srgbFlipCount==1; } if ( bPrintFreqflips ) // report on 50th flip { bPrintIt = bPrintIt || m_textures[index]->m_srgbFlipCount==50; } if ( bPrintIt ) { char *pFormatStr; pFormatStr = "srgb change (samp=%d): tex '%-30s' %08x %s (srgb=%d, %d times)"; if ( strlen( pTexName ) >= 30 ) { pFormatStr = "srgb change (samp=%d): tex '%s' %08x %s (srgb=%d, %d times)"; } printf( "\n" ); printf( pFormatStr, index, pTexName, m_textures[index], m_textures[index]->m_tex->m_layout->m_layoutSummary, (int)glsamp->m_srgb, m_textures[index]->m_srgbFlipCount ); if ( bPrintCrawls ) { static char *interesting_crawl_substrs[] = { "CShader::OnDrawElements", NULL }; // add more as needed CStackCrawlParams cp; memset( &cp, 0, sizeof(cp) ); cp.m_frameLimit = 20; g_extCocoaMgr->GetStackCrawl(&cp); for( int i=0; i< cp.m_frameCount; i++) { // for each row of crawl, decide if name is interesting bool bHit = bPrintMaxCrawls; for( char **match = interesting_crawl_substrs; (!bHit) && (*match != NULL); match++) { if (strstr(cp.m_crawlNames[i], *match)) { bHit = true; } } if ( bHit ) { printf( "\n\t%s", cp.m_crawlNames[i] ); } } printf( "\n"); } } #if GLMDEBUG && 0 //"toi" = texture of interest static char s_toi[256] = "colorcorrection"; if (strstr( pTexName, s_toi )) { // breakpoint on this if you like GLMPRINTF(( "srgb change %d for %s", m_textures[index]->m_srgbFlipCount, pTexName )); } #endif // re-submit the tex unless we're stifling it if (!CommandLine()->FindParm( "-glmnosrgbflips" )) { m_textures[index]->m_tex->ResetSRGB( glsamp->m_srgb, false ); } } else { //GLMPRINTF(("-Z- srgb sampling conflict: NOT fixing tex %08x [%s] (srgb req: %d) because (tex-srgb-capable=%d tex-renderable=%d)", m_textures[index], m_textures[index]->m_tex->m_layout->m_layoutSummary, (int)glsamp->m_srgb, (int)bSRGBCapableTex, (int)bRenderableTex )); // we just leave the sampler state where it is, and that's life } } } } } } HRESULT IDirect3DDevice9::FlushIndexBindings( void ) { // push index buffer state m_ctx->SetIndexBuffer( m_indices.m_idxBuffer->m_idxBuffer ); } #if 0 HRESULT IDirect3DDevice9::FlushVertexBindings( void ) { // push vertex buffer state for the current vertex decl GLMVertexSetup setup; IDirect3DVertexDeclaration9 *vxdecl = m_vertDecl; memset( &setup, 0, sizeof( setup ) ); // see if the elems in the vertex decl match the attrib map of the shader we're about to draw with. // can we do this in a simple style that handles both matched and unmatched orderings? // just pick up each elem from the decl. // visit the same slot in the shader attrib map. // if the usage/usageindex matches, you're good. // if not, hunt through the shader attrib map and find it. // if you can't find it, then the shader is not consuming that attribute - odd but not fatal ? // the serious one is shader trying to consume an attrib that isn't being sourced. // we can check for that though with a little more work (copy the shader attrib map and mark the attribs as each one gets satisfied) unsigned char vshAttribMap[ 16 ]; uint activeAttribCount = 0; for( int i=0; i<16; i++) { vshAttribMap[i] = m_vertexShader->m_vtxAttribMap[i]; if (vshAttribMap[i] != 0xBB) { activeAttribCount++; // this counting could be done at shader creation time, or changed to a mask } } for( int elemIndex=0; elemIndexm_elemCount; elemIndex++) { D3DVERTEXELEMENT9_GL *srcelem = &vxdecl->m_elements[elemIndex]; int matchIndex = elemIndex; // initial guess - will iterate if this does not match int tries = 0; // >16 means done bool matched = false; do { if ( ((vshAttribMap[matchIndex] >>4) == srcelem->m_dxdecl.Usage) && ((vshAttribMap[matchIndex] & 0x0F) == srcelem->m_dxdecl.UsageIndex) ) { // hit int attribIndex = matchIndex; int streamIndex = srcelem->m_dxdecl.Stream; GLMVertexAttributeDesc *dstAttr = &setup.m_attrs[ matchIndex ]; // copy whole thing *dstAttr = srcelem->m_gldecl; // then fix buffer, stride, offset dstAttr->m_buffer = m_streams[ streamIndex ].m_vtxBuffer->m_vtxBuffer; dstAttr->m_stride = m_streams[ streamIndex ].m_stride; dstAttr->m_offset += m_streams[ streamIndex ].m_offset; // set mask setup.m_attrMask |= (1<FindParm("-hushasserts") ) { AssertOnce( !"Vertex shader not consuming attribs that are sourced by decl"); } } } if (activeAttribCount >0) { // this one is more serious if ( !CommandLine()->FindParm("-hushasserts") ) { Assert( !"Vertex shader consuming attribs not sourced by decl"); } } // pass the whole shebang to GLM m_ctx->SetVertexAttributes( &setup ); } #endif 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_vertDecl; 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_vertDecl->m_elements; for( int j=0; j< m_vertDecl->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_buffer = 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<m_buffer = 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_datasize = 3; dstAttr->m_datatype = GL_FLOAT; dstAttr->m_normalized = false; break; case D3DDECLUSAGE_PSIZE: Debugger(); break; case D3DDECLUSAGE_TEXCOORD: dstAttr->m_datasize = 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_datasize = 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 ); } HRESULT IDirect3DDevice9::FlushGLM( void ) { Debugger();// old routine not used now } HRESULT IDirect3DDevice9::DrawPrimitive(D3DPRIMITIVETYPE PrimitiveType,UINT StartVertex,UINT PrimitiveCount) { this->FlushStates( 0xFFFFFFFF ); Debugger(); return S_OK; } // 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 accesssing the vertex buffer. Beginning at StartIndex to index vertices from the vertex 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. HRESULT IDirect3DDevice9::DrawIndexedPrimitive( D3DPRIMITIVETYPE Type,INT BaseVertexIndex,UINT MinVertexIndex,UINT NumVertices,UINT startIndex,UINT primCount ) { this->FlushStates( 0xFFFFFFFF ); this->FlushSamplers(); this->FlushIndexBindings( ); this->FlushVertexBindings( BaseVertexIndex ); m_ctx->FlushDrawStates( true ); 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)) ); 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)) ); 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)) ); break; } return S_OK; } HRESULT IDirect3DDevice9::DrawIndexedPrimitiveUP(D3DPRIMITIVETYPE PrimitiveType,UINT MinVertexIndex,UINT NumVertices,UINT PrimitiveCount,CONST void* pIndexData,D3DFORMAT IndexDataFormat,CONST void* pVertexStreamZeroData,UINT VertexStreamZeroStride) { this->FlushStates( 0xFFFFFFFF ); Debugger(); return S_OK; } BOOL IDirect3DDevice9::ShowCursor(BOOL bShow) { // FIXME NOP //Debugger(); 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) { this->FlushStates( (1<FlushDrawStates( false ); //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( int 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) { Debugger(); return S_OK; } HRESULT IDirect3DDevice9::SetTextureStageState(DWORD Stage,D3DTEXTURESTAGESTATETYPE Type,DWORD Value) { Debugger(); return S_OK; } HRESULT IDirect3DDevice9::ValidateDevice(DWORD* pNumPasses) { Debugger(); return S_OK; } HRESULT IDirect3DDevice9::SetMaterial(CONST D3DMATERIAL9* pMaterial) { GLMPRINTF(("-X- IDirect3DDevice9::SetMaterial - ignored.")); // Debugger(); return S_OK; } HRESULT IDirect3DDevice9::LightEnable(DWORD Index,BOOL Enable) { Debugger(); return S_OK; } HRESULT IDirect3DDevice9::SetScissorRect(CONST RECT* pRect) { int nSurfaceHeight = m_drawableFBO->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; gl.m_stateDirtyMask |= (1<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; gl.m_stateDirtyMask |= (1<WriteClipPlaneEquation( &peq, Index ); } return S_OK; } HRESULT IDirect3DDevice9::EvictManagedResources() { GLMPRINTF(("-X- IDirect3DDevice9::EvictManagedResources --> IGNORED")); return S_OK; } HRESULT IDirect3DDevice9::SetLight(DWORD Index,CONST D3DLIGHT9*) { Debugger(); return S_OK; } void IDirect3DDevice9::SetGammaRamp(UINT iSwapChain,DWORD Flags,CONST D3DGAMMARAMP* pRamp) { // just slam it directly for the time being // this code is OS X specific CGTableCount sampleCount; CGDisplayErr cgErr; CGGammaValue redt[256]; CGGammaValue grnt[256]; CGGammaValue blut[256]; for( int i=0; i<256; i++) { redt[i] = ((float)pRamp->red[i]) / 65535.0f; grnt[i] = ((float)pRamp->green[i]) / 65535.0f; blut[i] = ((float)pRamp->blue[i]) / 65535.0f; } cgErr = CGSetDisplayTransferByTable( 0, 256, redt, grnt, blut ); } // ------------------------------------------------------------------------------------------------------------------------------ // void* ID3DXBuffer::GetBufferPointer() { Debugger(); return NULL; } DWORD ID3DXBuffer::GetBufferSize() { Debugger(); return 0; } #pragma mark ----- More D3DX stuff // ------------------------------------------------------------------------------------------------------------------------------ // // D3DX stuff. // ------------------------------------------------------------------------------------------------------------------------------ // // matrix stack... HRESULT D3DXCreateMatrixStack( DWORD Flags, LPD3DXMATRIXSTACK* ppStack) { *ppStack = new ID3DXMatrixStack; (*ppStack)->Create(); return S_OK; } 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. Debugger(); } 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. Debugger(); } 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]; Debugger(); } 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. Debugger(); } 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]; Debugger(); } const char* D3DXGetPixelShaderProfile( IDirect3DDevice9 *pDevice ) { Debugger(); return ""; } 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; } // 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; vOut.x = vOut.y = vOut.z = 0.0f; 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 ) { 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; 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; 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; } *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 ); 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 ) { Debugger(); return NULL; } D3DXMATRIX* D3DXMatrixPerspectiveRH( D3DXMATRIX *pOut, FLOAT w, FLOAT h, FLOAT zn, FLOAT zf ) { Debugger(); return NULL; } D3DXMATRIX* D3DXMatrixPerspectiveOffCenterRH( D3DXMATRIX *pOut, FLOAT l, FLOAT r, FLOAT b, FLOAT t, FLOAT zn, FLOAT zf ) { Debugger(); 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) { Debugger(); // is anyone calling this ? return S_OK; } #endif