diff options
Diffstat (limited to 'materialsystem/shaderapidx9/TransitionTable.cpp')
| -rw-r--r-- | materialsystem/shaderapidx9/TransitionTable.cpp | 1924 |
1 files changed, 1924 insertions, 0 deletions
diff --git a/materialsystem/shaderapidx9/TransitionTable.cpp b/materialsystem/shaderapidx9/TransitionTable.cpp new file mode 100644 index 0000000..ab12389 --- /dev/null +++ b/materialsystem/shaderapidx9/TransitionTable.cpp @@ -0,0 +1,1924 @@ +//========= 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 ); + } + } +} |