//========= Copyright Valve Corporation, All rights reserved. ============// // // Purpose: // // $NoKeywords: $ // //===========================================================================// #define DISABLE_PROTECTED_THINGS #include "togl/rendermechanism.h" #include "TransitionTable.h" #include "recording.h" #include "shaderapidx8.h" #include "shaderapi/ishaderutil.h" #include "tier1/convar.h" #include "materialsystem/imaterialsystemhardwareconfig.h" #include "vertexshaderdx8.h" #include "tier0/vprof.h" #include "shaderdevicedx8.h" // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" enum { TEXTURE_STAGE_BIT_COUNT = 4, TEXTURE_STAGE_MAX_STAGE = 1 << TEXTURE_STAGE_BIT_COUNT, TEXTURE_STAGE_MASK = TEXTURE_STAGE_MAX_STAGE - 1, TEXTURE_OP_BIT_COUNT = 7 - TEXTURE_STAGE_BIT_COUNT, TEXTURE_OP_SHIFT = TEXTURE_STAGE_BIT_COUNT, TEXTURE_OP_MASK = ((1 << TEXTURE_OP_BIT_COUNT) - 1) << TEXTURE_OP_SHIFT, }; //----------------------------------------------------------------------------- // Texture op compressing/uncompressing //----------------------------------------------------------------------------- inline unsigned char TextureOp( TextureStateFunc_t func, int stage ) { // This fails if we've added too many texture stages states to fit in a byte. COMPILE_TIME_ASSERT( TEXTURE_STATE_COUNT < (1 << TEXTURE_OP_BIT_COUNT) ); Assert( stage < TEXTURE_STAGE_MAX_STAGE ); return ((func << TEXTURE_OP_SHIFT) & TEXTURE_OP_MASK) | (stage & TEXTURE_STAGE_MASK); } inline void GetTextureOp( unsigned char nBits, TextureStateFunc_t *pFunc, int *pStage ) { *pStage = (nBits & TEXTURE_STAGE_MASK); *pFunc = (TextureStateFunc_t)((nBits & TEXTURE_OP_MASK) >> TEXTURE_OP_SHIFT); } //----------------------------------------------------------------------------- // Stats //----------------------------------------------------------------------------- static int s_pRenderTransitions[RENDER_STATE_COUNT]; static int s_pTextureTransitions[TEXTURE_STATE_COUNT][TEXTURE_STAGE_MAX_STAGE]; //----------------------------------------------------------------------------- // Singleton //----------------------------------------------------------------------------- CTransitionTable *g_pTransitionTable = NULL; #ifdef DEBUG_BOARD_STATE inline ShadowState_t& BoardState() { return g_pTransitionTable->BoardState(); } #endif inline CTransitionTable::CurrentState_t& CurrentState() { return g_pTransitionTable->CurrentState(); } //----------------------------------------------------------------------------- // Less functions //----------------------------------------------------------------------------- bool CTransitionTable::ShadowStateDictLessFunc::Less( const CTransitionTable::ShadowStateDictEntry_t &src1, const CTransitionTable::ShadowStateDictEntry_t &src2, void *pCtx ) { return src1.m_nChecksum < src2.m_nChecksum; } bool CTransitionTable::SnapshotDictLessFunc::Less( const CTransitionTable::SnapshotDictEntry_t &src1, const CTransitionTable::SnapshotDictEntry_t &src2, void *pCtx ) { return src1.m_nChecksum < src2.m_nChecksum; } bool CTransitionTable::UniqueSnapshotLessFunc::Less( const CTransitionTable::TransitionList_t &src1, const CTransitionTable::TransitionList_t &src2, void *pCtx ) { return src1.m_NumOperations > src2.m_NumOperations; } //----------------------------------------------------------------------------- // Constructor, destructor //----------------------------------------------------------------------------- CTransitionTable::CTransitionTable() : m_DefaultStateSnapshot(-1), m_CurrentShadowId(-1), m_CurrentSnapshotId(-1), m_TransitionOps( 0, 8192 ), m_ShadowStateList( 0, 256 ), m_TransitionTable( 0, 256 ), m_SnapshotList( 0, 256 ), m_ShadowStateDict(0, 256 ), m_SnapshotDict( 0, 256 ), m_UniqueTransitions( 0, 4096 ) { Assert( !g_pTransitionTable ); g_pTransitionTable = this; #ifdef DEBUG_BOARD_STATE memset( &m_BoardState, 0, sizeof( m_BoardState ) ); memset( &m_BoardShaderState, 0, sizeof( m_BoardShaderState ) ); #endif } CTransitionTable::~CTransitionTable() { Assert( g_pTransitionTable == this ); g_pTransitionTable = NULL; } //----------------------------------------------------------------------------- // Initialization, shutdown //----------------------------------------------------------------------------- bool CTransitionTable::Init( ) { return true; } void CTransitionTable::Shutdown( ) { Reset(); } //----------------------------------------------------------------------------- // Creates a shadow, adding an entry into the shadow list and transition table //----------------------------------------------------------------------------- StateSnapshot_t CTransitionTable::CreateStateSnapshot( ShadowStateId_t shadowStateId, const ShadowShaderState_t& currentShaderState ) { StateSnapshot_t snapshotId = m_SnapshotList.AddToTail(); // Copy our snapshot into the list SnapshotShaderState_t &shaderState = m_SnapshotList[snapshotId]; shaderState.m_ShadowStateId = shadowStateId; memcpy( &shaderState.m_ShaderState, ¤tShaderState, sizeof(ShadowShaderState_t) ); memset( shaderState.m_ShaderState.m_nReserved, 0, sizeof( shaderState.m_ShaderState.m_nReserved ) ); shaderState.m_nReserved = 0; // needed to get a good CRC shaderState.m_nReserved2 = 0; // Insert entry into the lookup table SnapshotDictEntry_t insert; CRC32_Init( &insert.m_nChecksum ); CRC32_ProcessBuffer( &insert.m_nChecksum, &shaderState, sizeof(SnapshotShaderState_t) ); CRC32_Final( &insert.m_nChecksum ); insert.m_nSnapshot = snapshotId; m_SnapshotDict.Insert( insert ); return snapshotId; } //----------------------------------------------------------------------------- // Creates a shadow, adding an entry into the shadow list and transition table //----------------------------------------------------------------------------- CTransitionTable::ShadowStateId_t CTransitionTable::CreateShadowState( const ShadowState_t ¤tState ) { int newShaderState = m_ShadowStateList.AddToTail(); // Copy our snapshot into the list memcpy( &m_ShadowStateList[newShaderState], ¤tState, sizeof(ShadowState_t) ); // all existing states must transition to the new state int i; for ( i = 0; i < newShaderState; ++i ) { // Add a new transition to all existing states int newElem = m_TransitionTable[i].AddToTail(); m_TransitionTable[i][newElem].m_FirstOperation = INVALID_TRANSITION_OP; m_TransitionTable[i][newElem].m_NumOperations = 0; } // Add a new vector for this transition int newTransitionElem = m_TransitionTable.AddToTail(); m_TransitionTable[newTransitionElem].EnsureCapacity( 32 ); Assert( newShaderState == newTransitionElem ); for ( i = 0; i <= newShaderState; ++i ) { // Add a new transition from all existing states int newElem = m_TransitionTable[newShaderState].AddToTail(); m_TransitionTable[newShaderState][newElem].m_FirstOperation = INVALID_TRANSITION_OP; m_TransitionTable[newShaderState][newElem].m_NumOperations = 0; } // Insert entry into the lookup table ShadowStateDictEntry_t insert; CRC32_Init( &insert.m_nChecksum ); CRC32_ProcessBuffer( &insert.m_nChecksum, &m_ShadowStateList[newShaderState], sizeof(ShadowState_t) ); CRC32_Final( &insert.m_nChecksum ); insert.m_nShadowStateId = newShaderState; m_ShadowStateDict.Insert( insert ); return newShaderState; } //----------------------------------------------------------------------------- // Finds a snapshot, if it exists. Or creates a new one if it doesn't. //----------------------------------------------------------------------------- CTransitionTable::ShadowStateId_t CTransitionTable::FindShadowState( const ShadowState_t& currentState ) const { ShadowStateDictEntry_t find; CRC32_Init( &find.m_nChecksum ); CRC32_ProcessBuffer( &find.m_nChecksum, ¤tState, sizeof(ShadowState_t) ); CRC32_Final( &find.m_nChecksum ); int nDictCount = m_ShadowStateDict.Count(); int i = m_ShadowStateDict.FindLessOrEqual( find ); if ( i < 0 ) return (ShadowStateId_t)-1; for ( ; i < nDictCount; ++i ) { const ShadowStateDictEntry_t &entry = m_ShadowStateDict[i]; // Didn't find a match if ( entry.m_nChecksum > find.m_nChecksum ) break; if ( entry.m_nChecksum != find.m_nChecksum ) continue; ShadowStateId_t nShadowState = entry.m_nShadowStateId; if (!memcmp(&m_ShadowStateList[nShadowState], ¤tState, sizeof(ShadowState_t) )) return nShadowState; } // Need to create a new one return (ShadowStateId_t)-1; } //----------------------------------------------------------------------------- // Finds a snapshot, if it exists. Or creates a new one if it doesn't. //----------------------------------------------------------------------------- StateSnapshot_t CTransitionTable::FindStateSnapshot( ShadowStateId_t id, const ShadowShaderState_t& currentState ) const { SnapshotShaderState_t temp; temp.m_ShaderState = currentState; temp.m_ShadowStateId = id; memset( temp.m_ShaderState.m_nReserved, 0, sizeof( temp.m_ShaderState.m_nReserved ) ); temp.m_nReserved = 0; // needed to get a good CRC temp.m_nReserved2 = 0; SnapshotDictEntry_t find; CRC32_Init( &find.m_nChecksum ); CRC32_ProcessBuffer( &find.m_nChecksum, &temp, sizeof(temp) ); CRC32_Final( &find.m_nChecksum ); int nDictCount = m_SnapshotDict.Count(); int i = m_SnapshotDict.FindLessOrEqual( find ); if ( i < 0 ) return (StateSnapshot_t)-1; for ( ; i < nDictCount; ++i ) { // Didn't find a match if ( m_SnapshotDict[i].m_nChecksum > find.m_nChecksum ) break; if ( m_SnapshotDict[i].m_nChecksum != find.m_nChecksum ) continue; StateSnapshot_t nShapshot = m_SnapshotDict[i].m_nSnapshot; if ( (id == m_SnapshotList[nShapshot].m_ShadowStateId) && !memcmp(&m_SnapshotList[nShapshot].m_ShaderState, ¤tState, sizeof(ShadowShaderState_t)) ) { return nShapshot; } } // Need to create a new one return (StateSnapshot_t)-1; } //----------------------------------------------------------------------------- // Used to clear the transition table when we know it's become invalid. //----------------------------------------------------------------------------- void CTransitionTable::Reset() { m_ShadowStateList.RemoveAll(); m_SnapshotList.RemoveAll(); m_TransitionTable.RemoveAll(); m_TransitionOps.RemoveAll(); m_ShadowStateDict.RemoveAll(); m_SnapshotDict.RemoveAll(); m_UniqueTransitions.RemoveAll(); m_CurrentShadowId = -1; m_CurrentSnapshotId = -1; m_DefaultStateSnapshot = -1; } //----------------------------------------------------------------------------- // Sets the texture stage state //----------------------------------------------------------------------------- #ifdef _WIN32 #pragma warning( disable : 4189 ) #endif static inline void SetTextureStageState( int stage, D3DTEXTURESTAGESTATETYPE state, DWORD val ) { #if !defined( _X360 ) Assert( !g_pShaderDeviceDx8->IsDeactivated() ); Dx9Device()->SetTextureStageState( stage, state, val ); #endif } //Moved to a #define so every instance of this skips unsupported render states at compile time #define SetSamplerState( _stage, _state, _val ) \ { \ if ( (_state != D3DSAMP_NOTSUPPORTED) ) \ { \ Assert( !g_pShaderDeviceDx8->IsDeactivated() ); \ Dx9Device()->SetSamplerState( _stage, _state, _val ); \ } \ } //Moved to a #define so every instance of this skips unsupported render states at compile time #define SetRenderState( _state, _val ) \ { \ if ( _state != D3DRS_NOTSUPPORTED ) \ { \ Assert( !g_pShaderDeviceDx8->IsDeactivated() ); \ Dx9Device()->SetRenderState( _state, _val ); \ } \ } #ifdef DX_TO_GL_ABSTRACTION #define SetRenderStateConstMacro( state, val ) { if ( state != D3DRS_NOTSUPPORTED ) Dx9Device()->SetRenderStateConstInline( state, val ); } #else #define SetRenderStateConstMacro( state, val ) SetRenderState( state, val ) #endif #ifdef _WIN32 #pragma warning( default : 4189 ) #endif //----------------------------------------------------------------------------- // Methods that actually apply the state //----------------------------------------------------------------------------- #ifdef DEBUG_BOARD_STATE static bool g_SpewTransitions = false; #define UPDATE_BOARD_RENDER_STATE( _d3dState, _state ) \ { \ BoardState().m_ ## _state = shaderState.m_ ## _state; \ if (g_SpewTransitions) \ { \ char buf[128]; \ sprintf( buf, "Apply %s : %d\n", #_d3dState, shaderState.m_ ## _state ); \ Plat_DebugString(buf); \ } \ } #define UPDATE_BOARD_TEXTURE_STAGE_STATE( _d3dState, _state, _stage ) \ { \ BoardState().m_TextureStage[_stage].m_ ## _state = shaderState.m_TextureStage[_stage].m_ ## _state; \ if (g_SpewTransitions) \ { \ char buf[128]; \ sprintf( buf, "Apply Tex %s (%d): %d\n", #_d3dState, _stage, shaderState.m_TextureStage[_stage].m_ ## _state ); \ Plat_DebugString(buf); \ } \ } #define UPDATE_BOARD_SAMPLER_STATE( _d3dState, _state, _stage ) \ { \ BoardState().m_SamplerState[_stage].m_ ## _state = shaderState.m_SamplerState[_stage].m_ ## _state; \ if (g_SpewTransitions) \ { \ char buf[128]; \ sprintf( buf, "Apply SamplerSate %s (%d): %d\n", #_d3dState, stage, shaderState.m_SamplerState[_stage].m_ ## _state ); \ Plat_DebugString(buf); \ } \ } #else #define UPDATE_BOARD_RENDER_STATE( _d3dState, _state ) {} #define UPDATE_BOARD_TEXTURE_STAGE_STATE( _d3dState, _state, _stage ) {} #define UPDATE_BOARD_SAMPLER_STATE( _d3dState, _state, _stage ) {} #endif #define APPLY_RENDER_STATE_FUNC( _d3dState, _state ) \ void Apply ## _state( const ShadowState_t& shaderState, int arg ) \ { \ SetRenderState( _d3dState, shaderState.m_ ## _state ); \ UPDATE_BOARD_RENDER_STATE( _d3dState, _state ); \ } #define APPLY_TEXTURE_STAGE_STATE_FUNC( _d3dState, _state ) \ void Apply ## _state( const ShadowState_t& shaderState, int stage ) \ { \ SetTextureStageState( stage, _d3dState, shaderState.m_TextureStage[stage].m_ ## _state ); \ UPDATE_BOARD_TEXTURE_STAGE_STATE( _d3dState, _state, stage ); \ } #define APPLY_SAMPLER_STATE_FUNC( _d3dState, _state ) \ void Apply ## _state( const ShadowState_t& shaderState, int stage ) \ { \ SetSamplerState( stage, _d3dState, shaderState.m_SamplerState[stage].m_ ## _state ); \ UPDATE_BOARD_SAMPLER_STATE( _d3dState, _state, stage ); \ } // Special overridden sampler state to turn on Fetch4 on ATI hardware (and 360?) void ApplyFetch4Enable( const ShadowState_t& shaderState, int stage ) { if ( ShaderAPI()->SupportsFetch4() ) { SetSamplerState( stage, ATISAMP_FETCH4, shaderState.m_SamplerState[stage].m_Fetch4Enable ? ATI_FETCH4_ENABLE : ATI_FETCH4_DISABLE ); } UPDATE_BOARD_SAMPLER_STATE( ATISAMP_FETCH4, Fetch4Enable, stage ); } #ifdef DX_TO_GL_ABSTRACTION void ApplyShadowFilterEnable( const ShadowState_t& shaderState, int stage ) { SetSamplerState( stage, D3DSAMP_SHADOWFILTER, shaderState.m_SamplerState[stage].m_ShadowFilterEnable ); UPDATE_BOARD_SAMPLER_STATE( D3DSAMP_SHADOWFILTER, ShadowFilterEnable, stage ); } #endif //APPLY_RENDER_STATE_FUNC( D3DRS_ZWRITEENABLE, ZWriteEnable ) //APPLY_RENDER_STATE_FUNC( D3DRS_COLORWRITEENABLE, ColorWriteEnable ) APPLY_RENDER_STATE_FUNC( D3DRS_FILLMODE, FillMode ) APPLY_RENDER_STATE_FUNC( D3DRS_LIGHTING, Lighting ) APPLY_RENDER_STATE_FUNC( D3DRS_SPECULARENABLE, SpecularEnable ) APPLY_RENDER_STATE_FUNC( D3DRS_DIFFUSEMATERIALSOURCE, DiffuseMaterialSource ) APPLY_TEXTURE_STAGE_STATE_FUNC( D3DTSS_TEXCOORDINDEX, TexCoordIndex ) void ApplyZWriteEnable( const ShadowState_t& shaderState, int arg ) { SetRenderStateConstMacro( D3DRS_ZWRITEENABLE, shaderState.m_ZWriteEnable ); #if defined( _X360 ) //SetRenderStateConstMacro( D3DRS_HIZWRITEENABLE, shaderState.m_ZWriteEnable ? D3DHIZ_AUTOMATIC : D3DHIZ_DISABLE ); #endif UPDATE_BOARD_RENDER_STATE( D3DRS_ZWRITEENABLE, ZWriteEnable ); } void ApplyColorWriteEnable( const ShadowState_t& shaderState, int arg ) { SetRenderState( D3DRS_COLORWRITEENABLE, shaderState.m_ColorWriteEnable ); g_pTransitionTable->CurrentState().m_ColorWriteEnable = shaderState.m_ColorWriteEnable; UPDATE_BOARD_RENDER_STATE( D3DRS_COLORWRITEENABLE, ColorWriteEnable ); } void ApplySRGBReadEnable( const ShadowState_t& shaderState, int stage ) { # if ( !defined( _X360 ) ) { SetSamplerState( stage, D3DSAMP_SRGBTEXTURE, shaderState.m_SamplerState[stage].m_SRGBReadEnable ); } # else { ShaderAPI()->ApplySRGBReadState( stage, shaderState.m_SamplerState[stage].m_SRGBReadEnable ); } # endif UPDATE_BOARD_SAMPLER_STATE( D3DSAMP_SRGBTEXTURE, SRGBReadEnable, stage ); } void ApplySRGBWriteEnable( const ShadowState_t& shadowState, int stageUnused ) { g_pTransitionTable->ApplySRGBWriteEnable( shadowState ); } void CTransitionTable::ApplySRGBWriteEnable( const ShadowState_t& shaderState ) { // ApplySRGBWriteEnable set to true means that the shader is writing linear values. if ( CurrentState().m_bLinearColorSpaceFrameBufferEnable ) { // The shader had better be writing linear values since we can't convert to gamma here. // Can't leave this assert here since there are cases where the shader is doing the right thing. // This is good to test occasionally to make sure that the shaders are doing the right thing. // Assert( shaderState.m_SRGBWriteEnable ); // render target is linear SetRenderStateConstMacro( D3DRS_SRGBWRITEENABLE, 0 ); ShaderAPI()->EnabledSRGBWrite( false ); // fog isn't fixed-function with linear frame buffers, so don't bother with that here. } else { // render target is gamma // SRGBWrite enable can affect the space in which fog color is defined if ( HardwareConfig()->NeedsShaderSRGBConversion() ) { if ( HardwareConfig()->SupportsPixelShaders_2_b() ) //in 2b supported devices, we never actually enable SRGB writes, but instead handle the conversion in the pixel shader. But we want all other code to be unaware. { SetRenderStateConstMacro( D3DRS_SRGBWRITEENABLE, 0 ); } else { SetRenderStateConstMacro( D3DRS_SRGBWRITEENABLE, shaderState.m_SRGBWriteEnable ); } } else { SetRenderStateConstMacro( D3DRS_SRGBWRITEENABLE, shaderState.m_SRGBWriteEnable ); } ShaderAPI()->EnabledSRGBWrite( shaderState.m_SRGBWriteEnable ); if ( HardwareConfig()->SpecifiesFogColorInLinearSpace() ) { ShaderAPI()->ApplyFogMode( shaderState.m_FogMode, shaderState.m_SRGBWriteEnable, shaderState.m_bDisableFogGammaCorrection ); } } #ifdef _DEBUG BoardState().m_SRGBWriteEnable = shaderState.m_SRGBWriteEnable; if (g_SpewTransitions) { char buf[128]; sprintf( buf, "Apply %s : %d\n", "D3DRS_SRGBWRITEENABLE", shaderState.m_SRGBWriteEnable ); Plat_DebugString(buf); } #endif } void ApplyDisableFogGammaCorrection( const ShadowState_t& shadowState, int stageUnused ) { ShaderAPI()->ApplyFogMode( shadowState.m_FogMode, shadowState.m_SRGBWriteEnable, shadowState.m_bDisableFogGammaCorrection ); #ifdef DEBUG_BOARD_STATE g_pTransitionTable->BoardState().m_bDisableFogGammaCorrection = shadowState.m_bDisableFogGammaCorrection; #endif } void ApplyDepthTest( const ShadowState_t& state, int stage ) { g_pTransitionTable->ApplyDepthTest( state ); } void CTransitionTable::SetZEnable( D3DZBUFFERTYPE nEnable ) { if (m_CurrentState.m_ZEnable != nEnable ) { SetRenderStateConstMacro( D3DRS_ZENABLE, nEnable ); #if defined( _X360 ) //SetRenderState( D3DRS_HIZENABLE, nEnable ? D3DHIZ_AUTOMATIC : D3DHIZ_DISABLE ); #endif m_CurrentState.m_ZEnable = nEnable; } } void CTransitionTable::SetZFunc( D3DCMPFUNC nCmpFunc ) { if (m_CurrentState.m_ZFunc != nCmpFunc ) { SetRenderStateConstMacro( D3DRS_ZFUNC, nCmpFunc ); m_CurrentState.m_ZFunc = nCmpFunc; } } void CTransitionTable::ApplyDepthTest( const ShadowState_t& state ) { SetZEnable( state.m_ZEnable ); if (state.m_ZEnable != D3DZB_FALSE) { SetZFunc( state.m_ZFunc ); } if (m_CurrentState.m_ZBias != state.m_ZBias) { ShaderAPI()->ApplyZBias( state ); m_CurrentState.m_ZBias = (PolygonOffsetMode_t) state.m_ZBias; // Cast two bits from m_ZBias } #ifdef DEBUG_BOARD_STATE // This isn't quite true, but it's necessary for other error checking to work BoardState().m_ZEnable = state.m_ZEnable; BoardState().m_ZFunc = state.m_ZFunc; BoardState().m_ZBias = state.m_ZBias; #endif } void ApplyAlphaTest( const ShadowState_t& state, int stage ) { g_pTransitionTable->ApplyAlphaTest( state ); } void CTransitionTable::ApplyAlphaTest( const ShadowState_t& state ) { if (m_CurrentState.m_AlphaTestEnable != state.m_AlphaTestEnable) { SetRenderStateConstMacro( D3DRS_ALPHATESTENABLE, state.m_AlphaTestEnable ); m_CurrentState.m_AlphaTestEnable = state.m_AlphaTestEnable; } if (state.m_AlphaTestEnable) { // Set the blend state here... if (m_CurrentState.m_AlphaFunc != state.m_AlphaFunc) { SetRenderStateConstMacro( D3DRS_ALPHAFUNC, state.m_AlphaFunc ); m_CurrentState.m_AlphaFunc = state.m_AlphaFunc; } if (m_CurrentState.m_AlphaRef != state.m_AlphaRef) { SetRenderStateConstMacro( D3DRS_ALPHAREF, state.m_AlphaRef ); m_CurrentState.m_AlphaRef = state.m_AlphaRef; } } #ifdef DEBUG_BOARD_STATE // This isn't quite true, but it's necessary for other error checking to work BoardState().m_AlphaTestEnable = state.m_AlphaTestEnable; BoardState().m_AlphaFunc = state.m_AlphaFunc; BoardState().m_AlphaRef = state.m_AlphaRef; #endif } void ApplyAlphaBlend( const ShadowState_t& state, int stage ) { g_pTransitionTable->ApplyAlphaBlend( state ); } void CTransitionTable::ApplyAlphaBlend( const ShadowState_t& state ) { if (m_CurrentState.m_AlphaBlendEnable != state.m_AlphaBlendEnable) { SetRenderStateConstMacro( D3DRS_ALPHABLENDENABLE, state.m_AlphaBlendEnable ); m_CurrentState.m_AlphaBlendEnable = state.m_AlphaBlendEnable; } if (state.m_AlphaBlendEnable) { // Set the blend state here... if (m_CurrentState.m_SrcBlend != state.m_SrcBlend) { SetRenderStateConstMacro( D3DRS_SRCBLEND, state.m_SrcBlend ); m_CurrentState.m_SrcBlend = state.m_SrcBlend; } if (m_CurrentState.m_DestBlend != state.m_DestBlend) { SetRenderStateConstMacro( D3DRS_DESTBLEND, state.m_DestBlend ); m_CurrentState.m_DestBlend = state.m_DestBlend; } if (m_CurrentState.m_BlendOp != state.m_BlendOp ) { SetRenderStateConstMacro( D3DRS_BLENDOP, state.m_BlendOp ); m_CurrentState.m_BlendOp = state.m_BlendOp; } } #ifdef DEBUG_BOARD_STATE // This isn't quite true, but it's necessary for other error checking to work BoardState().m_AlphaBlendEnable = state.m_AlphaBlendEnable; BoardState().m_SrcBlend = state.m_SrcBlend; BoardState().m_DestBlend = state.m_DestBlend; BoardState().m_BlendOp = state.m_BlendOp; #endif } void ApplySeparateAlphaBlend( const ShadowState_t& state, int stage ) { g_pTransitionTable->ApplySeparateAlphaBlend( state ); } void CTransitionTable::ApplySeparateAlphaBlend( const ShadowState_t& state ) { if (m_CurrentState.m_SeparateAlphaBlendEnable != state.m_SeparateAlphaBlendEnable) { SetRenderStateConstMacro( D3DRS_SEPARATEALPHABLENDENABLE, state.m_SeparateAlphaBlendEnable ); m_CurrentState.m_SeparateAlphaBlendEnable = state.m_SeparateAlphaBlendEnable; } if (state.m_SeparateAlphaBlendEnable) { // Set the blend state here... if (m_CurrentState.m_SrcBlendAlpha != state.m_SrcBlendAlpha) { SetRenderStateConstMacro( D3DRS_SRCBLENDALPHA, state.m_SrcBlendAlpha ); m_CurrentState.m_SrcBlendAlpha = state.m_SrcBlendAlpha; } if (m_CurrentState.m_DestBlendAlpha != state.m_DestBlendAlpha) { SetRenderStateConstMacro( D3DRS_DESTBLENDALPHA, state.m_DestBlendAlpha ); m_CurrentState.m_DestBlendAlpha = state.m_DestBlendAlpha; } if (m_CurrentState.m_BlendOpAlpha != state.m_BlendOpAlpha ) { SetRenderStateConstMacro( D3DRS_BLENDOPALPHA, state.m_BlendOpAlpha ); m_CurrentState.m_BlendOpAlpha = state.m_BlendOpAlpha; } } #ifdef DEBUG_BOARD_STATE // This isn't quite true, but it's necessary for other error checking to work BoardState().m_SeparateAlphaBlendEnable = state.m_SeparateAlphaBlendEnable; BoardState().m_SrcBlendAlpha = state.m_SrcBlendAlpha; BoardState().m_DestBlendAlpha = state.m_DestBlendAlpha; BoardState().m_BlendOpAlpha = state.m_BlendOpAlpha; #endif } //----------------------------------------------------------------------------- // Applies alpha texture op //----------------------------------------------------------------------------- void ApplyColorTextureStage( const ShadowState_t& state, int stage ) { g_pTransitionTable->ApplyColorTextureStage( state, stage ); } void ApplyAlphaTextureStage( const ShadowState_t& state, int stage ) { g_pTransitionTable->ApplyAlphaTextureStage( state, stage ); } void CTransitionTable::ApplyColorTextureStage( const ShadowState_t& state, int stage ) { D3DTEXTUREOP op = state.m_TextureStage[stage].m_ColorOp; int arg1 = state.m_TextureStage[stage].m_ColorArg1; int arg2 = state.m_TextureStage[stage].m_ColorArg2; if (m_CurrentState.m_TextureStage[stage].m_ColorOp != op) { SetTextureStageState( stage, D3DTSS_COLOROP, op ); m_CurrentState.m_TextureStage[stage].m_ColorOp = op; } if (op != D3DTOP_DISABLE) { if (m_CurrentState.m_TextureStage[stage].m_ColorArg1 != arg1) { SetTextureStageState( stage, D3DTSS_COLORARG1, arg1 ); m_CurrentState.m_TextureStage[stage].m_ColorArg1 = arg1; } if (m_CurrentState.m_TextureStage[stage].m_ColorArg2 != arg2) { SetTextureStageState( stage, D3DTSS_COLORARG2, arg2 ); m_CurrentState.m_TextureStage[stage].m_ColorArg2 = arg2; } } #ifdef DEBUG_BOARD_STATE // This isn't quite true, but it's necessary for other error checking to work BoardState().m_TextureStage[stage].m_ColorOp = op; BoardState().m_TextureStage[stage].m_ColorArg1 = arg1; BoardState().m_TextureStage[stage].m_ColorArg2 = arg2; #endif } void CTransitionTable::ApplyAlphaTextureStage( const ShadowState_t& state, int stage ) { D3DTEXTUREOP op = state.m_TextureStage[stage].m_AlphaOp; int arg1 = state.m_TextureStage[stage].m_AlphaArg1; int arg2 = state.m_TextureStage[stage].m_AlphaArg2; if (m_CurrentState.m_TextureStage[stage].m_AlphaOp != op) { SetTextureStageState( stage, D3DTSS_ALPHAOP, op ); m_CurrentState.m_TextureStage[stage].m_AlphaOp = op; } if (op != D3DTOP_DISABLE) { if (m_CurrentState.m_TextureStage[stage].m_AlphaArg1 != arg1) { SetTextureStageState( stage, D3DTSS_ALPHAARG1, arg1 ); m_CurrentState.m_TextureStage[stage].m_AlphaArg1 = arg1; } if (m_CurrentState.m_TextureStage[stage].m_AlphaArg2 != arg2) { SetTextureStageState( stage, D3DTSS_ALPHAARG2, arg2 ); m_CurrentState.m_TextureStage[stage].m_AlphaArg2 = arg2; } } #ifdef DEBUG_BOARD_STATE // This isn't quite true, but it's necessary for other error checking to work BoardState().m_TextureStage[stage].m_AlphaOp = op; BoardState().m_TextureStage[stage].m_AlphaArg1 = arg1; BoardState().m_TextureStage[stage].m_AlphaArg2 = arg2; #endif } void ApplyActivateFixedFunction( const ShadowState_t& state, int stage ) { int nStageCount = HardwareConfig()->GetTextureStageCount(); for ( int i = 0; i < nStageCount; ++i ) { g_pTransitionTable->ApplyColorTextureStage( state, i ); g_pTransitionTable->ApplyAlphaTextureStage( state, i ); } } //----------------------------------------------------------------------------- // Enables textures //----------------------------------------------------------------------------- void ApplyTextureEnable( const ShadowState_t& state, int stage ) { // This may well enable/disable textures that are already enabled/disabled // but the ShaderAPI will handle that int i; int nSamplerCount = HardwareConfig()->GetSamplerCount(); for ( i = 0; i < nSamplerCount; ++i ) { ShaderAPI()->ApplyTextureEnable( state, i ); #ifdef DEBUG_BOARD_STATE BoardState().m_SamplerState[i].m_TextureEnable = state.m_SamplerState[i].m_TextureEnable; #endif } // Needed to prevent mat_dxlevel assertions #ifdef DEBUG_BOARD_STATE for ( i = nSamplerCount; i < MAX_SAMPLERS; ++i ) { BoardState().m_SamplerState[i].m_TextureEnable = false; } #endif } //----------------------------------------------------------------------------- // All transitions below this point depend on dynamic render state // FIXME: Eliminate these virtual calls? //----------------------------------------------------------------------------- void ApplyCullEnable( const ShadowState_t& state, int arg ) { ShaderAPI()->ApplyCullEnable( state.m_CullEnable ); #ifdef DEBUG_BOARD_STATE BoardState().m_CullEnable = state.m_CullEnable; #endif } //----------------------------------------------------------------------------- void ApplyAlphaToCoverage( const ShadowState_t& state, int arg ) { ShaderAPI()->ApplyAlphaToCoverage( state.m_EnableAlphaToCoverage ); #ifdef DEBUG_BOARD_STATE BoardState().m_EnableAlphaToCoverage = state.m_EnableAlphaToCoverage; #endif } //----------------------------------------------------------------------------- void ApplyVertexBlendEnable( const ShadowState_t& state, int stage ) { ShaderAPI()->SetVertexBlendState( state.m_VertexBlendEnable ? -1 : 0 ); #ifdef DEBUG_BOARD_STATE BoardState().m_VertexBlendEnable = state.m_VertexBlendEnable; #endif } //----------------------------------------------------------------------------- // Outputs the fog mode string //----------------------------------------------------------------------------- #ifdef RECORDING const char *ShaderFogModeToString( ShaderFogMode_t fogMode ) { switch( fogMode ) { case SHADER_FOGMODE_DISABLED: return "SHADER_FOGMODE_DISABLED"; case SHADER_FOGMODE_OO_OVERBRIGHT: return "SHADER_FOGMODE_OO_OVERBRIGHT"; case SHADER_FOGMODE_BLACK: return "SHADER_FOGMODE_BLACK"; case SHADER_FOGMODE_GREY: return "SHADER_FOGMODE_GREY"; case SHADER_FOGMODE_FOGCOLOR: return "SHADER_FOGMODE_FOGCOLOR"; case SHADER_FOGMODE_WHITE: return "SHADER_FOGMODE_WHITE"; case SHADER_FOGMODE_NUMFOGMODES: return "SHADER_FOGMODE_NUMFOGMODES"; default: return "ERROR"; } } #endif // Uses GetConfig().overbright and GetSceneFogMode, so // will have to fix up the state manually when those change. void ApplyFogMode( const ShadowState_t& state, int arg ) { #ifdef RECORDING char buf[1024]; sprintf( buf, "ApplyFogMode( %s )", ShaderFogModeToString( state.m_FogMode ) ); RECORD_DEBUG_STRING( buf ); #endif ShaderAPI()->ApplyFogMode( state.m_FogMode, state.m_SRGBWriteEnable, state.m_bDisableFogGammaCorrection ); #ifdef DEBUG_BOARD_STATE BoardState().m_FogMode = state.m_FogMode; #endif } //----------------------------------------------------------------------------- // Function tables mapping enum to function //----------------------------------------------------------------------------- ApplyStateFunc_t s_pRenderFunctionTable[] = { ApplyDepthTest, ApplyZWriteEnable, ApplyColorWriteEnable, ApplyAlphaTest, ApplyFillMode, ApplyLighting, ApplySpecularEnable, ApplySRGBWriteEnable, ApplyAlphaBlend, ApplySeparateAlphaBlend, ApplyCullEnable, ApplyVertexBlendEnable, ApplyFogMode, ApplyActivateFixedFunction, ApplyTextureEnable, // Enables textures on *all* stages ApplyDiffuseMaterialSource, ApplyDisableFogGammaCorrection, ApplyAlphaToCoverage, }; ApplyStateFunc_t s_pTextureFunctionTable[] = { ApplyTexCoordIndex, ApplySRGBReadEnable, ApplyFetch4Enable, #ifdef DX_TO_GL_ABSTRACTION ApplyShadowFilterEnable, #endif // Fixed function states ApplyColorTextureStage, ApplyAlphaTextureStage, }; //----------------------------------------------------------------------------- // Creates an entry in the state transition table //----------------------------------------------------------------------------- inline void CTransitionTable::AddTransition( RenderStateFunc_t func ) { int nElem = m_TransitionOps.AddToTail(); TransitionOp_t &op = m_TransitionOps[nElem]; op.m_nInfo.m_bIsTextureCode = false; op.m_nInfo.m_nOpCode = func; // Stats // ++s_pRenderTransitions[ func ]; } inline void CTransitionTable::AddTextureTransition( TextureStateFunc_t func, int stage ) { int nElem = m_TransitionOps.AddToTail(); TransitionOp_t &op = m_TransitionOps[nElem]; op.m_nInfo.m_bIsTextureCode = true; op.m_nInfo.m_nOpCode = TextureOp( func, stage ); // Stats // ++s_pTextureTransitions[ func ][stage]; } #define ADD_RENDER_STATE_TRANSITION( _state ) \ if (bForce || (toState.m_ ## _state != fromState.m_ ## _state)) \ { \ AddTransition( RENDER_STATE_ ## _state ); \ ++numOps; \ } #define ADD_TEXTURE_STAGE_STATE_TRANSITION( _stage, _state )\ if (bForce || (toState.m_TextureStage[_stage].m_ ## _state != fromState.m_TextureStage[_stage].m_ ## _state)) \ { \ Assert( _stage < MAX_TEXTURE_STAGES ); \ AddTextureTransition( TEXTURE_STATE_ ## _state, _stage ); \ ++numOps; \ } #define ADD_SAMPLER_STATE_TRANSITION( _stage, _state )\ if (bForce || (toState.m_SamplerState[_stage].m_ ## _state != fromState.m_SamplerState[_stage].m_ ## _state)) \ { \ Assert( _stage < MAX_SAMPLERS ); \ AddTextureTransition( TEXTURE_STATE_ ## _state, _stage ); \ ++numOps; \ } int CTransitionTable::CreateNormalTransitions( const ShadowState_t& fromState, const ShadowState_t& toState, bool bForce ) { int numOps = 0; // Special case for alpha blending to eliminate extra transitions bool blendEnableDifferent = (toState.m_AlphaBlendEnable != fromState.m_AlphaBlendEnable); bool srcBlendDifferent = toState.m_AlphaBlendEnable && (toState.m_SrcBlend != fromState.m_SrcBlend); bool destBlendDifferent = toState.m_AlphaBlendEnable && (toState.m_DestBlend != fromState.m_DestBlend); bool blendOpDifferent = toState.m_AlphaBlendEnable && ( toState.m_BlendOp != fromState.m_BlendOp ); if (bForce || blendOpDifferent || blendEnableDifferent || srcBlendDifferent || destBlendDifferent) { AddTransition( RENDER_STATE_AlphaBlend ); ++numOps; } // Shouldn't have m_SeparateAlphaBlendEnable set unless m_AlphaBlendEnable is also set. Assert ( toState.m_AlphaBlendEnable || !toState.m_SeparateAlphaBlendEnable ); bool blendSeparateAlphaEnableDifferent = (toState.m_SeparateAlphaBlendEnable != fromState.m_SeparateAlphaBlendEnable); bool srcBlendAlphaDifferent = toState.m_SeparateAlphaBlendEnable && (toState.m_SrcBlendAlpha != fromState.m_SrcBlendAlpha); bool destBlendAlphaDifferent = toState.m_SeparateAlphaBlendEnable && (toState.m_DestBlendAlpha != fromState.m_DestBlendAlpha); bool blendOpAlphaDifferent = toState.m_SeparateAlphaBlendEnable && ( toState.m_BlendOpAlpha != fromState.m_BlendOpAlpha ); if (bForce || blendOpAlphaDifferent || blendSeparateAlphaEnableDifferent || srcBlendAlphaDifferent || destBlendAlphaDifferent) { AddTransition( RENDER_STATE_SeparateAlphaBlend ); ++numOps; } bool bAlphaTestEnableDifferent = (toState.m_AlphaTestEnable != fromState.m_AlphaTestEnable); bool bAlphaFuncDifferent = toState.m_AlphaTestEnable && (toState.m_AlphaFunc != fromState.m_AlphaFunc); bool bAlphaRefDifferent = toState.m_AlphaTestEnable && (toState.m_AlphaRef != fromState.m_AlphaRef); if (bForce || bAlphaTestEnableDifferent || bAlphaFuncDifferent || bAlphaRefDifferent) { AddTransition( RENDER_STATE_AlphaTest ); ++numOps; } bool bDepthTestEnableDifferent = (toState.m_ZEnable != fromState.m_ZEnable); bool bDepthFuncDifferent = (toState.m_ZEnable != D3DZB_FALSE) && (toState.m_ZFunc != fromState.m_ZFunc); bool bDepthBiasDifferent = (toState.m_ZBias != fromState.m_ZBias); if (bForce || bDepthTestEnableDifferent || bDepthFuncDifferent || bDepthBiasDifferent) { AddTransition( RENDER_STATE_DepthTest ); ++numOps; } if ( bForce || (toState.m_UsingFixedFunction && !fromState.m_UsingFixedFunction) ) { AddTransition( RENDER_STATE_ActivateFixedFunction ); ++numOps; } if ( bForce || (toState.m_bDisableFogGammaCorrection != fromState.m_bDisableFogGammaCorrection) ) { AddTransition( RENDER_STATE_DisableFogGammaCorrection ); ++numOps; } int nStageCount = HardwareConfig()->GetTextureStageCount(); int i; for ( i = 0; i < nStageCount; ++i ) { // Special case for texture stage ops to eliminate extra transitions // NOTE: If we're forcing transitions, then ActivateFixedFunction above will take care of all these transitions if ( !bForce && toState.m_UsingFixedFunction && fromState.m_UsingFixedFunction ) { const TextureStageShadowState_t& fromTexture = fromState.m_TextureStage[i]; const TextureStageShadowState_t& toTexture = toState.m_TextureStage[i]; bool fromEnabled = (fromTexture.m_ColorOp != D3DTOP_DISABLE); bool toEnabled = (toTexture.m_ColorOp != D3DTOP_DISABLE); if ( fromEnabled || toEnabled ) { bool opDifferent = (toTexture.m_ColorOp != fromTexture.m_ColorOp); bool arg1Different = (toTexture.m_ColorArg1 != fromTexture.m_ColorArg1); bool arg2Different = (toTexture.m_ColorArg2 != fromTexture.m_ColorArg2); if (opDifferent || arg1Different || arg2Different ) { AddTextureTransition( TEXTURE_STATE_ColorTextureStage, i ); ++numOps; } } fromEnabled = (fromTexture.m_AlphaOp != D3DTOP_DISABLE); toEnabled = (toTexture.m_AlphaOp != D3DTOP_DISABLE); if ( fromEnabled || toEnabled ) { bool opDifferent = (toTexture.m_AlphaOp != fromTexture.m_AlphaOp); bool arg1Different = (toTexture.m_AlphaArg1 != fromTexture.m_AlphaArg1); bool arg2Different = (toTexture.m_AlphaArg2 != fromTexture.m_AlphaArg2); if (opDifferent || arg1Different || arg2Different ) { AddTextureTransition( TEXTURE_STATE_AlphaTextureStage, i ); ++numOps; } } } ADD_TEXTURE_STAGE_STATE_TRANSITION( i, TexCoordIndex ); } int nSamplerCount = HardwareConfig()->GetSamplerCount(); for ( int i = 0; i < nSamplerCount; ++i ) { ADD_SAMPLER_STATE_TRANSITION( i, SRGBReadEnable ); ADD_SAMPLER_STATE_TRANSITION( i, Fetch4Enable ); #ifdef DX_TO_GL_ABSTRACTION ADD_SAMPLER_STATE_TRANSITION( i, ShadowFilterEnable ); #endif } return numOps; } void CTransitionTable::CreateTransitionTableEntry( int to, int from ) { // You added or removed a state to the enums but not to the function table lists! COMPILE_TIME_ASSERT( sizeof(s_pRenderFunctionTable) == sizeof(ApplyStateFunc_t) * RENDER_STATE_COUNT ); COMPILE_TIME_ASSERT( sizeof(s_pTextureFunctionTable) == sizeof(ApplyStateFunc_t) * TEXTURE_STATE_COUNT ); // If from < 0, that means add *all* transitions into it. unsigned int firstElem = m_TransitionOps.Count(); unsigned short numOps = 0; const ShadowState_t& toState = m_ShadowStateList[to]; const ShadowState_t& fromState = (from >= 0) ? m_ShadowStateList[from] : m_ShadowStateList[to]; bool bForce = (from < 0); ADD_RENDER_STATE_TRANSITION( ZWriteEnable ) ADD_RENDER_STATE_TRANSITION( ColorWriteEnable ) ADD_RENDER_STATE_TRANSITION( FillMode ) ADD_RENDER_STATE_TRANSITION( Lighting ) ADD_RENDER_STATE_TRANSITION( SpecularEnable ) ADD_RENDER_STATE_TRANSITION( SRGBWriteEnable ) ADD_RENDER_STATE_TRANSITION( DiffuseMaterialSource ) // Some code for the non-trivial transitions numOps += CreateNormalTransitions( fromState, toState, bForce ); // NOTE: From here on down are transitions that depend on dynamic state // and which can therefore not appear in the state block ADD_RENDER_STATE_TRANSITION( CullEnable ) ADD_RENDER_STATE_TRANSITION( EnableAlphaToCoverage ) ADD_RENDER_STATE_TRANSITION( VertexBlendEnable ) // NOTE! : Have to do the extra check for changes in m_UsingFixedFunction // since d3d fog state is different if you are using fixed function vs. // using a vsh/psh. // This code is derived from: ADD_RENDER_STATE_TRANSITION( FogMode ) // If ADD_RENDER_STATE_TRANSITION ever changes, this needs to be updated! // This is another reason to try to have very little fixed function in the dx8/dx9 path. if( bForce || (toState.m_FogMode != fromState.m_FogMode ) || ( toState.m_UsingFixedFunction != fromState.m_UsingFixedFunction ) ) { AddTransition( RENDER_STATE_FogMode ); ++numOps; } bool bDifferentTexturesEnabled = false; int nSamplerCount = HardwareConfig()->GetSamplerCount(); for ( int i = 0; i < nSamplerCount; ++i ) { if ( toState.m_SamplerState[i].m_TextureEnable != fromState.m_SamplerState[i].m_TextureEnable ) { bDifferentTexturesEnabled = true; break; } } if ( bForce || bDifferentTexturesEnabled ) { AddTransition( RENDER_STATE_TextureEnable ); ++numOps; } // Look for identical transition lists, and use those instead... TransitionList_t& transition = (from >= 0) ? m_TransitionTable[to][from] : m_DefaultTransition; Assert( numOps <= 255 ); transition.m_NumOperations = numOps; // This condition can happen, and is valid. It occurs when we snapshot // state but do not generate a transition function for that state if (numOps == 0) { transition.m_FirstOperation = INVALID_TRANSITION_OP; return; } // An optimization to try to early out of the identical transition check // taking advantage of the fact that the matrix is usually diagonal. unsigned int nFirstTest = INVALID_TRANSITION_OP; if (from >= 0) { TransitionList_t &diagonalList = m_TransitionTable[from][to]; if ( diagonalList.m_NumOperations == numOps ) { nFirstTest = diagonalList.m_FirstOperation; } } unsigned int identicalListFirstElem = FindIdenticalTransitionList( firstElem, numOps, nFirstTest ); if (identicalListFirstElem == INVALID_TRANSITION_OP) { transition.m_FirstOperation = firstElem; m_UniqueTransitions.Insert( transition ); Assert( (int)firstElem + (int)numOps < 16777215 ); if( (int)firstElem + (int)numOps >= 16777215 ) { Warning("**** WARNING: Transition table overflow. Grab Brian\n"); } } else { // Remove the transitions ops we made; use the duplicate copy transition.m_FirstOperation = identicalListFirstElem; m_TransitionOps.RemoveMultiple( firstElem, numOps ); } } //----------------------------------------------------------------------------- // Tests a snapshot to see if it can be used //----------------------------------------------------------------------------- #define PERFORM_RENDER_STATE_TRANSITION( _state, _func ) \ ::Apply ## _func( _state, 0 ); #define PERFORM_TEXTURE_STAGE_STATE_TRANSITION( _state, _stage, _func ) \ ::Apply ## _func( _state, _stage ); #define PERFORM_SAMPLER_STATE_TRANSITION( _state, _stage, _func ) \ ::Apply ## _func( _state, _stage ); bool CTransitionTable::TestShadowState( const ShadowState_t& state, const ShadowShaderState_t &shaderState ) { PERFORM_RENDER_STATE_TRANSITION( state, DepthTest ) PERFORM_RENDER_STATE_TRANSITION( state, ZWriteEnable ) PERFORM_RENDER_STATE_TRANSITION( state, ColorWriteEnable ) PERFORM_RENDER_STATE_TRANSITION( state, AlphaTest ) PERFORM_RENDER_STATE_TRANSITION( state, FillMode ) PERFORM_RENDER_STATE_TRANSITION( state, Lighting ) PERFORM_RENDER_STATE_TRANSITION( state, SpecularEnable ) PERFORM_RENDER_STATE_TRANSITION( state, SRGBWriteEnable ) PERFORM_RENDER_STATE_TRANSITION( state, AlphaBlend ) PERFORM_RENDER_STATE_TRANSITION( state, SeparateAlphaBlend ) PERFORM_RENDER_STATE_TRANSITION( state, CullEnable ) PERFORM_RENDER_STATE_TRANSITION( state, AlphaToCoverage ) PERFORM_RENDER_STATE_TRANSITION( state, VertexBlendEnable ) PERFORM_RENDER_STATE_TRANSITION( state, FogMode ) PERFORM_RENDER_STATE_TRANSITION( state, ActivateFixedFunction ) PERFORM_RENDER_STATE_TRANSITION( state, TextureEnable ) PERFORM_RENDER_STATE_TRANSITION( state, DiffuseMaterialSource ) int i; int nStageCount = HardwareConfig()->GetTextureStageCount(); for ( i = 0; i < nStageCount; ++i ) { PERFORM_TEXTURE_STAGE_STATE_TRANSITION( state, i, ColorTextureStage ); PERFORM_TEXTURE_STAGE_STATE_TRANSITION( state, i, AlphaTextureStage ); PERFORM_TEXTURE_STAGE_STATE_TRANSITION( state, i, TexCoordIndex ); } int nSamplerCount = HardwareConfig()->GetSamplerCount(); for ( i = 0; i < nSamplerCount; ++i ) { PERFORM_SAMPLER_STATE_TRANSITION( state, i, SRGBReadEnable ); PERFORM_SAMPLER_STATE_TRANSITION( state, i, Fetch4Enable ); #ifdef DX_TO_GL_ABSTRACTION PERFORM_SAMPLER_STATE_TRANSITION( state, i, ShadowFilterEnable ); #endif } // Just make sure we've got a good snapshot RECORD_COMMAND( DX8_VALIDATE_DEVICE, 0 ); #if !defined( _X360 ) DWORD numPasses; HRESULT hr = Dx9Device()->ValidateDevice( &numPasses ); bool ok = !FAILED(hr); #else bool ok = true; #endif // Now set the board state to match the default state ApplyTransition( m_DefaultTransition, m_DefaultStateSnapshot ); ShaderManager()->SetVertexShader( shaderState.m_VertexShader ); ShaderManager()->SetPixelShader( shaderState.m_PixelShader ); return ok; } //----------------------------------------------------------------------------- // Finds identical transition lists and shares them //----------------------------------------------------------------------------- unsigned int CTransitionTable::FindIdenticalTransitionList( unsigned int firstElem, unsigned short numOps, unsigned int nFirstTest ) const { VPROF("CTransitionTable::FindIdenticalTransitionList"); // As it turns out, this works most of the time if ( nFirstTest != INVALID_TRANSITION_OP ) { const TransitionOp_t *pCurrOp = &m_TransitionOps[firstElem]; const TransitionOp_t *pTestOp = &m_TransitionOps[nFirstTest]; if ( !memcmp( pCurrOp, pTestOp, numOps * sizeof(TransitionOp_t) ) ) return nFirstTest; } // Look for a common list const TransitionOp_t &op = m_TransitionOps[firstElem]; int nCount = m_UniqueTransitions.Count(); for ( int i = 0; i < nCount; ++i ) { const TransitionList_t &list = m_UniqueTransitions[i]; // We can early out here because we've sorted the unique transitions // descending by count if ( list.m_NumOperations < numOps ) return INVALID_TRANSITION_OP; // If we don't find a match in the first int nPotentialMatch; int nLastTest = list.m_FirstOperation + list.m_NumOperations - numOps; for ( nPotentialMatch = list.m_FirstOperation; nPotentialMatch <= nLastTest; ++nPotentialMatch ) { // Find the first match const TransitionOp_t &testOp = m_TransitionOps[nPotentialMatch]; if ( testOp.m_nBits == op.m_nBits ) break; } // No matches found, continue if ( nPotentialMatch > nLastTest ) continue; // Ok, found a match of the first op, lets see if they all match if ( numOps == 1 ) return nPotentialMatch; const TransitionOp_t *pCurrOp = &m_TransitionOps[firstElem + 1]; const TransitionOp_t *pTestOp = &m_TransitionOps[nPotentialMatch + 1]; if ( !memcmp( pCurrOp, pTestOp, (numOps - 1) * sizeof(TransitionOp_t) ) ) return nPotentialMatch; } return INVALID_TRANSITION_OP; } //----------------------------------------------------------------------------- // Create startup snapshot //----------------------------------------------------------------------------- void CTransitionTable::TakeDefaultStateSnapshot( ) { if (m_DefaultStateSnapshot == -1) { m_DefaultStateSnapshot = TakeSnapshot(); // This will create a transition which sets *all* shadowed state CreateTransitionTableEntry( m_DefaultStateSnapshot, -1 ); } } //----------------------------------------------------------------------------- // Applies the transition list //----------------------------------------------------------------------------- void CTransitionTable::ApplyTransitionList( int snapshot, int nFirstOp, int nOpCount ) { VPROF("CTransitionTable::ApplyTransitionList"); // Don't bother if there's nothing to do if (nOpCount > 0) { // Trying to avoid function overhead here ShadowState_t& shadowState = m_ShadowStateList[snapshot]; TransitionOp_t* pTransitionOp = &m_TransitionOps[nFirstOp]; for (int i = 0; i < nOpCount; ++i ) { // invoke the transition method if ( pTransitionOp->m_nInfo.m_bIsTextureCode ) { TextureStateFunc_t code; int nStage; GetTextureOp( pTransitionOp->m_nInfo.m_nOpCode, &code, &nStage ); (*s_pTextureFunctionTable[code])( shadowState, nStage ); } else { (*s_pRenderFunctionTable[pTransitionOp->m_nInfo.m_nOpCode])( shadowState, 0 ); } ++pTransitionOp; } } } //----------------------------------------------------------------------------- // Apply startup snapshot //----------------------------------------------------------------------------- #ifdef _WIN32 #pragma warning( disable : 4189 ) #endif void CTransitionTable::ApplyTransition( TransitionList_t& list, int snapshot ) { VPROF("CTransitionTable::ApplyTransition"); if ( g_pShaderDeviceDx8->IsDeactivated() ) return; // Transition lists when using state blocks have 2 parts: the first // is the stateblock part, which is states that are not related to // dynamic state at all; followed by states that *are* affected by dynamic state int nFirstOp = list.m_FirstOperation; int nOpCount = list.m_NumOperations; ApplyTransitionList( snapshot, nFirstOp, nOpCount ); // Semi-hacky code to override what the transitions are doing PerformShadowStateOverrides(); // Set the current snapshot id m_CurrentShadowId = snapshot; #ifdef DEBUG_BOARD_STATE // Copy over the board states that aren't explicitly in the transition table // so the assertion works... int i; int nSamplerCount = HardwareConfig()->GetSamplerCount(); for ( i = nSamplerCount; i < MAX_SAMPLERS; ++i ) { m_BoardState.m_SamplerState[i].m_TextureEnable = CurrentShadowState()->m_SamplerState[i].m_TextureEnable; } int nTextureStageCount = HardwareConfig()->GetTextureStageCount(); for ( i = nTextureStageCount; i < MAX_TEXTURE_STAGES; ++i ) { memcpy( &m_BoardState.m_TextureStage[i], &CurrentShadowState()->m_TextureStage[i], sizeof(TextureStageShadowState_t) ); } m_BoardState.m_UsingFixedFunction = CurrentShadowState()->m_UsingFixedFunction; // State blocks bypass the code that sets the board state #ifdef _DEBUG // NOTE: A memcmp here isn't enough since we don't set alpha args in cases where the op is nothing. // Assert( !memcmp( &m_BoardState, &CurrentShadowState(), sizeof(m_BoardState) ) ); const ShadowState_t &testState1 = *CurrentShadowState(); ShadowState_t testState2 = m_BoardState; if ( testState1.m_ZEnable == D3DZB_FALSE ) { testState2.m_ZBias = testState1.m_ZBias; testState2.m_ZFunc = testState1.m_ZFunc; } if ( !testState1.m_AlphaTestEnable ) { testState2.m_AlphaRef = testState1.m_AlphaRef; testState2.m_AlphaFunc = testState1.m_AlphaFunc; } for( i = 0; i < nTextureStageCount; i++ ) { if ( !testState1.m_UsingFixedFunction ) { testState2.m_TextureStage[i].m_ColorOp = testState1.m_TextureStage[i].m_ColorOp; testState2.m_TextureStage[i].m_ColorArg1 = testState1.m_TextureStage[i].m_ColorArg1; testState2.m_TextureStage[i].m_ColorArg2 = testState1.m_TextureStage[i].m_ColorArg2; testState2.m_TextureStage[i].m_AlphaOp = testState1.m_TextureStage[i].m_AlphaOp; testState2.m_TextureStage[i].m_AlphaArg1 = testState1.m_TextureStage[i].m_AlphaArg1; testState2.m_TextureStage[i].m_AlphaArg2 = testState1.m_TextureStage[i].m_AlphaArg2; } else { if ( testState1.m_TextureStage[i].m_ColorOp == D3DTOP_DISABLE ) { testState2.m_TextureStage[i].m_ColorArg1 = testState1.m_TextureStage[i].m_ColorArg1; testState2.m_TextureStage[i].m_ColorArg2 = testState1.m_TextureStage[i].m_ColorArg2; } if ( testState1.m_TextureStage[i].m_AlphaOp == D3DTOP_DISABLE ) { testState2.m_TextureStage[i].m_AlphaArg1 = testState1.m_TextureStage[i].m_AlphaArg1; testState2.m_TextureStage[i].m_AlphaArg2 = testState1.m_TextureStage[i].m_AlphaArg2; } } } Assert( !memcmp( &testState1, &testState2, sizeof( testState1 ) ) ); #endif #endif } #ifdef _WIN32 #pragma warning( default : 4189 ) #endif //----------------------------------------------------------------------------- // Takes a snapshot, hooks it into the material //----------------------------------------------------------------------------- StateSnapshot_t CTransitionTable::TakeSnapshot( ) { // Do any final computation of the shadow state ShaderShadow()->ComputeAggregateShadowState(); // Get the current snapshot const ShadowState_t& currentState = ShaderShadow()->GetShadowState(); // Create a new snapshot ShadowStateId_t shadowStateId = FindShadowState( currentState ); if (shadowStateId == -1) { // Create entry in state transition table shadowStateId = CreateShadowState( currentState ); // Now create new transition entries for (int to = 0; to < shadowStateId; ++to) { CreateTransitionTableEntry( to, shadowStateId ); } for (int from = 0; from < shadowStateId; ++from) { CreateTransitionTableEntry( shadowStateId, from ); } } const ShadowShaderState_t& currentShaderState = ShaderShadow()->GetShadowShaderState(); StateSnapshot_t snapshotId = FindStateSnapshot( shadowStateId, currentShaderState ); if (snapshotId == -1) { // Create entry in state transition table snapshotId = CreateStateSnapshot( shadowStateId, currentShaderState ); } return snapshotId; } //----------------------------------------------------------------------------- // Apply shader state (stuff that doesn't lie in the transition table) //----------------------------------------------------------------------------- void CTransitionTable::ApplyShaderState( const ShadowState_t &shadowState, const ShadowShaderState_t &shaderState ) { VPROF("CTransitionTable::ApplyShaderState"); // Don't bother testing against the current state because there // could well be dynamic state modifiers affecting this too.... if ( !shadowState.m_UsingFixedFunction ) { // FIXME: Improve early-binding of vertex shader index ShaderManager()->SetVertexShader( shaderState.m_VertexShader ); ShaderManager()->SetPixelShader( shaderState.m_PixelShader ); #ifdef DEBUG_BOARD_STATE BoardShaderState().m_VertexShader = shaderState.m_VertexShader; BoardShaderState().m_PixelShader = shaderState.m_PixelShader; BoardShaderState().m_nStaticVshIndex = shaderState.m_nStaticVshIndex; BoardShaderState().m_nStaticPshIndex = shaderState.m_nStaticPshIndex; #endif } else { ShaderManager()->SetVertexShader( INVALID_SHADER ); ShaderManager()->SetPixelShader( INVALID_SHADER ); #if defined( _X360 ) // no fixed function support Assert( 0 ); #endif #ifdef DEBUG_BOARD_STATE BoardShaderState().m_VertexShader = INVALID_SHADER; BoardShaderState().m_PixelShader = INVALID_SHADER; BoardShaderState().m_nStaticVshIndex = 0; BoardShaderState().m_nStaticPshIndex = 0; #endif } } //----------------------------------------------------------------------------- // Makes the board state match the snapshot //----------------------------------------------------------------------------- void CTransitionTable::UseSnapshot( StateSnapshot_t snapshotId ) { VPROF("CTransitionTable::UseSnapshot"); ShadowStateId_t id = m_SnapshotList[snapshotId].m_ShadowStateId; if (m_CurrentSnapshotId != snapshotId) { // First apply things that are in the transition table if ( m_CurrentShadowId != id ) { TransitionList_t& transition = m_TransitionTable[id][m_CurrentShadowId]; ApplyTransition( transition, id ); } // NOTE: There is an opportunity here to set non-dynamic state that we don't // store in the transition list if we ever need it. m_CurrentSnapshotId = snapshotId; } // NOTE: This occurs regardless of whether the snapshot changed because it depends // on dynamic state (namely, the dynamic vertex + pixel shader index) // Followed by things that are not ApplyShaderState( m_ShadowStateList[id], m_SnapshotList[snapshotId].m_ShaderState ); #ifdef _DEBUG // NOTE: We can't ship with this active because mod makers may well violate this rule // We don't want no stinking fixed-function on hardware that has vertex and pixel shaders. . // This could cause a serious perf hit. if( HardwareConfig()->SupportsVertexAndPixelShaders() ) { // Assert( !CurrentShadowState().m_UsingFixedFunction ); } #endif } //----------------------------------------------------------------------------- // Cause the board to match the default state snapshot //----------------------------------------------------------------------------- void CTransitionTable::UseDefaultState( ) { VPROF("CTransitionTable::UseDefaultState"); // Need to blat these out because they are tested during transitions m_CurrentState.m_AlphaBlendEnable = false; m_CurrentState.m_SrcBlend = D3DBLEND_ONE; m_CurrentState.m_DestBlend = D3DBLEND_ZERO; m_CurrentState.m_BlendOp = D3DBLENDOP_ADD; SetRenderStateConstMacro( D3DRS_ALPHABLENDENABLE, m_CurrentState.m_AlphaBlendEnable ); SetRenderStateConstMacro( D3DRS_SRCBLEND, m_CurrentState.m_SrcBlend ); SetRenderStateConstMacro( D3DRS_DESTBLEND, m_CurrentState.m_DestBlend ); SetRenderStateConstMacro( D3DRS_BLENDOP, m_CurrentState.m_BlendOp ); m_CurrentState.m_SeparateAlphaBlendEnable = false; m_CurrentState.m_SrcBlendAlpha = D3DBLEND_ONE; m_CurrentState.m_DestBlendAlpha = D3DBLEND_ZERO; m_CurrentState.m_BlendOpAlpha = D3DBLENDOP_ADD; SetRenderStateConstMacro( D3DRS_SEPARATEALPHABLENDENABLE, m_CurrentState.m_SeparateAlphaBlendEnable ); SetRenderStateConstMacro( D3DRS_SRCBLENDALPHA, m_CurrentState.m_SrcBlendAlpha ); SetRenderStateConstMacro( D3DRS_DESTBLENDALPHA, m_CurrentState.m_DestBlendAlpha ); SetRenderStateConstMacro( D3DRS_BLENDOPALPHA, m_CurrentState.m_BlendOpAlpha ); m_CurrentState.m_ZEnable = D3DZB_TRUE; m_CurrentState.m_ZFunc = D3DCMP_LESSEQUAL; m_CurrentState.m_ZBias = SHADER_POLYOFFSET_DISABLE; SetRenderStateConstMacro( D3DRS_ZENABLE, m_CurrentState.m_ZEnable ); #if defined( _X360 ) //SetRenderStateConstMacro( D3DRS_HIZENABLE, m_CurrentState.m_ZEnable ? D3DHIZ_AUTOMATIC : D3DHIZ_DISABLE ); #endif SetRenderStateConstMacro( D3DRS_ZFUNC, m_CurrentState.m_ZFunc ); m_CurrentState.m_AlphaTestEnable = false; m_CurrentState.m_AlphaFunc = D3DCMP_GREATEREQUAL; m_CurrentState.m_AlphaRef = 0; SetRenderStateConstMacro( D3DRS_ALPHATESTENABLE, m_CurrentState.m_AlphaTestEnable ); SetRenderStateConstMacro( D3DRS_ALPHAFUNC, m_CurrentState.m_AlphaFunc ); SetRenderStateConstMacro( D3DRS_ALPHAREF, m_CurrentState.m_AlphaRef ); int nTextureStages = ShaderAPI()->GetActualTextureStageCount(); for ( int i = 0; i < nTextureStages; ++i) { TextureStage(i).m_ColorOp = D3DTOP_DISABLE; TextureStage(i).m_ColorArg1 = D3DTA_TEXTURE; TextureStage(i).m_ColorArg2 = (i == 0) ? D3DTA_DIFFUSE : D3DTA_CURRENT; TextureStage(i).m_AlphaOp = D3DTOP_DISABLE; TextureStage(i).m_AlphaArg1 = D3DTA_TEXTURE; TextureStage(i).m_AlphaArg2 = (i == 0) ? D3DTA_DIFFUSE : D3DTA_CURRENT; SetTextureStageState( i, D3DTSS_COLOROP, TextureStage(i).m_ColorOp ); SetTextureStageState( i, D3DTSS_COLORARG1, TextureStage(i).m_ColorArg1 ); SetTextureStageState( i, D3DTSS_COLORARG2, TextureStage(i).m_ColorArg2 ); SetTextureStageState( i, D3DTSS_ALPHAOP, TextureStage(i).m_AlphaOp ); SetTextureStageState( i, D3DTSS_ALPHAARG1, TextureStage(i).m_AlphaArg1 ); SetTextureStageState( i, D3DTSS_ALPHAARG2, TextureStage(i).m_AlphaArg2 ); } int nSamplerCount = ShaderAPI()->GetActualSamplerCount(); for ( int i = 0; i < nSamplerCount; ++i) { SetSamplerState( i, D3DSAMP_SRGBTEXTURE, SamplerState(i).m_SRGBReadEnable ); // Set default Fetch4 state on parts which support it if ( ShaderAPI()->SupportsFetch4() ) { SetSamplerState( i, ATISAMP_FETCH4, SamplerState(i).m_Fetch4Enable ? ATI_FETCH4_ENABLE : ATI_FETCH4_DISABLE ); } #ifdef DX_TO_GL_ABSTRACTION SetSamplerState( i, D3DSAMP_SHADOWFILTER, SamplerState(i).m_ShadowFilterEnable ); #endif } // Disable z overrides... m_CurrentState.m_bOverrideDepthEnable = false; m_CurrentState.m_bOverrideAlphaWriteEnable = false; m_CurrentState.m_bOverrideColorWriteEnable = false; m_CurrentState.m_ForceDepthFuncEquals = false; m_CurrentState.m_bLinearColorSpaceFrameBufferEnable = false; ApplyTransition( m_DefaultTransition, m_DefaultStateSnapshot ); ShaderManager()->SetVertexShader( INVALID_SHADER ); ShaderManager()->SetPixelShader( INVALID_SHADER ); m_CurrentSnapshotId = -1; } //----------------------------------------------------------------------------- // Snapshotted state overrides //----------------------------------------------------------------------------- void CTransitionTable::ForceDepthFuncEquals( bool bEnable ) { if( bEnable != m_CurrentState.m_ForceDepthFuncEquals ) { // Do this so that we can call this from within the rendering code // See OverrideDepthEnable + PerformShadowStateOverrides for a version // that isn't expected to be called from within rendering code if( !ShaderAPI()->IsRenderingMesh() ) { ShaderAPI()->FlushBufferedPrimitives(); } m_CurrentState.m_ForceDepthFuncEquals = bEnable; if( bEnable ) { SetZFunc( D3DCMP_EQUAL ); } else { if ( CurrentShadowState() ) { SetZFunc( CurrentShadowState()->m_ZFunc ); } } } } void CTransitionTable::OverrideDepthEnable( bool bEnable, bool bDepthEnable ) { if ( bEnable != m_CurrentState.m_bOverrideDepthEnable ) { ShaderAPI()->FlushBufferedPrimitives(); m_CurrentState.m_bOverrideDepthEnable = bEnable; m_CurrentState.m_OverrideZWriteEnable = bDepthEnable ? D3DZB_TRUE : D3DZB_FALSE; if ( m_CurrentState.m_bOverrideDepthEnable ) { SetZEnable( D3DZB_TRUE ); SetRenderStateConstMacro( D3DRS_ZWRITEENABLE, m_CurrentState.m_OverrideZWriteEnable ); #if defined( _X360 ) //SetRenderStateConstMacro( D3DRS_HIZWRITEENABLE, m_CurrentState.m_OverrideZWriteEnable ? D3DHIZ_AUTOMATIC : D3DHIZ_DISABLE ); #endif } else { if ( CurrentShadowState() ) { SetZEnable( CurrentShadowState()->m_ZEnable ); SetRenderStateConstMacro( D3DRS_ZWRITEENABLE, CurrentShadowState()->m_ZWriteEnable ); #if defined( _X360 ) //SetRenderStateConstMacro( D3DRS_HIZWRITEENABLE, CurrentShadowState()->m_ZWriteEnable ? D3DHIZ_AUTOMATIC : D3DHIZ_DISABLE ); #endif } } } } void CTransitionTable::OverrideAlphaWriteEnable( bool bOverrideEnable, bool bAlphaWriteEnable ) { if ( bOverrideEnable != m_CurrentState.m_bOverrideAlphaWriteEnable ) { ShaderAPI()->FlushBufferedPrimitives(); m_CurrentState.m_bOverrideAlphaWriteEnable = bOverrideEnable; m_CurrentState.m_bOverriddenAlphaWriteValue = bAlphaWriteEnable; DWORD dwSetValue = m_CurrentState.m_ColorWriteEnable; if ( m_CurrentState.m_bOverrideAlphaWriteEnable ) { if( m_CurrentState.m_bOverriddenAlphaWriteValue ) { dwSetValue |= D3DCOLORWRITEENABLE_ALPHA; } else { dwSetValue &= ~D3DCOLORWRITEENABLE_ALPHA; } } else { if ( CurrentShadowState() ) { //probably being paranoid, but only copy the alpha flag from the shadow state dwSetValue &= ~D3DCOLORWRITEENABLE_ALPHA; dwSetValue |= CurrentShadowState()->m_ColorWriteEnable & D3DCOLORWRITEENABLE_ALPHA; } } if( dwSetValue != m_CurrentState.m_ColorWriteEnable ) { m_CurrentState.m_ColorWriteEnable = dwSetValue; SetRenderState( D3DRS_COLORWRITEENABLE, m_CurrentState.m_ColorWriteEnable ); } } } void CTransitionTable::OverrideColorWriteEnable( bool bOverrideEnable, bool bColorWriteEnable ) { if ( bOverrideEnable != m_CurrentState.m_bOverrideColorWriteEnable ) { ShaderAPI()->FlushBufferedPrimitives(); m_CurrentState.m_bOverrideColorWriteEnable = bOverrideEnable; m_CurrentState.m_bOverriddenColorWriteValue = bColorWriteEnable; DWORD dwSetValue = m_CurrentState.m_ColorWriteEnable; if ( m_CurrentState.m_bOverrideColorWriteEnable ) { if( m_CurrentState.m_bOverriddenColorWriteValue ) { dwSetValue |= (D3DCOLORWRITEENABLE_RED | D3DCOLORWRITEENABLE_GREEN | D3DCOLORWRITEENABLE_BLUE); } else { dwSetValue &= ~(D3DCOLORWRITEENABLE_RED | D3DCOLORWRITEENABLE_GREEN | D3DCOLORWRITEENABLE_BLUE); } } else { if ( CurrentShadowState() ) { //probably being paranoid, but only copy the alpha flag from the shadow state dwSetValue &= ~(D3DCOLORWRITEENABLE_RED | D3DCOLORWRITEENABLE_GREEN | D3DCOLORWRITEENABLE_BLUE); dwSetValue |= CurrentShadowState()->m_ColorWriteEnable & (D3DCOLORWRITEENABLE_RED | D3DCOLORWRITEENABLE_GREEN | D3DCOLORWRITEENABLE_BLUE); } } if( dwSetValue != m_CurrentState.m_ColorWriteEnable ) { m_CurrentState.m_ColorWriteEnable = dwSetValue; SetRenderState( D3DRS_COLORWRITEENABLE, m_CurrentState.m_ColorWriteEnable ); } } } void CTransitionTable::EnableLinearColorSpaceFrameBuffer( bool bEnable ) { if ( m_CurrentState.m_bLinearColorSpaceFrameBufferEnable != bEnable && CurrentShadowState() ) { ShaderAPI()->FlushBufferedPrimitives(); m_CurrentState.m_bLinearColorSpaceFrameBufferEnable = bEnable; ApplySRGBWriteEnable( *CurrentShadowState() ); } } //----------------------------------------------------------------------------- // Perform state block overrides //----------------------------------------------------------------------------- void CTransitionTable::PerformShadowStateOverrides( ) { VPROF("CTransitionTable::PerformShadowStateOverrides"); // Deal with funky overrides here, because the state blocks can't... if ( m_CurrentState.m_ForceDepthFuncEquals ) { SetZFunc( D3DCMP_EQUAL ); } if ( m_CurrentState.m_bOverrideDepthEnable ) { SetZEnable( D3DZB_TRUE ); SetRenderStateConstMacro( D3DRS_ZWRITEENABLE, m_CurrentState.m_OverrideZWriteEnable ); #if defined( _X360 ) //SetRenderStateConstMacro( D3DRS_HIZWRITEENABLE, m_CurrentState.m_OverrideZWriteEnable ? D3DHIZ_AUTOMATIC : D3DHIZ_DISABLE ); #endif } if ( m_CurrentState.m_bOverrideAlphaWriteEnable ) { DWORD dwSetValue = m_CurrentState.m_ColorWriteEnable & ~D3DCOLORWRITEENABLE_ALPHA; dwSetValue |= m_CurrentState.m_bOverriddenAlphaWriteValue ? D3DCOLORWRITEENABLE_ALPHA : 0; if ( dwSetValue != m_CurrentState.m_ColorWriteEnable ) { m_CurrentState.m_ColorWriteEnable = dwSetValue; SetRenderState( D3DRS_COLORWRITEENABLE, m_CurrentState.m_ColorWriteEnable ); } } if ( m_CurrentState.m_bOverrideColorWriteEnable ) { DWORD dwSetValue = m_CurrentState.m_ColorWriteEnable & ~(D3DCOLORWRITEENABLE_RED | D3DCOLORWRITEENABLE_GREEN | D3DCOLORWRITEENABLE_BLUE); dwSetValue |= m_CurrentState.m_bOverriddenColorWriteValue ? (D3DCOLORWRITEENABLE_RED | D3DCOLORWRITEENABLE_GREEN | D3DCOLORWRITEENABLE_BLUE) : 0; if ( dwSetValue != m_CurrentState.m_ColorWriteEnable ) { m_CurrentState.m_ColorWriteEnable = dwSetValue; SetRenderState( D3DRS_COLORWRITEENABLE, m_CurrentState.m_ColorWriteEnable ); } } }