You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1924 lines
65 KiB
1924 lines
65 KiB
//========= 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 ); |
|
} |
|
} |
|
}
|
|
|