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.
3786 lines
116 KiB
3786 lines
116 KiB
//========= Copyright Valve Corporation, All rights reserved. ============// |
|
// |
|
// Vertex/Pixel Shaders |
|
// |
|
//===========================================================================// |
|
#define DISABLE_PROTECTED_THINGS |
|
#if ( defined(_WIN32) && !defined( _X360 ) ) |
|
#elif POSIX |
|
#include <sys/types.h> |
|
#include <sys/socket.h> |
|
#include <netdb.h> |
|
#include <netinet/in.h> |
|
#include <netinet/tcp.h> |
|
#include <errno.h> |
|
#include <sys/ioctl.h> |
|
#define closesocket close |
|
#define WSAGetLastError() errno |
|
#undef SOCKET |
|
typedef int SOCKET; |
|
#define SOCKET_ERROR (-1) |
|
#define SD_SEND 0x01 |
|
#define INVALID_SOCKET (~0) |
|
#endif |
|
|
|
#include "togl/rendermechanism.h" |
|
#include "vertexshaderdx8.h" |
|
#include "tier1/utlsymbol.h" |
|
#include "tier1/utlvector.h" |
|
#include "tier1/utldict.h" |
|
#include "tier1/utllinkedlist.h" |
|
#include "tier1/utlbuffer.h" |
|
#include "tier1/UtlStringMap.h" |
|
#include "locald3dtypes.h" |
|
#include "shaderapidx8_global.h" |
|
#include "recording.h" |
|
#include "tier0/vprof.h" |
|
#include "materialsystem/imaterialsystem.h" |
|
#include "materialsystem/imaterialsystemhardwareconfig.h" |
|
#include "KeyValues.h" |
|
#include "shaderapidx8.h" |
|
#include "materialsystem/IShader.h" |
|
#include "IShaderSystem.h" |
|
#include "tier0/fasttimer.h" |
|
#include <sys/stat.h> |
|
#include <time.h> |
|
#include <stdlib.h> |
|
#include "filesystem.h" |
|
#include "convar.h" |
|
#include "materialsystem/shader_vcs_version.h" |
|
#include "tier1/lzmaDecoder.h" |
|
#include "tier1/utlmap.h" |
|
|
|
#include "datacache/idatacache.h" |
|
#include "tier1/diff.h" |
|
#include "shaderdevicedx8.h" |
|
#include "filesystem/IQueuedLoader.h" |
|
#include "tier2/tier2.h" |
|
#include "shaderapi/ishaderutil.h" |
|
#include "tier0/icommandline.h" |
|
|
|
#include "Color.h" |
|
#include "tier0/dbg.h" |
|
|
|
#ifdef REMOTE_DYNAMIC_SHADER_COMPILE |
|
|
|
# if defined (POSIX) |
|
|
|
# include <sys/types.h> |
|
# include <sys/socket.h> |
|
|
|
# else |
|
|
|
# include <winsock2.h> |
|
# include <ws2tcpip.h> |
|
|
|
# endif |
|
|
|
#endif |
|
|
|
// NOTE: This has to be the last file included! |
|
#include "tier0/memdbgon.h" |
|
|
|
|
|
// It currently includes windows.h and we don't want that. |
|
#ifdef USE_ACTUAL_DX |
|
|
|
#include "../utils/bzip2/bzlib.h" |
|
|
|
#else |
|
|
|
int BZ2_bzBuffToBuffDecompress( |
|
char* dest, |
|
unsigned int* destLen, |
|
char* source, |
|
unsigned int sourceLen, |
|
int small, |
|
int verbosity |
|
) |
|
{ |
|
return 0; |
|
} |
|
|
|
#endif |
|
|
|
static ConVar mat_remoteshadercompile( "mat_remoteshadercompile", "127.0.0.1", FCVAR_CHEAT ); |
|
|
|
//#define PROFILE_SHADER_CREATE |
|
|
|
//#define NO_AMBIENT_CUBE |
|
#define MAX_BONES 3 |
|
|
|
// debugging aid |
|
#define MAX_SHADER_HISTORY 16 |
|
|
|
#if !defined( _X360 ) |
|
#define SHADER_FNAME_EXTENSION ".vcs" |
|
#else |
|
#define SHADER_FNAME_EXTENSION ".360.vcs" |
|
#endif |
|
|
|
#ifdef DYNAMIC_SHADER_COMPILE |
|
volatile static char s_ShaderCompileString[]="dynamic_shader_compile_is_on"; |
|
#endif |
|
|
|
#ifdef DYNAMIC_SHADER_COMPILE |
|
static void MatFlushShaders( void ); |
|
#endif |
|
|
|
// D3D to OpenGL translator |
|
//static D3DToGL_ASM sg_D3DToOpenGLTranslator; // Remove the _ASM to switch to the new translator |
|
//static D3DToGL sg_NewD3DToOpenGLTranslator; // Remove the _ASM to switch to the new translator |
|
|
|
static const char *GetLightTypeName( VertexShaderLightTypes_t type ) |
|
{ |
|
static const char *s_VertexShaderLightTypeNames[] = |
|
{ |
|
"LIGHT_NONE", |
|
"LIGHT_SPOT", |
|
"LIGHT_POINT", |
|
"LIGHT_DIRECTIONAL", |
|
"LIGHT_STATIC", |
|
"LIGHT_AMBIENTCUBE", |
|
}; |
|
return s_VertexShaderLightTypeNames[type+1]; |
|
} |
|
|
|
#ifdef PROFILE_SHADER_CREATE |
|
static FILE *GetDebugFileHandle( void ) |
|
{ |
|
static FILE *fp = NULL; |
|
if( !fp ) |
|
{ |
|
fp = fopen( "shadercreate.txt", "w" ); |
|
Assert( fp ); |
|
} |
|
return fp; |
|
} |
|
#endif // PROFILE_SHADER_CREATE |
|
|
|
#ifdef DX_TO_GL_ABSTRACTION |
|
// mat_autoload_glshaders instructs the engine to load a cached shader table at startup |
|
// it will try for glshaders.cfg first, then fall back to glbaseshaders.cfg if not found |
|
// mat_autosave_glshaders instructs the engine to save out the shader table at key points |
|
// to the filename glshaders.cfg |
|
// |
|
|
|
ConVar mat_autosave_glshaders( "mat_autosave_glshaders", "1" ); |
|
ConVar mat_autoload_glshaders( "mat_autoload_glshaders", "1" ); |
|
#endif |
|
//----------------------------------------------------------------------------- |
|
// Explicit instantiation of shader buffer implementation |
|
//----------------------------------------------------------------------------- |
|
template class CShaderBuffer< ID3DXBuffer >; |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Used to find unique shaders |
|
//----------------------------------------------------------------------------- |
|
#ifdef MEASURE_DRIVER_ALLOCATIONS |
|
static CUtlMap< CRC32_t, int, int > s_UniqueVS( 0, 0, DefLessFunc( CRC32_t ) ); |
|
static CUtlMap< CRC32_t, int, int > s_UniquePS( 0, 0, DefLessFunc( CRC32_t ) ); |
|
static CUtlMap< IDirect3DVertexShader9*, CRC32_t, int > s_VSLookup( 0, 0, DefLessFunc( IDirect3DVertexShader9* ) ); |
|
static CUtlMap< IDirect3DPixelShader9*, CRC32_t, int > s_PSLookup( 0, 0, DefLessFunc( IDirect3DPixelShader9* ) ); |
|
#endif |
|
|
|
static int s_NumPixelShadersCreated = 0; |
|
static int s_NumVertexShadersCreated = 0; |
|
|
|
static void RegisterVS( const void* pShaderBits, int nShaderSize, IDirect3DVertexShader9* pShader ) |
|
{ |
|
#ifdef MEASURE_DRIVER_ALLOCATIONS |
|
CRC32_t crc; |
|
CRC32_Init( &crc ); |
|
CRC32_ProcessBuffer( &crc, pShaderBits, nShaderSize ); |
|
CRC32_Final( &crc ); |
|
|
|
s_VSLookup.Insert( pShader, crc ); |
|
|
|
int nIndex = s_UniqueVS.Find( crc ); |
|
if ( nIndex != s_UniqueVS.InvalidIndex() ) |
|
{ |
|
++s_UniqueVS[nIndex]; |
|
} |
|
else |
|
{ |
|
int nMemUsed = 23 * 1024; |
|
s_UniqueVS.Insert( crc, 1 ); |
|
VPROF_INCREMENT_GROUP_COUNTER( "unique vs count", COUNTER_GROUP_NO_RESET, 1 ); |
|
VPROF_INCREMENT_GROUP_COUNTER( "vs driver mem", COUNTER_GROUP_NO_RESET, nMemUsed ); |
|
VPROF_INCREMENT_GROUP_COUNTER( "total driver mem", COUNTER_GROUP_NO_RESET, nMemUsed ); |
|
} |
|
#endif |
|
} |
|
|
|
static void RegisterPS( const void* pShaderBits, int nShaderSize, IDirect3DPixelShader9* pShader ) |
|
{ |
|
#ifdef MEASURE_DRIVER_ALLOCATIONS |
|
CRC32_t crc; |
|
CRC32_Init( &crc ); |
|
CRC32_ProcessBuffer( &crc, pShaderBits, nShaderSize ); |
|
CRC32_Final( &crc ); |
|
|
|
s_PSLookup.Insert( pShader, crc ); |
|
|
|
int nIndex = s_UniquePS.Find( crc ); |
|
if ( nIndex != s_UniquePS.InvalidIndex() ) |
|
{ |
|
++s_UniquePS[nIndex]; |
|
} |
|
else |
|
{ |
|
int nMemUsed = 400; |
|
s_UniquePS.Insert( crc, 1 ); |
|
VPROF_INCREMENT_GROUP_COUNTER( "unique ps count", COUNTER_GROUP_NO_RESET, 1 ); |
|
VPROF_INCREMENT_GROUP_COUNTER( "ps driver mem", COUNTER_GROUP_NO_RESET, nMemUsed ); |
|
VPROF_INCREMENT_GROUP_COUNTER( "total driver mem", COUNTER_GROUP_NO_RESET, nMemUsed ); |
|
} |
|
#endif |
|
} |
|
|
|
static void UnregisterVS( IDirect3DVertexShader9* pShader ) |
|
{ |
|
#ifdef MEASURE_DRIVER_ALLOCATIONS |
|
int nCRCIndex = s_VSLookup.Find( pShader ); |
|
if ( nCRCIndex == s_VSLookup.InvalidIndex() ) |
|
return; |
|
|
|
CRC32_t crc = s_VSLookup[nCRCIndex]; |
|
s_VSLookup.RemoveAt( nCRCIndex ); |
|
|
|
int nIndex = s_UniqueVS.Find( crc ); |
|
if ( nIndex != s_UniqueVS.InvalidIndex() ) |
|
{ |
|
if ( --s_UniqueVS[nIndex] <= 0 ) |
|
{ |
|
int nMemUsed = 23 * 1024; |
|
VPROF_INCREMENT_GROUP_COUNTER( "unique vs count", COUNTER_GROUP_NO_RESET, -1 ); |
|
VPROF_INCREMENT_GROUP_COUNTER( "vs driver mem", COUNTER_GROUP_NO_RESET, -nMemUsed ); |
|
VPROF_INCREMENT_GROUP_COUNTER( "total driver mem", COUNTER_GROUP_NO_RESET, -nMemUsed ); |
|
s_UniqueVS.Remove( nIndex ); |
|
} |
|
} |
|
#endif |
|
} |
|
|
|
static void UnregisterPS( IDirect3DPixelShader9* pShader ) |
|
{ |
|
#ifdef MEASURE_DRIVER_ALLOCATIONS |
|
int nCRCIndex = s_PSLookup.Find( pShader ); |
|
if ( nCRCIndex == s_PSLookup.InvalidIndex() ) |
|
return; |
|
|
|
CRC32_t crc = s_PSLookup[nCRCIndex]; |
|
s_PSLookup.RemoveAt( nCRCIndex ); |
|
|
|
int nIndex = s_UniquePS.Find( crc ); |
|
if ( nIndex != s_UniquePS.InvalidIndex() ) |
|
{ |
|
if ( --s_UniquePS[nIndex] <= 0 ) |
|
{ |
|
int nMemUsed = 400; |
|
VPROF_INCREMENT_GROUP_COUNTER( "unique ps count", COUNTER_GROUP_NO_RESET, -1 ); |
|
VPROF_INCREMENT_GROUP_COUNTER( "ps driver mem", COUNTER_GROUP_NO_RESET, -nMemUsed ); |
|
VPROF_INCREMENT_GROUP_COUNTER( "total driver mem", COUNTER_GROUP_NO_RESET, -nMemUsed ); |
|
s_UniquePS.Remove( nIndex ); |
|
} |
|
} |
|
#endif |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// The lovely low-level dx call to create a vertex shader |
|
//----------------------------------------------------------------------------- |
|
static HardwareShader_t CreateD3DVertexShader( DWORD *pByteCode, int numBytes, const char *pShaderName, char *debugLabel = NULL ) |
|
{ |
|
MEM_ALLOC_D3D_CREDIT(); |
|
|
|
if ( !pByteCode ) |
|
{ |
|
Assert( 0 ); |
|
return INVALID_HARDWARE_SHADER; |
|
} |
|
|
|
// Compute the vertex specification |
|
HardwareShader_t hShader; |
|
|
|
#ifdef DX_TO_GL_ABSTRACTION |
|
HRESULT hr = Dx9Device()->CreateVertexShader( pByteCode, (IDirect3DVertexShader9 **)&hShader, pShaderName, debugLabel ); |
|
#else |
|
if ( IsEmulatingGL() ) |
|
{ |
|
DWORD dwVersion = D3DXGetShaderVersion( pByteCode ); |
|
REFERENCE( dwVersion ); |
|
Assert ( D3DSHADER_VERSION_MAJOR( dwVersion ) == 2 ); |
|
} |
|
|
|
#if defined(_X360) || !defined(DX_TO_GL_ABSTRACTION) |
|
HRESULT hr = Dx9Device()->CreateVertexShader( pByteCode, (IDirect3DVertexShader9 **)&hShader ); |
|
#else |
|
HRESULT hr = Dx9Device()->CreateVertexShader( pByteCode, (IDirect3DVertexShader9 **)&hShader, pShaderName ); |
|
#endif |
|
|
|
#endif |
|
|
|
// NOTE: This isn't recorded before the CreateVertexShader because |
|
// we don't know the value of shader until after the CreateVertexShader. |
|
RECORD_COMMAND( DX8_CREATE_VERTEX_SHADER, 3 ); |
|
RECORD_INT( ( int )hShader ); // hack hack hack |
|
RECORD_INT( numBytes ); |
|
RECORD_STRUCT( pByteCode, numBytes ); |
|
|
|
if ( FAILED( hr ) ) |
|
{ |
|
Assert( 0 ); |
|
hShader = INVALID_HARDWARE_SHADER; |
|
} |
|
else |
|
{ |
|
s_NumVertexShadersCreated++; |
|
RegisterVS( pByteCode, numBytes, (IDirect3DVertexShader9 *)hShader ); |
|
} |
|
return hShader; |
|
} |
|
|
|
static void PatchPixelShaderForAtiMsaaHack(DWORD *pShader, DWORD dwTexCoordMask) |
|
{ |
|
if ( IsPC() ) |
|
{ |
|
bool bIsSampler, bIsTexCoord; |
|
|
|
// Should be able to patch only ps2.0 |
|
if (*pShader != 0xFFFF0200) |
|
return; |
|
|
|
pShader++; |
|
|
|
while (pShader) |
|
{ |
|
switch (*pShader & D3DSI_OPCODE_MASK) |
|
{ |
|
case D3DSIO_COMMENT: |
|
// Process comment |
|
pShader = pShader + (*pShader >> 16) + 1; |
|
break; |
|
|
|
case D3DSIO_END: |
|
// End of shader |
|
return; |
|
|
|
case D3DSIO_DCL: |
|
bIsSampler = (*(pShader + 1) & D3DSP_TEXTURETYPE_MASK) != D3DSTT_UNKNOWN; |
|
bIsTexCoord = (((*(pShader + 2) & D3DSP_REGTYPE_MASK) >> D3DSP_REGTYPE_SHIFT) + |
|
((*(pShader + 2) & D3DSP_REGTYPE_MASK2) >> D3DSP_REGTYPE_SHIFT2)) == D3DSPR_TEXTURE; |
|
|
|
if (!bIsSampler && bIsTexCoord) |
|
{ |
|
DWORD dwTexCoord = *(pShader + 2) & D3DSP_REGNUM_MASK; |
|
DWORD mask = 0x01; |
|
for (DWORD i = 0; i < 16; i++) |
|
{ |
|
if (((dwTexCoordMask & mask) == mask) && (dwTexCoord == i)) |
|
{ |
|
// If found -- patch and get out |
|
// *(pShader + 2) |= D3DSPDM_PARTIALPRECISION; |
|
*(pShader + 2) |= D3DSPDM_MSAMPCENTROID; |
|
break; |
|
} |
|
mask <<= 1; |
|
} |
|
} |
|
// Intentionally fall through... |
|
|
|
default: |
|
// Skip instruction |
|
pShader = pShader + ((*pShader & D3DSI_INSTLENGTH_MASK) >> D3DSI_INSTLENGTH_SHIFT) + 1; |
|
} |
|
} |
|
} |
|
} |
|
|
|
static ConVar mat_force_ps_patch( "mat_force_ps_patch", "0" ); |
|
static ConVar mat_disable_ps_patch( "mat_disable_ps_patch", "0", FCVAR_ALLOWED_IN_COMPETITIVE ); |
|
|
|
//----------------------------------------------------------------------------- |
|
// The lovely low-level dx call to create a pixel shader |
|
//----------------------------------------------------------------------------- |
|
static HardwareShader_t CreateD3DPixelShader( DWORD *pByteCode, unsigned int nCentroidMask, int numBytes, const char* pShaderName, char *debugLabel = NULL ) |
|
{ |
|
MEM_ALLOC_D3D_CREDIT(); |
|
|
|
if ( !pByteCode ) |
|
return INVALID_HARDWARE_SHADER; |
|
|
|
if ( IsPC() && nCentroidMask && |
|
( HardwareConfig()->NeedsATICentroidHack() || |
|
mat_force_ps_patch.GetInt() ) ) |
|
{ |
|
if ( !mat_disable_ps_patch.GetInt() ) |
|
{ |
|
PatchPixelShaderForAtiMsaaHack( pByteCode, nCentroidMask ); |
|
} |
|
} |
|
|
|
HardwareShader_t shader; |
|
#if defined( DX_TO_GL_ABSTRACTION ) |
|
#if defined( OSX ) |
|
HRESULT hr = Dx9Device()->CreatePixelShader( pByteCode, ( IDirect3DPixelShader ** )&shader, pShaderName, debugLabel ); |
|
#else |
|
HRESULT hr = Dx9Device()->CreatePixelShader( pByteCode, ( IDirect3DPixelShader ** )&shader, pShaderName, debugLabel, &nCentroidMask ); |
|
#endif |
|
#else |
|
if ( IsEmulatingGL() ) |
|
{ |
|
DWORD dwVersion; |
|
dwVersion = D3DXGetShaderVersion( pByteCode ); |
|
Assert ( D3DSHADER_VERSION_MAJOR( dwVersion ) == 2 ); |
|
} |
|
#if defined(_X360) || !defined(DX_TO_GL_ABSTRACTION) |
|
HRESULT hr = Dx9Device()->CreatePixelShader( pByteCode, ( IDirect3DPixelShader ** )&shader ); |
|
#else |
|
HRESULT hr = Dx9Device()->CreatePixelShader( pByteCode, ( IDirect3DPixelShader ** )&shader, pShaderName ); |
|
#endif |
|
#endif |
|
|
|
// NOTE: We have to do this after creating the pixel shader since we don't know |
|
// lookup.m_PixelShader yet!!!!!!! |
|
RECORD_COMMAND( DX8_CREATE_PIXEL_SHADER, 3 ); |
|
RECORD_INT( ( int )shader ); // hack hack hack |
|
RECORD_INT( numBytes ); |
|
RECORD_STRUCT( pByteCode, numBytes ); |
|
|
|
if ( FAILED( hr ) ) |
|
{ |
|
Assert(0); |
|
shader = INVALID_HARDWARE_SHADER; |
|
} |
|
else |
|
{ |
|
s_NumPixelShadersCreated++; |
|
RegisterPS( pByteCode, numBytes, ( IDirect3DPixelShader9* )shader ); |
|
} |
|
|
|
return shader; |
|
} |
|
|
|
template<class T> int BinarySearchCombos( uint32 nStaticComboID, int nCombos, T const *pRecords ) |
|
{ |
|
// Use binary search - data is sorted |
|
int nLowerIdx = 1; |
|
int nUpperIdx = nCombos; |
|
for (;;) |
|
{ |
|
if ( nUpperIdx < nLowerIdx ) |
|
return -1; |
|
|
|
int nMiddleIndex = ( nLowerIdx + nUpperIdx ) / 2; |
|
uint32 nProbe = pRecords[nMiddleIndex-1].m_nStaticComboID; |
|
if ( nStaticComboID < nProbe ) |
|
{ |
|
nUpperIdx = nMiddleIndex - 1; |
|
} |
|
else |
|
{ |
|
if ( nStaticComboID > nProbe ) |
|
nLowerIdx = nMiddleIndex + 1; |
|
else |
|
return nMiddleIndex - 1; |
|
} |
|
} |
|
} |
|
|
|
inline int FindShaderStaticCombo( uint32 nStaticComboID, const ShaderHeader_t& header, StaticComboRecord_t *pRecords ) |
|
{ |
|
if ( header.m_nVersion < 5 ) |
|
return -1; |
|
|
|
return BinarySearchCombos( nStaticComboID, header.m_nNumStaticCombos, pRecords ); |
|
} |
|
|
|
// cache redundant i/o fetched components of the vcs files |
|
struct ShaderFileCache_t |
|
{ |
|
CUtlSymbol m_Name; |
|
CUtlSymbol m_Filename; |
|
ShaderHeader_t m_Header; |
|
bool m_bVertexShader; |
|
|
|
// valid for diff version only - contains the microcode used as the reference for diff algorithm |
|
CUtlBuffer m_ReferenceCombo; |
|
|
|
// valid for ver5 only - contains the directory |
|
CUtlVector< StaticComboRecord_t > m_StaticComboRecords; |
|
CUtlVector< StaticComboAliasRecord_t > m_StaticComboDupRecords; |
|
|
|
ShaderFileCache_t() |
|
{ |
|
// invalid until version established |
|
m_Header.m_nVersion = 0; |
|
} |
|
|
|
bool IsValid() const |
|
{ |
|
return m_Header.m_nVersion != 0; |
|
} |
|
|
|
bool IsOldVersion() const |
|
{ |
|
return m_Header.m_nVersion < 5; |
|
} |
|
|
|
int IsVersion6() const |
|
{ |
|
return ( m_Header.m_nVersion == 6 ); |
|
} |
|
|
|
int FindCombo( uint32 nStaticComboID ) |
|
{ |
|
int nSearchAliases = BinarySearchCombos( nStaticComboID, m_StaticComboDupRecords.Count(), m_StaticComboDupRecords.Base() ); |
|
if ( nSearchAliases != -1 ) |
|
nStaticComboID = m_StaticComboDupRecords[nSearchAliases].m_nSourceStaticCombo; |
|
return FindShaderStaticCombo( nStaticComboID, m_Header, m_StaticComboRecords.Base() ); |
|
} |
|
|
|
bool operator==( const ShaderFileCache_t& a ) const |
|
{ |
|
return m_Name == a.m_Name && m_bVertexShader == a.m_bVertexShader; |
|
} |
|
}; |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Vertex + pixel shader manager |
|
//----------------------------------------------------------------------------- |
|
class CShaderManager : public IShaderManager |
|
{ |
|
public: |
|
CShaderManager(); |
|
virtual ~CShaderManager(); |
|
|
|
// Methods of IShaderManager |
|
virtual void Init(); |
|
virtual void Shutdown(); |
|
virtual IShaderBuffer *CompileShader( const char *pProgram, size_t nBufLen, const char *pShaderVersion ); |
|
virtual VertexShaderHandle_t CreateVertexShader( IShaderBuffer* pShaderBuffer ); |
|
virtual void DestroyVertexShader( VertexShaderHandle_t hShader ); |
|
virtual PixelShaderHandle_t CreatePixelShader( IShaderBuffer* pShaderBuffer ); |
|
virtual void DestroyPixelShader( PixelShaderHandle_t hShader ); |
|
virtual VertexShader_t CreateVertexShader( const char *pVertexShaderFile, int nStaticVshIndex = 0, char *debugLabel = NULL ); |
|
virtual PixelShader_t CreatePixelShader( const char *pPixelShaderFile, int nStaticPshIndex = 0, char *debugLabel = NULL ); |
|
virtual void SetVertexShader( VertexShader_t shader ); |
|
virtual void SetPixelShader( PixelShader_t shader ); |
|
virtual void BindVertexShader( VertexShaderHandle_t shader ); |
|
virtual void BindPixelShader( PixelShaderHandle_t shader ); |
|
virtual void *GetCurrentVertexShader(); |
|
virtual void *GetCurrentPixelShader(); |
|
virtual void ResetShaderState(); |
|
void FlushShaders(); |
|
virtual void ClearVertexAndPixelShaderRefCounts(); |
|
virtual void PurgeUnusedVertexAndPixelShaders(); |
|
void SpewVertexAndPixelShaders(); |
|
const char *GetActiveVertexShaderName(); |
|
const char *GetActivePixelShaderName(); |
|
bool CreateDynamicCombos_Ver4( void *pContext, uint8 *pComboBuffer ); |
|
bool CreateDynamicCombos_Ver5( void *pContext, uint8 *pComboBuffer, char *debugLabel = NULL ); |
|
|
|
#if defined( DX_TO_GL_ABSTRACTION ) |
|
virtual void DoStartupShaderPreloading(); |
|
#endif |
|
|
|
static void QueuedLoaderCallback( void *pContext, void *pContext2, const void *pData, int nSize, LoaderError_t loaderError ); |
|
|
|
private: |
|
typedef CUtlFixedLinkedList< IDirect3DVertexShader9* >::IndexType_t VertexShaderIndex_t; |
|
typedef CUtlFixedLinkedList< IDirect3DPixelShader9* >::IndexType_t PixelShaderIndex_t; |
|
|
|
struct ShaderStaticCombos_t |
|
{ |
|
int m_nCount; |
|
|
|
// Can't use CUtlVector here since you CUtlLinkedList<CUtlVector<>> doesn't work. |
|
HardwareShader_t *m_pHardwareShaders; |
|
struct ShaderCreationData_t |
|
{ |
|
CUtlVector<uint8> ByteCode; |
|
uint32 iCentroidMask; |
|
}; |
|
|
|
ShaderCreationData_t *m_pCreationData; |
|
}; |
|
|
|
struct ShaderLookup_t |
|
{ |
|
CUtlSymbol m_Name; |
|
int m_nStaticIndex; |
|
ShaderStaticCombos_t m_ShaderStaticCombos; |
|
DWORD m_Flags; |
|
int m_nRefCount; |
|
uintp m_hShaderFileCache; |
|
|
|
// for queued loading, bias an aligned optimal buffer forward to correct location |
|
int m_nDataOffset; |
|
|
|
// diff version, valid during load only |
|
ShaderDictionaryEntry_t *m_pComboDictionary; |
|
|
|
ShaderLookup_t() |
|
{ |
|
m_Flags = 0; |
|
m_nRefCount = 0; |
|
m_ShaderStaticCombos.m_nCount = 0; |
|
m_ShaderStaticCombos.m_pHardwareShaders = 0; |
|
m_ShaderStaticCombos.m_pCreationData = 0; |
|
m_pComboDictionary = NULL; |
|
} |
|
void IncRefCount() |
|
{ |
|
m_nRefCount++; |
|
} |
|
bool operator==( const ShaderLookup_t& a ) const |
|
{ |
|
return m_Name == a.m_Name && m_nStaticIndex == a.m_nStaticIndex; |
|
} |
|
}; |
|
|
|
#ifdef DYNAMIC_SHADER_COMPILE |
|
struct Combo_t |
|
{ |
|
CUtlSymbol m_ComboName; |
|
int m_nMin; |
|
int m_nMax; |
|
}; |
|
|
|
struct ShaderCombos_t |
|
{ |
|
CUtlVector<Combo_t> m_StaticCombos; |
|
CUtlVector<Combo_t> m_DynamicCombos; |
|
int GetNumDynamicCombos( void ) const |
|
{ |
|
int combos = 1; |
|
int i; |
|
for( i = 0; i < m_DynamicCombos.Count(); i++ ) |
|
{ |
|
combos *= ( m_DynamicCombos[i].m_nMax - m_DynamicCombos[i].m_nMin + 1 ); |
|
} |
|
return combos; |
|
} |
|
int GetNumStaticCombos( void ) const |
|
{ |
|
int combos = 1; |
|
int i; |
|
for( i = 0; i < m_StaticCombos.Count(); i++ ) |
|
{ |
|
combos *= ( m_StaticCombos[i].m_nMax - m_StaticCombos[i].m_nMin + 1 ); |
|
} |
|
return combos; |
|
} |
|
}; |
|
#endif |
|
|
|
private: |
|
void CreateStaticShaders(); |
|
void DestroyStaticShaders(); |
|
|
|
#if defined ( DYNAMIC_SHADER_COMPILE ) && defined( REMOTE_DYNAMIC_SHADER_COMPILE ) |
|
void InitRemoteShaderCompile(); |
|
void DeinitRemoteShaderCompile(); |
|
#endif |
|
|
|
// The low-level dx call to set the vertex shader state |
|
void SetVertexShaderState( HardwareShader_t shader, DataCacheHandle_t hCachedShader = DC_INVALID_HANDLE ); |
|
|
|
// The low-level dx call to set the pixel shader state |
|
void SetPixelShaderState( HardwareShader_t shader, DataCacheHandle_t hCachedShader = DC_INVALID_HANDLE ); |
|
|
|
// Destroys all shaders |
|
void DestroyAllShaders(); |
|
|
|
// Destroy a particular vertex shader |
|
void DestroyVertexShader( VertexShader_t shader ); |
|
// Destroy a particular pixel shader |
|
void DestroyPixelShader( PixelShader_t shader ); |
|
|
|
bool LoadAndCreateShaders( ShaderLookup_t &lookup, bool bVertexShader, char *debugLabel = NULL ); |
|
FileHandle_t OpenFileAndLoadHeader( const char *pFileName, ShaderHeader_t *pHeader ); |
|
|
|
#ifdef DYNAMIC_SHADER_COMPILE |
|
bool LoadAndCreateShaders_Dynamic( ShaderLookup_t &lookup, bool bVertexShader ); |
|
const ShaderCombos_t *FindOrCreateShaderCombos( const char *pShaderName ); |
|
HardwareShader_t CompileShader( const char *pShaderName, int nStaticIndex, int nDynamicIndex, bool bVertexShader ); |
|
#endif |
|
|
|
void DisassembleShader( ShaderLookup_t *pLookup, int dynamicCombo, uint8 *pByteCode ); |
|
void WriteTranslatedFile( ShaderLookup_t *pLookup, int dynamicCombo, char *pFileContents, char *pFileExtension ); |
|
|
|
// DX_TO_GL_ABSTRACTION only, no-op otherwise |
|
|
|
void SaveShaderCache( char *cacheName ); // query GLM pair cache for all active shader pairs and write them to disk in named file |
|
bool LoadShaderCache( char *cacheName ); // read named file, establish compiled shader sets for each vertex+static and pixel+static, then link pairs as listed in table |
|
// return true on success, false if file not found |
|
|
|
// old void WarmShaderCache(); |
|
|
|
CUtlFixedLinkedList< ShaderLookup_t > m_VertexShaderDict; |
|
CUtlFixedLinkedList< ShaderLookup_t > m_PixelShaderDict; |
|
|
|
CUtlSymbolTable m_ShaderSymbolTable; |
|
|
|
#ifdef DYNAMIC_SHADER_COMPILE |
|
typedef HRESULT (__stdcall *ShaderCompileFromFileFunc_t)( LPCSTR pSrcFile, CONST D3DXMACRO* pDefines, |
|
LPD3DXINCLUDE pInclude, LPCSTR pFunctionName, LPCSTR pProfile, DWORD Flags, |
|
LPD3DXBUFFER* ppShader, LPD3DXBUFFER * ppErrorMsgs, LPD3DXCONSTANTTABLE * ppConstantTable ); |
|
CUtlStringMap<ShaderCombos_t> m_ShaderNameToCombos; |
|
CSysModule *m_pShaderCompiler30; |
|
ShaderCompileFromFileFunc_t m_ShaderCompileFileFunc30; |
|
#endif |
|
|
|
// The current vertex and pixel shader |
|
HardwareShader_t m_HardwareVertexShader; |
|
HardwareShader_t m_HardwarePixelShader; |
|
|
|
CUtlFixedLinkedList< IDirect3DVertexShader9* > m_RawVertexShaderDict; |
|
CUtlFixedLinkedList< IDirect3DPixelShader9* > m_RawPixelShaderDict; |
|
|
|
CUtlFixedLinkedList< ShaderFileCache_t > m_ShaderFileCache; |
|
|
|
// false, creates during init. |
|
// true, creates on access, helps reduce d3d memory for tools, but causes i/o hitches. |
|
bool m_bCreateShadersOnDemand; |
|
|
|
#if defined( _DEBUG ) |
|
// for debugging (can't resolve UtlSym) |
|
// need some history because 360 d3d has rips related to sequencing |
|
char vshDebugName[MAX_SHADER_HISTORY][64]; |
|
int vshDebugIndex; |
|
char pshDebugName[MAX_SHADER_HISTORY][64]; |
|
int pshDebugIndex; |
|
#endif |
|
|
|
#if defined ( DYNAMIC_SHADER_COMPILE ) && defined( REMOTE_DYNAMIC_SHADER_COMPILE ) |
|
SOCKET m_RemoteShaderCompileSocket; |
|
#endif |
|
|
|
}; |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Singleton accessor |
|
//----------------------------------------------------------------------------- |
|
static CShaderManager s_ShaderManager; |
|
IShaderManager *g_pShaderManager = &s_ShaderManager; |
|
|
|
//----------------------------------------------------------------------------- |
|
// Constructor, destructor |
|
//----------------------------------------------------------------------------- |
|
CShaderManager::CShaderManager() : |
|
m_ShaderSymbolTable( 0, 32, true /* caseInsensitive */ ), |
|
m_VertexShaderDict( 32 ), |
|
m_PixelShaderDict( 32 ), |
|
m_ShaderFileCache( 32 ) |
|
{ |
|
m_bCreateShadersOnDemand = false; |
|
|
|
#ifdef DYNAMIC_SHADER_COMPILE |
|
m_pShaderCompiler30 = 0; |
|
m_ShaderCompileFileFunc30 = 0; |
|
#ifdef REMOTE_DYNAMIC_SHADER_COMPILE |
|
m_RemoteShaderCompileSocket = INVALID_SOCKET; |
|
#endif |
|
#endif |
|
|
|
#ifdef _DEBUG |
|
vshDebugIndex = 0; |
|
pshDebugIndex = 0; |
|
#endif |
|
} |
|
|
|
CShaderManager::~CShaderManager() |
|
{ |
|
#if defined ( DYNAMIC_SHADER_COMPILE ) && defined( REMOTE_DYNAMIC_SHADER_COMPILE ) |
|
DeinitRemoteShaderCompile(); |
|
#endif |
|
} |
|
|
|
#define REMOTE_SHADER_COMPILE_PORT "20000" |
|
|
|
#if defined ( DYNAMIC_SHADER_COMPILE ) && defined( REMOTE_DYNAMIC_SHADER_COMPILE ) |
|
void CShaderManager::InitRemoteShaderCompile() |
|
{ |
|
DeinitRemoteShaderCompile(); |
|
|
|
int nResult = 0; |
|
#ifdef _WIN32 |
|
WSADATA wsaData; |
|
nResult = WSAStartup( 0x101, &wsaData ); |
|
if ( nResult != 0 ) |
|
{ |
|
Warning( "CShaderManager::Init - Could not init socket for remote dynamic shader compilation\n" ); |
|
} |
|
#endif |
|
|
|
struct addrinfo hints; |
|
ZeroMemory( &hints, sizeof(hints) ); |
|
hints.ai_family = AF_UNSPEC; |
|
hints.ai_socktype = SOCK_STREAM; |
|
hints.ai_protocol = IPPROTO_TCP; |
|
|
|
// Resolve the server address and port |
|
struct addrinfo *result = NULL; |
|
nResult = getaddrinfo( mat_remoteshadercompile.GetString(), REMOTE_SHADER_COMPILE_PORT, &hints, &result ); |
|
if ( nResult != 0 ) |
|
{ |
|
Warning( "getaddrinfo failed: %d\n", nResult ); |
|
#ifdef _WIN32 |
|
WSACleanup(); |
|
#endif |
|
Assert( 0 ); |
|
} |
|
|
|
// Attempt to connect to an address until one succeeds |
|
for( struct addrinfo *ptr = result; ptr != NULL; ptr = ptr->ai_next ) |
|
{ |
|
// Create a SOCKET for connecting to remote shader compilation server |
|
m_RemoteShaderCompileSocket = socket( ptr->ai_family, ptr->ai_socktype, ptr->ai_protocol ); |
|
if ( m_RemoteShaderCompileSocket == INVALID_SOCKET ) |
|
{ |
|
Warning( "Error at socket(): %ld\n", WSAGetLastError() ); |
|
freeaddrinfo( result ); |
|
#ifdef _WIN32 |
|
WSACleanup(); |
|
#endif |
|
Assert( 0 ); |
|
continue; |
|
} |
|
|
|
// Connect to server. |
|
nResult = connect( m_RemoteShaderCompileSocket, ptr->ai_addr, (int)ptr->ai_addrlen); |
|
if ( nResult == SOCKET_ERROR ) |
|
{ |
|
closesocket( m_RemoteShaderCompileSocket ); |
|
m_RemoteShaderCompileSocket = INVALID_SOCKET; |
|
continue; |
|
} |
|
break; |
|
} |
|
|
|
freeaddrinfo( result ); |
|
|
|
if ( m_RemoteShaderCompileSocket == INVALID_SOCKET ) |
|
{ |
|
Warning( "Unable to connect to remote shader compilation server!\n" ); |
|
#ifdef _WIN32 |
|
WSACleanup(); |
|
#endif |
|
Assert ( 0 ); |
|
} |
|
} |
|
|
|
void CShaderManager::DeinitRemoteShaderCompile() |
|
{ |
|
if ( m_RemoteShaderCompileSocket != INVALID_SOCKET ) |
|
{ |
|
if ( shutdown( m_RemoteShaderCompileSocket, SD_SEND ) == SOCKET_ERROR ) |
|
{ |
|
Warning( "Remote shader compilation shutdown failed: %d\n", WSAGetLastError() ); |
|
} |
|
closesocket( m_RemoteShaderCompileSocket ); |
|
m_RemoteShaderCompileSocket = INVALID_SOCKET; |
|
} |
|
} |
|
#endif // defined ( DYNAMIC_SHADER_COMPILE ) && defined( REMOTE_DYNAMIC_SHADER_COMPILE ) |
|
|
|
//----------------------------------------------------------------------------- |
|
// Initialization, shutdown |
|
//----------------------------------------------------------------------------- |
|
void CShaderManager::Init() |
|
{ |
|
// incompatible with the 360, violates loading system |
|
// only used by PC to help tools reduce d3d footprint |
|
m_bCreateShadersOnDemand = IsPC() && ( ShaderUtil()->InEditorMode() || CommandLine()->CheckParm( "-shadersondemand" ) ); |
|
|
|
#ifdef DYNAMIC_SHADER_COMPILE |
|
if( !IsX360() ) |
|
{ |
|
|
|
#ifdef REMOTE_DYNAMIC_SHADER_COMPILE |
|
InitRemoteShaderCompile(); |
|
#else // REMOTE_DYNAMIC_SHADER_COMPILE |
|
|
|
#ifdef _DEBUG |
|
m_pShaderCompiler30 = Sys_LoadModule( "d3dx9d_33.dll" ); |
|
#endif |
|
if (!m_pShaderCompiler30) |
|
{ |
|
m_pShaderCompiler30 = Sys_LoadModule( "d3dx9_33.dll" ); |
|
} |
|
|
|
if ( m_pShaderCompiler30 ) |
|
{ |
|
m_ShaderCompileFileFunc30 = (ShaderCompileFromFileFunc_t)GetProcAddress( (HMODULE)m_pShaderCompiler30, "D3DXCompileShaderFromFileA" ); |
|
} |
|
|
|
#endif |
|
|
|
} |
|
#endif // DYNAMIC_SHADER_COMPILE |
|
|
|
CreateStaticShaders(); |
|
} |
|
|
|
void CShaderManager::Shutdown() |
|
{ |
|
#ifdef DYNAMIC_SHADER_COMPILE |
|
if ( m_pShaderCompiler30 ) |
|
{ |
|
Sys_UnloadModule( m_pShaderCompiler30 ); |
|
m_pShaderCompiler30 = 0; |
|
m_ShaderCompileFileFunc30 = 0; |
|
} |
|
#endif |
|
|
|
#if defined (DX_TO_GL_ABSTRACTION) && !defined (ANDROID) |
|
|
|
if (mat_autosave_glshaders.GetInt()) |
|
{ |
|
SaveShaderCache("glshaders.cfg"); |
|
} |
|
#endif |
|
|
|
DestroyAllShaders(); |
|
DestroyStaticShaders(); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Compiles shaders |
|
//----------------------------------------------------------------------------- |
|
IShaderBuffer *CShaderManager::CompileShader( const char *pProgram, size_t nBufLen, const char *pShaderVersion ) |
|
{ |
|
int nCompileFlags = D3DXSHADER_AVOID_FLOW_CONTROL; |
|
|
|
#ifdef _DEBUG |
|
nCompileFlags |= D3DXSHADER_DEBUG; |
|
#endif |
|
|
|
LPD3DXBUFFER pCompiledShader, pErrorMessages; |
|
HRESULT hr = D3DXCompileShader( pProgram, nBufLen, |
|
NULL, NULL, "main", pShaderVersion, nCompileFlags, |
|
&pCompiledShader, &pErrorMessages, NULL ); |
|
|
|
if ( FAILED( hr ) ) |
|
{ |
|
if ( pErrorMessages ) |
|
{ |
|
const char *pErrorMessage = (const char *)pErrorMessages->GetBufferPointer(); |
|
Warning( "Shader compilation failed! Reported the following errors:\n%s\n", pErrorMessage ); |
|
pErrorMessages->Release(); |
|
} |
|
return NULL; |
|
} |
|
|
|
// NOTE: This uses small block heap allocator; so I'm not going |
|
// to bother creating a memory pool. |
|
CShaderBuffer< ID3DXBuffer > *pShaderBuffer = new CShaderBuffer< ID3DXBuffer >( pCompiledShader ); |
|
if ( pErrorMessages ) |
|
{ |
|
pErrorMessages->Release(); |
|
} |
|
|
|
return pShaderBuffer; |
|
} |
|
|
|
|
|
VertexShaderHandle_t CShaderManager::CreateVertexShader( IShaderBuffer* pShaderBuffer ) |
|
{ |
|
// Create the vertex shader |
|
IDirect3DVertexShader9 *pVertexShader = NULL; |
|
|
|
#if defined(_X360) || !defined(DX_TO_GL_ABSTRACTION) |
|
HRESULT hr = Dx9Device()->CreateVertexShader( (const DWORD*)pShaderBuffer->GetBits(), &pVertexShader ); |
|
#else |
|
HRESULT hr = Dx9Device()->CreateVertexShader( (const DWORD*)pShaderBuffer->GetBits(), &pVertexShader, NULL ); |
|
#endif |
|
|
|
if ( FAILED( hr ) || !pVertexShader ) |
|
return VERTEX_SHADER_HANDLE_INVALID; |
|
|
|
s_NumVertexShadersCreated++; |
|
RegisterVS( pShaderBuffer->GetBits(), pShaderBuffer->GetSize(), pVertexShader ); |
|
|
|
// Insert the shader into the dictionary of shaders |
|
VertexShaderIndex_t i = m_RawVertexShaderDict.AddToTail( pVertexShader ); |
|
return (VertexShaderHandle_t)i; |
|
} |
|
|
|
void CShaderManager::DestroyVertexShader( VertexShaderHandle_t hShader ) |
|
{ |
|
if ( hShader == VERTEX_SHADER_HANDLE_INVALID ) |
|
return; |
|
|
|
VertexShaderIndex_t i = (VertexShaderIndex_t)(uintp)hShader; |
|
IDirect3DVertexShader9 *pVertexShader = m_RawVertexShaderDict[ i ]; |
|
|
|
UnregisterVS( pVertexShader ); |
|
|
|
VerifyEquals( (int)pVertexShader->Release(), 0 ); |
|
m_RawVertexShaderDict.Remove( i ); |
|
} |
|
|
|
PixelShaderHandle_t CShaderManager::CreatePixelShader( IShaderBuffer* pShaderBuffer ) |
|
{ |
|
// Create the vertex shader |
|
IDirect3DPixelShader9 *pPixelShader = NULL; |
|
#if defined(_X360) || !defined(DX_TO_GL_ABSTRACTION) |
|
HRESULT hr = Dx9Device()->CreatePixelShader( (const DWORD*)pShaderBuffer->GetBits(), &pPixelShader ); |
|
#else |
|
HRESULT hr = Dx9Device()->CreatePixelShader( (const DWORD*)pShaderBuffer->GetBits(), &pPixelShader, NULL ); |
|
#endif |
|
|
|
if ( FAILED( hr ) || !pPixelShader ) |
|
return PIXEL_SHADER_HANDLE_INVALID; |
|
|
|
s_NumPixelShadersCreated++; |
|
|
|
RegisterPS( pShaderBuffer->GetBits(), pShaderBuffer->GetSize(), pPixelShader ); |
|
|
|
// Insert the shader into the dictionary of shaders |
|
PixelShaderIndex_t i = m_RawPixelShaderDict.AddToTail( pPixelShader ); |
|
return (PixelShaderHandle_t)i; |
|
} |
|
|
|
void CShaderManager::DestroyPixelShader( PixelShaderHandle_t hShader ) |
|
{ |
|
if ( hShader == PIXEL_SHADER_HANDLE_INVALID ) |
|
return; |
|
|
|
PixelShaderIndex_t i = (PixelShaderIndex_t)(uintp)hShader; |
|
IDirect3DPixelShader9 *pPixelShader = m_RawPixelShaderDict[ i ]; |
|
|
|
UnregisterPS( pPixelShader ); |
|
|
|
VerifyEquals( (int)pPixelShader->Release(), 0 ); |
|
m_RawPixelShaderDict.Remove( i ); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Globals |
|
//----------------------------------------------------------------------------- |
|
HardwareShader_t s_pIllegalMaterialPS = INVALID_HARDWARE_SHADER; |
|
|
|
//----------------------------------------------------------------------------- |
|
// Static methods |
|
//----------------------------------------------------------------------------- |
|
void CShaderManager::CreateStaticShaders() |
|
{ |
|
MEM_ALLOC_D3D_CREDIT(); |
|
|
|
if ( !HardwareConfig()->SupportsVertexAndPixelShaders() ) |
|
{ |
|
return; |
|
} |
|
|
|
if ( IsPC() ) |
|
{ |
|
// GR - hack for illegal materials |
|
const DWORD psIllegalMaterial[] = |
|
{ |
|
#ifdef DX_TO_GL_ABSTRACTION |
|
// Use a PS 2.0 binary shader on DX_TO_GL_ABSTRACTION |
|
0xffff0200, 0x05000051, 0xa00f0000, 0x3f800000, |
|
0x00000000, 0x3f800000, 0x3f800000, 0x02000001, |
|
0x800f0000, 0xa0e40000, 0x02000001, 0x800f0800, |
|
0x80e40000, 0x0000ffff |
|
#else |
|
0xffff0101, 0x00000051, 0xa00f0000, 0x00000000, 0x3f800000, 0x00000000, |
|
0x3f800000, 0x00000001, 0x800f0000, 0xa0e40000, 0x0000ffff |
|
#endif |
|
}; |
|
// create default shader |
|
#if defined(_X360) || !defined(DX_TO_GL_ABSTRACTION) |
|
Dx9Device()->CreatePixelShader( psIllegalMaterial, ( IDirect3DPixelShader9 ** )&s_pIllegalMaterialPS ); |
|
#else |
|
Dx9Device()->CreatePixelShader( psIllegalMaterial, ( IDirect3DPixelShader9 ** )&s_pIllegalMaterialPS, NULL ); |
|
#endif |
|
} |
|
} |
|
|
|
void CShaderManager::DestroyStaticShaders() |
|
{ |
|
// GR - invalid material hack |
|
// destroy internal shader |
|
if ( s_pIllegalMaterialPS != INVALID_HARDWARE_SHADER ) |
|
{ |
|
( ( IDirect3DPixelShader9 * )s_pIllegalMaterialPS )->Release(); |
|
s_pIllegalMaterialPS = INVALID_HARDWARE_SHADER; |
|
} |
|
} |
|
|
|
#ifdef DYNAMIC_SHADER_COMPILE |
|
static const char *GetShaderSourcePath( void ) |
|
{ |
|
static char shaderDir[MAX_PATH]; |
|
// GR - just in case init this... |
|
static bool bHaveShaderDir = false; |
|
if( !bHaveShaderDir ) |
|
{ |
|
bHaveShaderDir = true; |
|
# if ( defined( DYNAMIC_SHADER_COMPILE_CUSTOM_PATH ) ) |
|
{ |
|
Q_strncpy( shaderDir, DYNAMIC_SHADER_COMPILE_CUSTOM_PATH, MAX_PATH ); |
|
} |
|
# else |
|
{ |
|
# if ( defined( _X360 ) ) |
|
{ |
|
char hostName[128] = ""; |
|
const char *pHostName = CommandLine()->ParmValue( "-host" ); |
|
if ( !pHostName ) |
|
{ |
|
// the 360 machine name must be <HostPC>_360 |
|
DWORD length = sizeof( hostName ); |
|
DmGetXboxName( hostName, &length ); |
|
char *p = strstr( hostName, "_360" ); |
|
*p = '\0'; |
|
pHostName = hostName; |
|
} |
|
|
|
Q_snprintf( shaderDir, MAX_PATH, "net:\\smb\\%s\\stdshaders", pHostName ); |
|
} |
|
# else |
|
{ |
|
Q_strncpy( shaderDir, __FILE__, MAX_PATH ); |
|
Q_StripFilename( shaderDir ); |
|
Q_StripLastDir( shaderDir, MAX_PATH ); |
|
Q_strncat( shaderDir, "stdshaders", MAX_PATH, COPY_ALL_CHARACTERS ); |
|
} |
|
# endif |
|
} |
|
# endif |
|
} |
|
return shaderDir; |
|
} |
|
#endif |
|
|
|
#ifdef DYNAMIC_SHADER_COMPILE |
|
const CShaderManager::ShaderCombos_t *CShaderManager::FindOrCreateShaderCombos( const char *pShaderName ) |
|
{ |
|
if( m_ShaderNameToCombos.Defined( pShaderName ) ) |
|
{ |
|
return &m_ShaderNameToCombos[pShaderName]; |
|
} |
|
ShaderCombos_t &combos = m_ShaderNameToCombos[pShaderName]; |
|
char filename[MAX_PATH]; |
|
// try the vsh dir first. |
|
Q_strncpy( filename, GetShaderSourcePath(), MAX_PATH ); |
|
Q_strncat( filename, "\\", MAX_PATH, COPY_ALL_CHARACTERS ); |
|
Q_strncat( filename, pShaderName, MAX_PATH, COPY_ALL_CHARACTERS ); |
|
Q_strncat( filename, ".vsh", MAX_PATH, COPY_ALL_CHARACTERS ); |
|
CUtlInplaceBuffer bffr( 0, 0, CUtlInplaceBuffer::TEXT_BUFFER ); |
|
|
|
bool bOpenResult = g_pFullFileSystem->ReadFile( filename, NULL, bffr ); |
|
|
|
if ( bOpenResult ) |
|
{ |
|
NULL; |
|
} |
|
else |
|
{ |
|
// try the fxc dir. |
|
Q_strncpy( filename, GetShaderSourcePath(), MAX_PATH ); |
|
Q_strncat( filename, "\\", MAX_PATH, COPY_ALL_CHARACTERS ); |
|
Q_strncat( filename, pShaderName, MAX_PATH, COPY_ALL_CHARACTERS ); |
|
Q_strncat( filename, ".fxc", MAX_PATH, COPY_ALL_CHARACTERS ); |
|
bOpenResult = g_pFullFileSystem->ReadFile( filename, NULL, bffr ); |
|
|
|
if ( !bOpenResult ) |
|
{ |
|
// Maybe this is a specific version [20 & 20b] -> [2x] |
|
if ( Q_strlen( pShaderName ) >= 3 ) |
|
{ |
|
char *pszEndFilename = filename + strlen( filename ); |
|
if ( !Q_stricmp( pszEndFilename - 6, "30.fxc" ) ) |
|
{ |
|
// Total hack. Who knows what builds that 30 shader? |
|
strcpy( pszEndFilename - 6, "20b.fxc" ); |
|
bOpenResult = g_pFullFileSystem->ReadFile( filename, NULL, bffr ); |
|
if ( !bOpenResult ) |
|
{ |
|
strcpy( pszEndFilename - 6, "2x.fxc" ); |
|
bOpenResult = g_pFullFileSystem->ReadFile( filename, NULL, bffr ); |
|
} |
|
if ( !bOpenResult ) |
|
{ |
|
strcpy( pszEndFilename - 6, "20.fxc" ); |
|
bOpenResult = g_pFullFileSystem->ReadFile( filename, NULL, bffr ); |
|
} |
|
} |
|
else |
|
{ |
|
if ( !stricmp( pszEndFilename - 6, "20.fxc" ) ) |
|
{ |
|
pszEndFilename[ -5 ] = 'x'; |
|
} |
|
else if ( !stricmp( pszEndFilename - 7, "20b.fxc" ) ) |
|
{ |
|
strcpy( pszEndFilename - 7, "2x.fxc" ); |
|
--pszEndFilename; |
|
} |
|
else if ( !stricmp( pszEndFilename - 6, "11.fxc" ) ) |
|
{ |
|
strcpy( pszEndFilename - 6, "xx.fxc" ); |
|
} |
|
|
|
bOpenResult = g_pFullFileSystem->ReadFile( filename, NULL, bffr ); |
|
if ( !bOpenResult ) |
|
{ |
|
if ( !stricmp( pszEndFilename - 6, "2x.fxc" ) ) |
|
{ |
|
pszEndFilename[ -6 ] = 'x'; |
|
bOpenResult = g_pFullFileSystem->ReadFile( filename, NULL, bffr ); |
|
} |
|
} |
|
} |
|
} |
|
} |
|
|
|
if ( !bOpenResult ) |
|
{ |
|
Assert( 0 ); |
|
return NULL; |
|
} |
|
} |
|
|
|
while( char *line = bffr.InplaceGetLinePtr() ) |
|
{ |
|
// dear god perl is better at this kind of shit! |
|
int begin = 0; |
|
int end = 0; |
|
|
|
// check if the line starts with '//' |
|
if( line[0] != '/' || line[1] != '/' ) |
|
{ |
|
continue; |
|
} |
|
|
|
// Check if line intended for platform lines |
|
if( IsX360() ) |
|
{ |
|
if ( Q_stristr( line, "[PC]" ) ) |
|
continue; |
|
} |
|
else |
|
{ |
|
if ( Q_stristr( line, "[360]" ) || Q_stristr( line, "[XBOX]" ) ) |
|
continue; |
|
} |
|
|
|
// Skip any lines intended for other shader version |
|
if ( Q_stristr( pShaderName, "_ps20" ) && !Q_stristr( pShaderName, "_ps20b" ) && |
|
Q_stristr( line, "[ps" ) && !Q_stristr( line, "[ps20]" ) ) |
|
continue; |
|
if ( Q_stristr( pShaderName, "_ps20b" ) && |
|
Q_stristr( line, "[ps" ) && !Q_stristr( line, "[ps20b]" ) ) |
|
continue; |
|
if ( Q_stristr( pShaderName, "_ps30" ) && |
|
Q_stristr( line, "[ps" ) && !Q_stristr( line, "[ps30]" ) ) |
|
continue; |
|
if ( Q_stristr( pShaderName, "_vs20" ) && |
|
Q_stristr( line, "[vs" ) && !Q_stristr( line, "[vs20]" ) ) |
|
continue; |
|
if ( Q_stristr( pShaderName, "_vs30" ) && |
|
Q_stristr( line, "[vs" ) && !Q_stristr( line, "[vs30]" ) ) |
|
continue; |
|
|
|
char *pScan = &line[2]; |
|
while( *pScan == ' ' || *pScan == '\t' ) |
|
{ |
|
pScan++; |
|
} |
|
|
|
bool bDynamic; |
|
if( Q_strncmp( pScan, "DYNAMIC", 7 ) == 0 ) |
|
{ |
|
bDynamic = true; |
|
pScan += 7; |
|
} |
|
else if( Q_strncmp( pScan, "STATIC", 6 ) == 0 ) |
|
{ |
|
bDynamic = false; |
|
pScan += 6; |
|
} |
|
else |
|
{ |
|
continue; |
|
} |
|
|
|
// skip whitespace |
|
while( *pScan == ' ' || *pScan == '\t' ) |
|
{ |
|
pScan++; |
|
} |
|
|
|
// check for colon |
|
if( *pScan != ':' ) |
|
{ |
|
continue; |
|
} |
|
pScan++; |
|
|
|
// skip whitespace |
|
while( *pScan == ' ' || *pScan == '\t' ) |
|
{ |
|
pScan++; |
|
} |
|
|
|
// check for quote |
|
if( *pScan != '\"' ) |
|
{ |
|
continue; |
|
} |
|
pScan++; |
|
|
|
char *pBeginningOfName = pScan; |
|
while( 1 ) |
|
{ |
|
if( *pScan == '\0' ) |
|
{ |
|
break; |
|
} |
|
if( *pScan == '\"' ) |
|
{ |
|
break; |
|
} |
|
pScan++; |
|
} |
|
|
|
if( *pScan == '\0' ) |
|
{ |
|
continue; |
|
} |
|
|
|
// must have hit a quote. .done with string. |
|
// slam a NULL at the end quote of the string so that we have the string at pBeginningOfName. |
|
*pScan = '\0'; |
|
pScan++; |
|
|
|
// skip whitespace |
|
while( *pScan == ' ' || *pScan == '\t' ) |
|
{ |
|
pScan++; |
|
} |
|
|
|
// check for quote |
|
if( *pScan != '\"' ) |
|
{ |
|
continue; |
|
} |
|
pScan++; |
|
|
|
// make sure that we have a number after the quote. |
|
if( !isdigit( *pScan ) ) |
|
{ |
|
continue; |
|
} |
|
|
|
while( isdigit( *pScan ) ) |
|
{ |
|
begin = begin * 10 + ( *pScan - '0' ); |
|
pScan++; |
|
} |
|
|
|
if( pScan[0] != '.' || pScan[1] != '.' ) |
|
{ |
|
continue; |
|
} |
|
pScan += 2; |
|
|
|
// make sure that we have a number |
|
if( !isdigit( *pScan ) ) |
|
{ |
|
continue; |
|
} |
|
|
|
while( isdigit( *pScan ) ) |
|
{ |
|
end = end * 10 + ( *pScan - '0' ); |
|
pScan++; |
|
} |
|
|
|
if( pScan[0] != '\"' ) |
|
{ |
|
continue; |
|
} |
|
|
|
// sweet freaking jesus. .done parsing the line. |
|
// char buf[1024]; |
|
// sprintf( buf, "\"%s\" \"%s\" %d %d\n", bDynamic ? "DYNAMIC" : "STATIC", pBeginningOfName, begin, end ); |
|
// Plat_DebugString( buf ); |
|
|
|
Combo_t *pCombo = NULL; |
|
if( bDynamic ) |
|
{ |
|
pCombo = &combos.m_DynamicCombos[combos.m_DynamicCombos.AddToTail()]; |
|
} |
|
else |
|
{ |
|
pCombo = &combos.m_StaticCombos[combos.m_StaticCombos.AddToTail()]; |
|
} |
|
|
|
pCombo->m_ComboName = m_ShaderSymbolTable.AddString( pBeginningOfName ); |
|
pCombo->m_nMin = begin; |
|
pCombo->m_nMax = end; |
|
} |
|
|
|
return &combos; |
|
} |
|
#endif // DYNAMIC_SHADER_COMPILE |
|
|
|
#ifdef DYNAMIC_SHADER_COMPILE |
|
#ifndef DX_TO_GL_ABSTRACTION |
|
//----------------------------------------------------------------------------- |
|
// Used to deal with include files |
|
//----------------------------------------------------------------------------- |
|
class CDxInclude : public ID3DXInclude |
|
{ |
|
public: |
|
CDxInclude( const char *pMainFileName ); |
|
|
|
#if defined( _X360 ) |
|
virtual HRESULT WINAPI Open( D3DXINCLUDE_TYPE IncludeType, LPCSTR pFileName, LPCVOID pParentData, LPCVOID * ppData, UINT * pBytes, LPSTR pFullPath, DWORD cbFullPath ); |
|
#else |
|
STDMETHOD(Open)(THIS_ D3DXINCLUDE_TYPE IncludeType, LPCSTR pFileName, LPCVOID pParentData, LPCVOID *ppData, UINT *pBytes); |
|
#endif |
|
|
|
STDMETHOD(Close)(THIS_ LPCVOID pData); |
|
|
|
private: |
|
char m_pBasePath[MAX_PATH]; |
|
|
|
#if defined( _X360 ) |
|
char m_pFullPath[MAX_PATH]; |
|
#endif |
|
}; |
|
|
|
CDxInclude::CDxInclude( const char *pMainFileName ) |
|
{ |
|
Q_ExtractFilePath( pMainFileName, m_pBasePath, sizeof(m_pBasePath) ); |
|
} |
|
|
|
|
|
#if defined( _X360 ) |
|
HRESULT CDxInclude::Open( D3DXINCLUDE_TYPE IncludeType, LPCSTR pFileName, LPCVOID pParentData, LPCVOID * ppData, UINT * pBytes, LPSTR pFullPath, DWORD cbFullPath ) |
|
#else |
|
HRESULT CDxInclude::Open( D3DXINCLUDE_TYPE IncludeType, LPCSTR pFileName, LPCVOID pParentData, LPCVOID * ppData, UINT * pBytes ) |
|
#endif |
|
{ |
|
char pTemp[MAX_PATH]; |
|
if ( !Q_IsAbsolutePath( pFileName ) && ( IncludeType == D3DXINC_LOCAL ) ) |
|
{ |
|
Q_ComposeFileName( m_pBasePath, pFileName, pTemp, sizeof(pTemp) ); |
|
pFileName = pTemp; |
|
} |
|
|
|
CUtlBuffer buf( 0, 0, CUtlBuffer::TEXT_BUFFER ); |
|
if ( !g_pFullFileSystem->ReadFile( pFileName, NULL, buf ) ) |
|
return E_FAIL; |
|
|
|
*pBytes = buf.TellMaxPut(); |
|
void *pMem = malloc( *pBytes ); |
|
memcpy( pMem, buf.Base(), *pBytes ); |
|
*ppData = pMem; |
|
|
|
# if ( defined( _X360 ) ) |
|
{ |
|
Q_ComposeFileName( m_pBasePath, pFileName, m_pFullPath, sizeof(m_pFullPath) ); |
|
pFullPath = m_pFullPath; |
|
cbFullPath = MAX_PATH; |
|
} |
|
# endif |
|
|
|
return S_OK; |
|
} |
|
|
|
HRESULT CDxInclude::Close( LPCVOID pData ) |
|
{ |
|
void *pMem = const_cast<void*>( pData ); |
|
free( pMem ); |
|
return S_OK; |
|
} |
|
#endif // not DX_TO_GL_ABSTRACTION |
|
|
|
static const char *FileNameToShaderModel( const char *pShaderName, bool bVertexShader ) |
|
{ |
|
// Figure out the shader model |
|
const char *pShaderModel = NULL; |
|
if( bVertexShader ) |
|
{ |
|
if( Q_stristr( pShaderName, "vs20" ) ) |
|
{ |
|
pShaderModel = "vs_2_0"; |
|
bVertexShader = true; |
|
} |
|
else if( Q_stristr( pShaderName, "vs11" ) ) |
|
{ |
|
pShaderModel = "vs_1_1"; |
|
bVertexShader = true; |
|
} |
|
else if( Q_stristr( pShaderName, "vs14" ) ) |
|
{ |
|
pShaderModel = "vs_1_1"; |
|
bVertexShader = true; |
|
} |
|
else if( Q_stristr( pShaderName, "vs30" ) ) |
|
{ |
|
pShaderModel = "vs_3_0"; |
|
bVertexShader = true; |
|
} |
|
else |
|
{ |
|
#ifdef _DEBUG |
|
Error( "Failed dynamic shader compiled\nBuild shaderapidx9.dll in debug to find problem\n" ); |
|
#else |
|
Assert( 0 ); |
|
#endif |
|
} |
|
} |
|
else |
|
{ |
|
if( Q_stristr( pShaderName, "ps20b" ) ) |
|
{ |
|
pShaderModel = "ps_2_b"; |
|
} |
|
else if( Q_stristr( pShaderName, "ps20" ) ) |
|
{ |
|
pShaderModel = "ps_2_0"; |
|
} |
|
else if( Q_stristr( pShaderName, "ps11" ) ) |
|
{ |
|
pShaderModel = "ps_1_1"; |
|
} |
|
else if( Q_stristr( pShaderName, "ps14" ) ) |
|
{ |
|
pShaderModel = "ps_1_4"; |
|
} |
|
else if( Q_stristr( pShaderName, "ps30" ) ) |
|
{ |
|
pShaderModel = "ps_3_0"; |
|
} |
|
else |
|
{ |
|
#ifdef _DEBUG |
|
Error( "Failed dynamic shader compiled\nBuild shaderapidx9.dll in debug to find problem\n" ); |
|
#else |
|
Assert( 0 ); |
|
#endif |
|
} |
|
} |
|
return pShaderModel; |
|
} |
|
#endif |
|
|
|
#ifdef DYNAMIC_SHADER_COMPILE |
|
|
|
#if defined( _X360 ) |
|
static ConVar mat_flushshaders_generate_updbs( "mat_flushshaders_generate_updbs", "0", 0, "Generates UPDBs whenever you flush shaders." ); |
|
#endif |
|
|
|
HardwareShader_t CShaderManager::CompileShader( const char *pShaderName, |
|
int nStaticIndex, int nDynamicIndex, bool bVertexShader ) |
|
{ |
|
VPROF_BUDGET( "CompileShader", "CompileShader" ); |
|
Assert( m_ShaderNameToCombos.Defined( pShaderName ) ); |
|
if( !m_ShaderNameToCombos.Defined( pShaderName ) ) |
|
{ |
|
return INVALID_HARDWARE_SHADER; |
|
} |
|
const ShaderCombos_t &combos = m_ShaderNameToCombos[pShaderName]; |
|
#ifdef _DEBUG |
|
int numStaticCombos = combos.GetNumStaticCombos(); |
|
int numDynamicCombos = combos.GetNumDynamicCombos(); |
|
#endif |
|
Assert( nStaticIndex % numDynamicCombos == 0 ); |
|
Assert( ( nStaticIndex % numDynamicCombos ) >= 0 && ( nStaticIndex % numDynamicCombos ) < numStaticCombos ); |
|
Assert( nDynamicIndex >= 0 && nDynamicIndex < numDynamicCombos ); |
|
|
|
# ifdef DYNAMIC_SHADER_COMPILE_VERBOSE |
|
|
|
//Warning( "Compiling %s %s\n\tdynamic:", bVertexShader ? "vsh" : "psh", pShaderName ); |
|
Warning( "Compiling " ); |
|
if ( bVertexShader ) |
|
ConColorMsg( Color( 0, 255, 0, 255 ), "vsh - %s ", pShaderName ); |
|
else |
|
ConColorMsg( Color( 0, 255, 255, 255 ), "psh - %s ", pShaderName ); |
|
Warning( "\n\tdynamic:" ); |
|
|
|
# endif |
|
|
|
CUtlVector<D3DXMACRO> macros; |
|
// plus 1 for null termination, plus 1 for #define SHADER_MODEL_*, and plus 1 for #define _X360 on 360 |
|
macros.SetCount( combos.m_DynamicCombos.Count() + combos.m_StaticCombos.Count() + 2 + ( IsX360() ? 1 : 0 ) ); |
|
|
|
int nCombo = nStaticIndex + nDynamicIndex; |
|
int macroIndex = 0; |
|
int i; |
|
for( i = 0; i < combos.m_DynamicCombos.Count(); i++ ) |
|
{ |
|
int countForCombo = combos.m_DynamicCombos[i].m_nMax - combos.m_DynamicCombos[i].m_nMin + 1; |
|
int val = nCombo % countForCombo + combos.m_DynamicCombos[i].m_nMin; |
|
nCombo /= countForCombo; |
|
macros[macroIndex].Name = m_ShaderSymbolTable.String( combos.m_DynamicCombos[i].m_ComboName ); |
|
char buf[16]; |
|
sprintf( buf, "%d", val ); |
|
CUtlSymbol valSymbol( buf ); |
|
macros[macroIndex].Definition = valSymbol.String(); |
|
# ifdef DYNAMIC_SHADER_COMPILE_VERBOSE |
|
Warning( " %s=%s", macros[macroIndex].Name, macros[macroIndex].Definition ); |
|
# endif |
|
macroIndex++; |
|
} |
|
|
|
# ifdef DYNAMIC_SHADER_COMPILE_VERBOSE |
|
Warning( "\n\tstatic:" ); |
|
# endif |
|
for( i = 0; i < combos.m_StaticCombos.Count(); i++ ) |
|
{ |
|
int countForCombo = combos.m_StaticCombos[i].m_nMax - combos.m_StaticCombos[i].m_nMin + 1; |
|
int val = nCombo % countForCombo + combos.m_StaticCombos[i].m_nMin; |
|
nCombo /= countForCombo; |
|
macros[macroIndex].Name = m_ShaderSymbolTable.String( combos.m_StaticCombos[i].m_ComboName ); |
|
char buf[16]; |
|
sprintf( buf, "%d", val ); |
|
CUtlSymbol valSymbol( buf ); |
|
macros[macroIndex].Definition = valSymbol.String(); |
|
# ifdef DYNAMIC_SHADER_COMPILE_VERBOSE |
|
Warning( " %s=%s", macros[macroIndex].Name, macros[macroIndex].Definition ); |
|
# endif |
|
macroIndex++; |
|
} |
|
|
|
# ifdef DYNAMIC_SHADER_COMPILE_VERBOSE |
|
Warning( "\n" ); |
|
# endif |
|
|
|
char filename[MAX_PATH]; |
|
Q_strncpy( filename, GetShaderSourcePath(), MAX_PATH ); |
|
Q_strncat( filename, "\\", MAX_PATH, COPY_ALL_CHARACTERS ); |
|
Q_strncat( filename, pShaderName, MAX_PATH, COPY_ALL_CHARACTERS ); |
|
Q_strncat( filename, ".fxc", MAX_PATH, COPY_ALL_CHARACTERS ); |
|
|
|
const char *pShaderModel = FileNameToShaderModel( pShaderName, bVertexShader ); |
|
|
|
// define the shader model |
|
char shaderModelDefineString[1024]; |
|
Q_snprintf( shaderModelDefineString, 1024, "SHADER_MODEL_%s", pShaderModel ); |
|
Q_strupr( shaderModelDefineString ); |
|
macros[macroIndex].Name = shaderModelDefineString; |
|
macros[macroIndex].Definition = "1"; |
|
macroIndex++; |
|
|
|
char x360DefineString[1024]; |
|
if( IsX360() ) |
|
{ |
|
Q_snprintf( x360DefineString, 1024, "_X360", pShaderModel ); |
|
Q_strupr( x360DefineString ); |
|
macros[macroIndex].Name = x360DefineString; |
|
macros[macroIndex].Definition = "1"; |
|
macroIndex++; |
|
} |
|
|
|
// NULL terminate. |
|
macros[macroIndex].Name = NULL; |
|
macros[macroIndex].Definition = NULL; |
|
|
|
// Instead of erroring out, infinite-loop on shader compilation |
|
// (i.e. give developers a chance to fix the shader code w/out restarting the game) |
|
#ifndef _DEBUG |
|
int retriesLeft = 20; |
|
retry_compile: |
|
#endif |
|
|
|
// Try and open the file to see if it exists |
|
FileHandle_t fp = g_pFullFileSystem->Open( filename, "r" ); |
|
|
|
if ( fp == FILESYSTEM_INVALID_HANDLE ) |
|
{ |
|
// Maybe this is a specific version [20 & 20b] -> [2x] |
|
if ( strlen( pShaderName ) >= 3 ) |
|
{ |
|
char *pszEndFilename = filename + strlen( filename ); |
|
if ( !Q_stricmp( pszEndFilename - 6, "30.fxc" ) ) |
|
{ |
|
strcpy( pszEndFilename - 6, "20b.fxc" ); |
|
fp = g_pFullFileSystem->Open( filename, "r" ); |
|
if ( fp == FILESYSTEM_INVALID_HANDLE ) |
|
{ |
|
strcpy( pszEndFilename - 6, "2x.fxc" ); |
|
fp = g_pFullFileSystem->Open( filename, "r" ); |
|
} |
|
if ( fp == FILESYSTEM_INVALID_HANDLE ) |
|
{ |
|
strcpy( pszEndFilename - 6, "20.fxc" ); |
|
fp = g_pFullFileSystem->Open( filename, "r" ); |
|
} |
|
} |
|
else |
|
{ |
|
if ( !Q_stricmp( pszEndFilename - 6, "20.fxc" ) ) |
|
{ |
|
pszEndFilename[ -5 ] = 'x'; |
|
fp = g_pFullFileSystem->Open( filename, "r" ); |
|
} |
|
else if ( !Q_stricmp( pszEndFilename - 7, "20b.fxc" ) ) |
|
{ |
|
strcpy( pszEndFilename - 7, "2x.fxc" ); |
|
fp = g_pFullFileSystem->Open( filename, "r" ); |
|
} |
|
else if ( !stricmp( pszEndFilename - 6, "11.fxc" ) ) |
|
{ |
|
strcpy( pszEndFilename - 6, "xx.fxc" ); |
|
fp = g_pFullFileSystem->Open( filename, "r" ); |
|
} |
|
|
|
if ( fp == FILESYSTEM_INVALID_HANDLE ) |
|
{ |
|
if ( !stricmp( pszEndFilename - 6, "2x.fxc" ) ) |
|
{ |
|
pszEndFilename[ -6 ] = 'x'; |
|
fp = g_pFullFileSystem->Open( filename, "r" ); |
|
} |
|
} |
|
} |
|
} |
|
} |
|
|
|
if ( fp != FILESYSTEM_INVALID_HANDLE ) |
|
{ |
|
g_pFullFileSystem->Close( fp ); |
|
} |
|
|
|
#ifdef REMOTE_DYNAMIC_SHADER_COMPILE |
|
#define SEND_BUF_SIZE 40000 |
|
#define RECV_BUF_SIZE 40000 |
|
|
|
// Remotely-compiled shader code |
|
uint32 *pRemotelyCompiledShader = NULL; |
|
uint32 nRemotelyCompiledShaderLength = 0; |
|
|
|
if ( m_RemoteShaderCompileSocket == INVALID_SOCKET ) |
|
{ |
|
InitRemoteShaderCompile(); |
|
} |
|
|
|
// In this case, we're going to use a remote service to do our compiling |
|
if ( m_RemoteShaderCompileSocket != INVALID_SOCKET ) |
|
{ |
|
// Build up command list for remote shader compiler |
|
char pSendbuf[SEND_BUF_SIZE], pRecvbuf[RECV_BUF_SIZE], pFixedFilename[MAX_PATH], buf[MAX_PATH]; |
|
V_FixupPathName( pFixedFilename, MAX_PATH, filename ); |
|
V_FileBase( pFixedFilename, buf, MAX_PATH ); // Just find base filename |
|
V_strncat( buf, ".fxc", MAX_PATH ); |
|
V_snprintf( pSendbuf, SEND_BUF_SIZE, "%s\n", buf ); |
|
V_strncat( pSendbuf, pShaderModel, SEND_BUF_SIZE ); |
|
V_strncat( pSendbuf, "\n", SEND_BUF_SIZE ); |
|
V_snprintf( buf, MAX_PATH, "%d\n", macros.Count() ); |
|
V_strncat( pSendbuf, buf, SEND_BUF_SIZE ); |
|
for ( int i=0; i < macros.Count(); i++ ) |
|
{ |
|
V_snprintf( buf, MAX_PATH, "%s\n%s\n", macros[i].Name, macros[i].Definition ); |
|
V_strncat( pSendbuf, buf, SEND_BUF_SIZE ); |
|
} |
|
V_strncat( pSendbuf, "", SEND_BUF_SIZE ); |
|
|
|
// Send commands to remote shader compiler |
|
int nResult = send( m_RemoteShaderCompileSocket, pSendbuf, (int)strlen( pSendbuf ), 0 ); |
|
if ( nResult == SOCKET_ERROR ) |
|
{ |
|
Warning( "send failed: %d\n", WSAGetLastError() ); |
|
DeinitRemoteShaderCompile(); |
|
} |
|
|
|
if ( m_RemoteShaderCompileSocket != INVALID_SOCKET ) |
|
{ |
|
// Block here until we get a result back from the server |
|
nResult = recv( m_RemoteShaderCompileSocket, pRecvbuf, RECV_BUF_SIZE, 0 ); |
|
if ( nResult == 0 ) |
|
{ |
|
Warning( "Connection closed\n" ); |
|
DeinitRemoteShaderCompile(); |
|
} |
|
else if ( nResult < 0 ) |
|
{ |
|
Warning( "recv failed: %d\n", WSAGetLastError() ); |
|
DeinitRemoteShaderCompile(); |
|
} |
|
|
|
if ( m_RemoteShaderCompileSocket != INVALID_SOCKET ) |
|
{ |
|
// Grab the first 32 bits, which tell us what the rest of the data is |
|
uint32 nCompileResultCode; |
|
memcpy( &nCompileResultCode, pRecvbuf, sizeof( nCompileResultCode ) ); |
|
|
|
// If is zero, we have an error, so the rest of the data is a text string from the compiler |
|
if ( nCompileResultCode == 0x00000000 ) |
|
{ |
|
Warning( "Remote shader compile error: %s\n", pRecvbuf+4 ); |
|
} |
|
else // we have an actual binary shader blob coming back |
|
{ |
|
nRemotelyCompiledShaderLength = nCompileResultCode; |
|
pRemotelyCompiledShader = (uint32 *) pRecvbuf; |
|
pRemotelyCompiledShader++; |
|
} |
|
} |
|
} |
|
} // End using remote compile service |
|
#endif // REMOTE_DYNAMIC_SHADER_COMPILE |
|
|
|
#if defined( DYNAMIC_SHADER_COMPILE ) |
|
bool bShadersNeedFlush = false; |
|
#endif |
|
|
|
#if defined( DYNAMIC_SHADER_COMPILE ) && !defined( REMOTE_DYNAMIC_SHADER_COMPILE ) |
|
LPD3DXBUFFER pShader = NULL; |
|
LPD3DXBUFFER pErrorMessages = NULL; |
|
HRESULT hr = S_OK; |
|
bool b30Shader = !Q_stricmp( pShaderModel, "vs_3_0" ) || !Q_stricmp( pShaderModel, "ps_3_0" ); |
|
|
|
if ( m_ShaderCompileFileFunc30 && b30Shader ) |
|
{ |
|
CDxInclude dxInclude( filename ); |
|
hr = m_ShaderCompileFileFunc30( filename, macros.Base(), &dxInclude, |
|
"main", pShaderModel, 0 /* DWORD Flags */, &pShader, &pErrorMessages, NULL /* LPD3DXCONSTANTTABLE *ppConstantTable */ ); |
|
} |
|
else |
|
{ |
|
# if ( !defined( _X360 ) ) |
|
{ |
|
if ( b30Shader ) |
|
{ |
|
Warning( "Compiling with a stale version of d3dx. Should have d3d9x_33.dll installed (Apr 2007)\n" ); |
|
} |
|
hr = D3DXCompileShaderFromFile( filename, macros.Base(), NULL /* LPD3DXINCLUDE */, |
|
"main", pShaderModel, 0 /* DWORD Flags */, &pShader, &pErrorMessages, NULL /* LPD3DXCONSTANTTABLE *ppConstantTable */ ); |
|
|
|
|
|
#ifdef REMOTE_DYNAMIC_SHADER_COMPILE |
|
// If we're using the remote compiling service, let's double-check against a local compile |
|
if ( ( m_RemoteShaderCompileSocket != INVALID_SOCKET ) && pRemotelyCompiledShader ) |
|
{ |
|
if ( ( memcmp( pRemotelyCompiledShader, pShader->GetBufferPointer(), pShader->GetBufferSize() ) != 0 ) || |
|
( pShader->GetBufferSize() != nRemotelyCompiledShaderLength) ) |
|
{ |
|
Warning( "Remote and local shaders don't match!\n" ); |
|
return INVALID_HARDWARE_SHADER; |
|
} |
|
} |
|
#endif // REMOTE_DYNAMIC_SHADER_COMPILE |
|
|
|
} |
|
# else |
|
{ |
|
D3DXSHADER_COMPILE_PARAMETERS compileParams; |
|
memset( &compileParams, 0, sizeof( compileParams ) ); |
|
|
|
char pUPDBOutputFile[MAX_PATH] = ""; //where we write the file |
|
char pUPDBPIXLookup[MAX_PATH] = ""; //where PIX (on a pc) looks for the file |
|
|
|
compileParams.Flags |= D3DXSHADEREX_OPTIMIZE_UCODE; |
|
|
|
if( mat_flushshaders_generate_updbs.GetBool() ) |
|
{ |
|
//UPDB generation for PIX debugging |
|
compileParams.Flags |= D3DXSHADEREX_GENERATE_UPDB; |
|
compileParams.UPDBPath = pUPDBPIXLookup; |
|
|
|
Q_snprintf( pUPDBOutputFile, MAX_PATH, "%s\\UPDB_X360\\%s_S%d_D%d.updb", GetShaderSourcePath(), pShaderName, nStaticIndex, nDynamicIndex ); |
|
|
|
//replace "net:\smb" with another "\" turning the xbox network address format into the pc network address format |
|
V_strcpy_safe( pUPDBPIXLookup, &pUPDBOutputFile[7] ); |
|
pUPDBPIXLookup[0] = '\\'; |
|
} |
|
|
|
hr = D3DXCompileShaderFromFileEx( filename, macros.Base(), NULL /* LPD3DXINCLUDE */, |
|
"main", pShaderModel, 0 /* DWORD Flags */, &pShader, &pErrorMessages, NULL /* LPD3DXCONSTANTTABLE *ppConstantTable */, &compileParams ); |
|
|
|
if( (pUPDBOutputFile[0] != '\0') && compileParams.pUPDBBuffer ) //Did we generate a updb? |
|
{ |
|
CUtlBuffer outbuffer; |
|
DWORD dataSize = compileParams.pUPDBBuffer->GetBufferSize(); |
|
outbuffer.EnsureCapacity( dataSize ); |
|
memcpy( outbuffer.Base(), compileParams.pUPDBBuffer->GetBufferPointer(), dataSize ); |
|
outbuffer.SeekPut( CUtlBuffer::SEEK_CURRENT, dataSize ); |
|
g_pFullFileSystem->WriteFile( pUPDBOutputFile, NULL, outbuffer ); |
|
|
|
compileParams.pUPDBBuffer->Release(); |
|
} |
|
} |
|
# endif |
|
} |
|
|
|
if ( hr != D3D_OK ) |
|
{ |
|
const char *pErrorMessageString = ( const char * )pErrorMessages->GetBufferPointer(); |
|
Plat_DebugString( pErrorMessageString ); |
|
Plat_DebugString( "\n" ); |
|
|
|
#ifndef _DEBUG |
|
if ( retriesLeft-- > 0 ) |
|
{ |
|
DevMsg( 0, "Failed dynamic shader compiled - fix the shader while the debugger is at the breakpoint, then continue\n" ); |
|
DebuggerBreakIfDebugging(); |
|
#if defined( DYNAMIC_SHADER_COMPILE ) |
|
bShadersNeedFlush = true; |
|
#endif |
|
goto retry_compile; |
|
} |
|
if( !IsX360() ) //errors make the 360 puke and die. We have a better solution for this particular error |
|
Error( "Failed dynamic shader compile\nBuild shaderapidx9.dll in debug to find problem\n" ); |
|
#else |
|
Assert( 0 ); |
|
|
|
#endif // _DEBUG |
|
|
|
return INVALID_HARDWARE_SHADER; |
|
} |
|
else |
|
#endif // #if defined( DYNAMIC_SHADER_COMPILE ) && !defined( REMOTE_DYNAMIC_SHADER_COMPILE ) |
|
|
|
{ |
|
#ifdef DYNAMIC_SHADER_COMPILE_WRITE_ASSEMBLY |
|
// enable to dump the disassembly for shader validation |
|
char exampleCommandLine[2048]; |
|
Q_strncpy( exampleCommandLine, "// Run from stdshaders\n// ..\\..\\dx9sdk\\utilities\\fxc.exe ", sizeof( exampleCommandLine ) ); |
|
int i; |
|
for( i = 0; macros[i].Name; i++ ) |
|
{ |
|
Q_strncat( exampleCommandLine, "/D", sizeof( exampleCommandLine ) ); |
|
Q_strncat( exampleCommandLine, macros[i].Name, sizeof( exampleCommandLine ) ); |
|
Q_strncat( exampleCommandLine, "=", sizeof( exampleCommandLine ) ); |
|
Q_strncat( exampleCommandLine, macros[i].Definition, sizeof( exampleCommandLine ) ); |
|
Q_strncat( exampleCommandLine, " ", sizeof( exampleCommandLine ) ); |
|
} |
|
|
|
Q_strncat( exampleCommandLine, "/T", sizeof( exampleCommandLine ) ); |
|
Q_strncat( exampleCommandLine, pShaderModel, sizeof( exampleCommandLine ) ); |
|
Q_strncat( exampleCommandLine, " ", sizeof( exampleCommandLine ) ); |
|
Q_strncat( exampleCommandLine, filename, sizeof( exampleCommandLine ) ); |
|
Q_strncat( exampleCommandLine, "\n", sizeof( exampleCommandLine ) ); |
|
|
|
ID3DXBuffer *pd3dxBuffer; |
|
HRESULT hr; |
|
hr = D3DXDisassembleShader( ( DWORD* )pShader->GetBufferPointer(), false, NULL, &pd3dxBuffer ); |
|
Assert( hr == D3D_OK ); |
|
CUtlBuffer tempBuffer; |
|
tempBuffer.SetBufferType( true, false ); |
|
int exampleCommandLineLength = strlen( exampleCommandLine ); |
|
tempBuffer.EnsureCapacity( pd3dxBuffer->GetBufferSize() + exampleCommandLineLength ); |
|
memcpy( tempBuffer.Base(), exampleCommandLine, exampleCommandLineLength ); |
|
memcpy( ( char * )tempBuffer.Base() + exampleCommandLineLength, pd3dxBuffer->GetBufferPointer(), pd3dxBuffer->GetBufferSize() ); |
|
tempBuffer.SeekPut( CUtlBuffer::SEEK_CURRENT, pd3dxBuffer->GetBufferSize() + exampleCommandLineLength ); |
|
char filename[MAX_PATH]; |
|
sprintf( filename, "%s_%d_%d.asm", pShaderName, nStaticIndex, nDynamicIndex ); |
|
g_pFullFileSystem->WriteFile( filename, "DEFAULT_WRITE_PATH", tempBuffer ); |
|
#endif |
|
|
|
#ifdef REMOTE_DYNAMIC_SHADER_COMPILE |
|
if ( bVertexShader ) |
|
{ |
|
return CreateD3DVertexShader( ( DWORD * )pRemotelyCompiledShader, nRemotelyCompiledShaderLength, pShaderName ); |
|
} |
|
else |
|
{ |
|
return CreateD3DPixelShader( ( DWORD * )pRemotelyCompiledShader, 0, nRemotelyCompiledShaderLength, pShaderName ); // hack hack hack! need to get centroid info from the source |
|
} |
|
#else // local compile, not remote |
|
if ( bVertexShader ) |
|
{ |
|
return CreateD3DVertexShader( ( DWORD * )pShader->GetBufferPointer(), pShader->GetBufferSize(), pShaderName ); |
|
} |
|
else |
|
{ |
|
return CreateD3DPixelShader( ( DWORD * )pShader->GetBufferPointer(), 0, pShader->GetBufferSize(), pShaderName ); // hack hack hack! need to get centroid info from the source |
|
} |
|
#endif |
|
|
|
#if defined( DYNAMIC_SHADER_COMPILE ) |
|
// We keep up with whether we hit a compile error above. If we did, then we likely need to recompile everything again since we could have changed global code. |
|
if ( bShadersNeedFlush ) |
|
{ |
|
MatFlushShaders(); |
|
} |
|
#endif |
|
} |
|
|
|
#ifndef REMOTE_DYNAMIC_SHADER_COMPILE |
|
if ( pShader ) |
|
{ |
|
pShader->Release(); |
|
} |
|
#endif |
|
|
|
#if defined( DYNAMIC_SHADER_COMPILE ) && !defined( REMOTE_DYNAMIC_SHADER_COMPILE ) |
|
if ( pErrorMessages ) |
|
{ |
|
pErrorMessages->Release(); |
|
} |
|
#endif |
|
|
|
} |
|
#endif |
|
|
|
#ifdef DYNAMIC_SHADER_COMPILE |
|
bool CShaderManager::LoadAndCreateShaders_Dynamic( ShaderLookup_t &lookup, bool bVertexShader ) |
|
{ |
|
const char *pName = m_ShaderSymbolTable.String( lookup.m_Name ); |
|
const ShaderCombos_t *pCombos = FindOrCreateShaderCombos( pName ); |
|
if ( !pCombos ) |
|
{ |
|
return false; |
|
} |
|
|
|
int numDynamicCombos = pCombos->GetNumDynamicCombos(); |
|
lookup.m_ShaderStaticCombos.m_pHardwareShaders = new HardwareShader_t[numDynamicCombos]; |
|
lookup.m_ShaderStaticCombos.m_nCount = numDynamicCombos; |
|
lookup.m_ShaderStaticCombos.m_pCreationData = new ShaderStaticCombos_t::ShaderCreationData_t[numDynamicCombos]; |
|
|
|
int i; |
|
for( i = 0; i < numDynamicCombos; i++ ) |
|
{ |
|
lookup.m_ShaderStaticCombos.m_pHardwareShaders[i] = INVALID_HARDWARE_SHADER; |
|
} |
|
return true; |
|
} |
|
#endif |
|
|
|
//----------------------------------------------------------------------------- |
|
// Open the shader file, optionally gets the header |
|
//----------------------------------------------------------------------------- |
|
FileHandle_t CShaderManager::OpenFileAndLoadHeader( const char *pFileName, ShaderHeader_t *pHeader ) |
|
{ |
|
FileHandle_t fp = g_pFullFileSystem->Open( pFileName, "rb", "GAME" ); |
|
if ( fp == FILESYSTEM_INVALID_HANDLE ) |
|
{ |
|
return FILESYSTEM_INVALID_HANDLE; |
|
} |
|
|
|
if ( pHeader ) |
|
{ |
|
// read the header |
|
g_pFullFileSystem->Read( pHeader, sizeof( ShaderHeader_t ), fp ); |
|
|
|
switch ( pHeader->m_nVersion ) |
|
{ |
|
case 4: |
|
// version with combos done as diffs vs a reference combo |
|
// vsh/psh or older fxc |
|
break; |
|
|
|
case 5: |
|
case 6: |
|
// version with optimal dictionary and compressed combo block |
|
break; |
|
|
|
default: |
|
Assert( 0 ); |
|
Warning( "Shader %s is the wrong version %d, expecting %d\n", pFileName, pHeader->m_nVersion, SHADER_VCS_VERSION_NUMBER ); |
|
g_pFullFileSystem->Close( fp ); |
|
return FILESYSTEM_INVALID_HANDLE; |
|
} |
|
} |
|
|
|
return fp; |
|
} |
|
|
|
//--------------------------------------------------------------------------------------------------------- |
|
// Writes text files named for looked-up shaders. Used by GL shader translator to dump code for debugging |
|
//--------------------------------------------------------------------------------------------------------- |
|
void CShaderManager::WriteTranslatedFile( ShaderLookup_t *pLookup, int dynamicCombo, char *pFileContents, char *pFileExtension ) |
|
{ |
|
const char *pName = m_ShaderSymbolTable.String( pLookup->m_Name ); |
|
int nNumChars = V_strlen( pFileContents ); |
|
|
|
CUtlBuffer tempBuffer; |
|
tempBuffer.SetBufferType( true, false ); |
|
tempBuffer.EnsureCapacity( nNumChars ); |
|
memcpy( ( char * )tempBuffer.Base(), pFileContents, nNumChars ); |
|
tempBuffer.SeekPut( CUtlBuffer::SEEK_CURRENT, nNumChars ); |
|
|
|
char filename[MAX_PATH]; |
|
sprintf( filename, "%s_%d_%d.%s", pName, pLookup->m_nStaticIndex, dynamicCombo, pFileExtension ); |
|
g_pFullFileSystem->WriteFile( filename, "DEFAULT_WRITE_PATH", tempBuffer ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Disassemble a shader for debugging. Writes .asm files. |
|
//----------------------------------------------------------------------------- |
|
void CShaderManager::DisassembleShader( ShaderLookup_t *pLookup, int dynamicCombo, uint8 *pByteCode ) |
|
{ |
|
#if defined( WRITE_ASSEMBLY ) |
|
const char *pName = m_ShaderSymbolTable.String( pLookup->m_Name ); |
|
|
|
ID3DXBuffer *pd3dxBuffer; |
|
HRESULT hr; |
|
hr = D3DXDisassembleShader( (DWORD*)pByteCode, false, NULL, &pd3dxBuffer ); |
|
Assert( hr == D3D_OK ); |
|
|
|
CUtlBuffer tempBuffer; |
|
tempBuffer.SetBufferType( true, false ); |
|
tempBuffer.EnsureCapacity( pd3dxBuffer->GetBufferSize() ); |
|
memcpy( ( char * )tempBuffer.Base(), pd3dxBuffer->GetBufferPointer(), pd3dxBuffer->GetBufferSize() ); |
|
tempBuffer.SeekPut( CUtlBuffer::SEEK_CURRENT, pd3dxBuffer->GetBufferSize() ); |
|
|
|
char filename[MAX_PATH]; |
|
sprintf( filename, "%s_%d_%d.asm", pName, pLookup->m_nStaticIndex, dynamicCombo ); |
|
g_pFullFileSystem->WriteFile( filename, "DEFAULT_WRITE_PATH", tempBuffer ); |
|
#endif |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Create dynamic combos |
|
//----------------------------------------------------------------------------- |
|
bool CShaderManager::CreateDynamicCombos_Ver4( void *pContext, uint8 *pComboBuffer ) |
|
{ |
|
ShaderLookup_t* pLookup = (ShaderLookup_t *)pContext; |
|
|
|
ShaderFileCache_t *pFileCache = &m_ShaderFileCache[pLookup->m_hShaderFileCache]; |
|
ShaderHeader_t *pHeader = &pFileCache->m_Header; |
|
|
|
int nReferenceComboSizeForDiffs = ((ShaderHeader_t_v4 *)pHeader)->m_nDiffReferenceSize; |
|
|
|
uint8 *pReferenceShader = NULL; |
|
uint8 *pDiffOutputBuffer = NULL; |
|
if ( nReferenceComboSizeForDiffs ) |
|
{ |
|
// reference combo is *always* the largest combo, so safe worst case size for uncompression buffer |
|
pReferenceShader = (uint8 *)pFileCache->m_ReferenceCombo.Base(); |
|
pDiffOutputBuffer = (uint8 *)_alloca( nReferenceComboSizeForDiffs ); |
|
} |
|
|
|
// build this shader's dynamic combos |
|
bool bOK = true; |
|
int nStartingOffset = 0; |
|
for ( int i = 0; i < pHeader->m_nDynamicCombos; i++ ) |
|
{ |
|
if ( pLookup->m_pComboDictionary[i].m_Offset == -1 ) |
|
{ |
|
// skipped |
|
continue; |
|
} |
|
|
|
if ( !nStartingOffset ) |
|
{ |
|
nStartingOffset = pLookup->m_pComboDictionary[i].m_Offset; |
|
} |
|
|
|
// offsets better be sequentially ascending |
|
Assert( nStartingOffset <= pLookup->m_pComboDictionary[i].m_Offset ); |
|
|
|
if ( pLookup->m_pComboDictionary[i].m_Size <= 0 ) |
|
{ |
|
// skipped |
|
continue; |
|
} |
|
|
|
// get the right byte code from the monolithic buffer |
|
uint8 *pByteCode = (uint8 *)pComboBuffer + pLookup->m_nDataOffset + pLookup->m_pComboDictionary[i].m_Offset - nStartingOffset; |
|
int nByteCodeSize = pLookup->m_pComboDictionary[i].m_Size; |
|
|
|
if ( pReferenceShader ) |
|
{ |
|
// reference combo better be the largest combo, otherwise memory corruption |
|
Assert( nReferenceComboSizeForDiffs >= nByteCodeSize ); |
|
|
|
// use the differencing algorithm to recover the full shader |
|
int nOriginalSize; |
|
ApplyDiffs( |
|
pReferenceShader, |
|
pByteCode, |
|
nReferenceComboSizeForDiffs, |
|
nByteCodeSize, |
|
nOriginalSize, |
|
pDiffOutputBuffer, |
|
nReferenceComboSizeForDiffs ); |
|
|
|
pByteCode = pDiffOutputBuffer; |
|
nByteCodeSize = nOriginalSize; |
|
} |
|
|
|
#if defined( WRITE_ASSEMBLY ) |
|
DisassembleShader( pLookup, i, pByteCode ); |
|
#endif |
|
HardwareShader_t hardwareShader = INVALID_HARDWARE_SHADER; |
|
|
|
if ( IsPC() && m_bCreateShadersOnDemand ) |
|
{ |
|
// cache the code off for later |
|
pLookup->m_ShaderStaticCombos.m_pCreationData[i].ByteCode.SetSize( nByteCodeSize ); |
|
V_memcpy( pLookup->m_ShaderStaticCombos.m_pCreationData[i].ByteCode.Base(), pByteCode, nByteCodeSize ); |
|
pLookup->m_ShaderStaticCombos.m_pCreationData[i].iCentroidMask = pFileCache->m_bVertexShader ? 0 : pHeader->m_nCentroidMask; |
|
} |
|
else |
|
{ |
|
const char *pShaderName = m_ShaderSymbolTable.String( pLookup->m_Name ); |
|
if ( pFileCache->m_bVertexShader ) |
|
{ |
|
hardwareShader = CreateD3DVertexShader( reinterpret_cast< DWORD *>( pByteCode ), nByteCodeSize, pShaderName ); |
|
} |
|
else |
|
{ |
|
hardwareShader = CreateD3DPixelShader( reinterpret_cast< DWORD *>( pByteCode ), pHeader->m_nCentroidMask, nByteCodeSize, pShaderName ); |
|
} |
|
|
|
if ( hardwareShader == INVALID_HARDWARE_SHADER ) |
|
{ |
|
Assert( 0 ); |
|
bOK = false; |
|
break; |
|
} |
|
} |
|
pLookup->m_ShaderStaticCombos.m_pHardwareShaders[i] = hardwareShader; |
|
} |
|
|
|
delete [] pLookup->m_pComboDictionary; |
|
pLookup->m_pComboDictionary = NULL; |
|
|
|
return bOK; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Create dynamic combos |
|
//----------------------------------------------------------------------------- |
|
static uint32 NextULONG( uint8 * &pData ) |
|
{ |
|
// handle unaligned read |
|
uint32 nRet; |
|
memcpy( &nRet, pData, sizeof( nRet ) ); |
|
pData += sizeof( nRet ); |
|
return nRet; |
|
} |
|
|
|
|
|
bool CShaderManager::CreateDynamicCombos_Ver5( void *pContext, uint8 *pComboBuffer, char *debugLabel ) |
|
{ |
|
ShaderLookup_t* pLookup = (ShaderLookup_t *)pContext; |
|
ShaderFileCache_t *pFileCache = &m_ShaderFileCache[pLookup->m_hShaderFileCache]; |
|
uint8 *pCompressedShaders = pComboBuffer + pLookup->m_nDataOffset; |
|
|
|
uint8 *pUnpackBuffer = new uint8[MAX_SHADER_UNPACKED_BLOCK_SIZE]; |
|
|
|
char *debugLabelPtr = debugLabel; // can be moved to point at something else if need be |
|
|
|
// now, loop through all blocks |
|
bool bOK = true; |
|
while ( bOK ) |
|
{ |
|
uint32 nBlockSize = NextULONG( pCompressedShaders ); |
|
if ( nBlockSize == 0xffffffff ) |
|
{ |
|
// any more blocks? |
|
break; |
|
} |
|
|
|
switch( nBlockSize & 0xc0000000 ) |
|
{ |
|
case 0: // bzip2 |
|
{ |
|
// uncompress |
|
uint32 nOutsize = MAX_SHADER_UNPACKED_BLOCK_SIZE; |
|
int nRslt = BZ2_bzBuffToBuffDecompress( |
|
reinterpret_cast<char *>( pUnpackBuffer ), |
|
&nOutsize, |
|
reinterpret_cast<char *>( pCompressedShaders ), |
|
nBlockSize, 1, 0 ); |
|
if ( nRslt < 0 ) |
|
{ |
|
// errors are negative for bzip |
|
Assert( 0 ); |
|
Warning( "BZIP Error (%d) decompressing shader", nRslt ); |
|
bOK = false; |
|
} |
|
|
|
pCompressedShaders += nBlockSize; |
|
nBlockSize = nOutsize; // how much data there is |
|
} |
|
break; |
|
|
|
case 0x80000000: // uncompressed |
|
{ |
|
// not compressed, as is |
|
nBlockSize &= 0x3fffffff; |
|
memcpy( pUnpackBuffer, pCompressedShaders, nBlockSize ); |
|
pCompressedShaders += nBlockSize; |
|
} |
|
break; |
|
|
|
case 0x40000000: // lzma compressed |
|
{ |
|
nBlockSize &= 0x3fffffff; |
|
|
|
size_t nOutsize = CLZMA::Uncompress( |
|
reinterpret_cast<uint8 *>( pCompressedShaders ), |
|
pUnpackBuffer ); |
|
pCompressedShaders += nBlockSize; |
|
nBlockSize = nOutsize; // how much data there is |
|
} |
|
break; |
|
|
|
default: |
|
{ |
|
Assert( 0 ); |
|
Error(" unrecognized shader compression type = file corrupt?"); |
|
bOK = false; |
|
} |
|
} |
|
|
|
uint8 *pReadPtr = pUnpackBuffer; |
|
while ( pReadPtr < pUnpackBuffer+nBlockSize ) |
|
{ |
|
uint32 nCombo_ID = NextULONG( pReadPtr ); |
|
uint32 nShaderSize = NextULONG( pReadPtr ); |
|
|
|
#if defined( WRITE_ASSEMBLY ) |
|
DisassembleShader( pLookup, nCombo_ID, pReadPtr ); |
|
#endif |
|
HardwareShader_t hardwareShader = INVALID_HARDWARE_SHADER; |
|
|
|
int iIndex = nCombo_ID; |
|
if ( iIndex >= pLookup->m_nStaticIndex ) |
|
iIndex -= pLookup->m_nStaticIndex; // ver5 stores combos as full combo, ver6 as dynamic combo # only |
|
if ( IsPC() && m_bCreateShadersOnDemand ) |
|
{ |
|
// cache the code off for later |
|
pLookup->m_ShaderStaticCombos.m_pCreationData[iIndex].ByteCode.SetSize( nShaderSize ); |
|
V_memcpy( pLookup->m_ShaderStaticCombos.m_pCreationData[iIndex].ByteCode.Base(), pReadPtr, nShaderSize ); |
|
pLookup->m_ShaderStaticCombos.m_pCreationData[iIndex].iCentroidMask = pFileCache->m_bVertexShader ? 0 : pFileCache->m_Header.m_nCentroidMask; |
|
} |
|
else |
|
{ |
|
const char *pShaderName = m_ShaderSymbolTable.String( pLookup->m_Name ); |
|
|
|
if ( pFileCache->m_bVertexShader ) |
|
{ |
|
#if 0 |
|
// this is all test code |
|
CUtlBuffer bufGLCode( 1000, 50000, CUtlBuffer::TEXT_BUFFER ); |
|
CUtlBuffer bufNewGLCode( 1000, 50000, CUtlBuffer::TEXT_BUFFER ); |
|
CUtlBuffer bufGLSLCode( 1000, 50000, CUtlBuffer::TEXT_BUFFER ); |
|
bool bVertexShader; |
|
|
|
uint32 nOptions = 0; |
|
nOptions |= D3DToGL_OptionUseEnvParams; |
|
nOptions |= D3DToGL_OptionDoFixupZ; |
|
nOptions |= D3DToGL_OptionDoFixupY; |
|
//options |= D3DToGL_OptionSpew; |
|
|
|
// sg_D3DToOpenGLTranslator.TranslateShader( (uint32 *) pReadPtr, (char *)bufGLCode.Base(), bufGLCode.Size(), &bVertexShader, nOptions, -1, debugLabel ); |
|
// sg_NewD3DToOpenGLTranslator.TranslateShader( (uint32 *) pReadPtr, &bufNewGLCode, &bVertexShader, nOptions, -1, debugLabel ); |
|
|
|
|
|
// bool bDumpGLSL = false; |
|
// if ( !stricmp( "vs-file vertexlit_and_unlit_generic_bump_vs20 vs-index 144", debugLabel ) && ( iIndex == 0 ) ) |
|
// { |
|
// DisassembleShader( pLookup, iIndex, pReadPtr ); // Direct3D |
|
// bDumpGLSL = true; |
|
// } |
|
|
|
|
|
// GLSL options |
|
nOptions |= D3DToGL_OptionGLSL; // | D3DToGL_AddHexComments | D3DToGL_PutHexCommentsAfterLines; |
|
if ( !IsOSX() ) |
|
{ |
|
nOptions |= D3DToGL_OptionAllowStaticControlFlow; |
|
} |
|
sg_NewD3DToOpenGLTranslator.TranslateShader( (uint32 *) pReadPtr, &bufGLSLCode, &bVertexShader, nOptions, -1, 0, debugLabel ); |
|
Assert( bVertexShader ); |
|
|
|
// Test to make sure these are identical |
|
// if ( bDumpGLSL )//V_strcmp( (char *)bufGLCode.Base(), (char *)bufNewGLCode.Base() ) ) |
|
// { |
|
// WriteTranslatedFile( pLookup, iIndex, (char *)bufGLCode.Base(), "avp" ); // Old |
|
// WriteTranslatedFile( pLookup, iIndex, (char *)bufNewGLCode.Base(), "avp2" ); // New |
|
WriteTranslatedFile( pLookup, iIndex, (char *)bufGLSLCode.Base(), "glsl_v" ); // GLSL |
|
// DisassembleShader( pLookup, iIndex, pReadPtr ); // Direct3D |
|
// } |
|
|
|
#if defined( WRITE_ASSEMBLY ) |
|
WriteTranslatedFile( pLookup, iIndex, (char *)bufGLCode.Base(), "avp" ); |
|
#endif |
|
#endif // 0 |
|
|
|
#ifdef DX_TO_GL_ABSTRACTION |
|
// munge the debug label a bit to aid in decoding... catenate the iIndex on the end |
|
char temp[1024]; |
|
sprintf(temp, "%s vs-combo %d", (debugLabel)?debugLabel:"none", iIndex ); |
|
debugLabelPtr = temp; |
|
#endif |
|
// pass binary code to d3d interface, on GL it will invoke the translator back to asm |
|
hardwareShader = CreateD3DVertexShader( reinterpret_cast< DWORD *>( pReadPtr ), nShaderSize, pShaderName, debugLabelPtr ); |
|
} |
|
else |
|
{ |
|
#if 0 |
|
// this is all test code |
|
// CUtlBuffer bufGLCode( 1000, 50000, CUtlBuffer::TEXT_BUFFER ); |
|
// CUtlBuffer bufNewGLCode( 1000, 50000, CUtlBuffer::TEXT_BUFFER ); |
|
CUtlBuffer bufGLSLCode( 1000, 50000, CUtlBuffer::TEXT_BUFFER ); |
|
bool bVertexShader; |
|
|
|
|
|
uint32 nOptions = D3DToGL_OptionUseEnvParams; |
|
|
|
// sg_D3DToOpenGLTranslator.TranslateShader( (uint32 *) pReadPtr, (char *)bufGLCode.Base(), bufGLCode.Size(), &bVertexShader, D3DToGL_OptionUseEnvParams, -1, debugLabel ); |
|
// sg_NewD3DToOpenGLTranslator.TranslateShader( (uint32 *) pReadPtr, &bufNewGLCode, &bVertexShader, D3DToGL_OptionUseEnvParams, -1, debugLabel ); |
|
|
|
// GLSL options |
|
nOptions |= D3DToGL_OptionGLSL;// | D3DToGL_OptionSRGBWriteSuffix | D3DToGL_AddHexComments | D3DToGL_PutHexCommentsAfterLines; |
|
if ( !IsOSX() ) |
|
{ |
|
nOptions |= D3DToGL_OptionAllowStaticControlFlow; |
|
} |
|
sg_NewD3DToOpenGLTranslator.TranslateShader( (uint32 *) pReadPtr, &bufGLSLCode, &bVertexShader, nOptions, -1, 0, debugLabel ); |
|
|
|
Assert( !bVertexShader ); |
|
|
|
// Test to make sure these are identical |
|
// if ( V_strcmp( (char *)bufGLCode.Base(), (char *)bufNewGLCode.Base() ) ) |
|
// { |
|
// WriteTranslatedFile( pLookup, iIndex, (char *)bufGLCode.Base(), "afp" ); // Old |
|
// WriteTranslatedFile( pLookup, iIndex, (char *)bufNewGLCode.Base(), "afp2" ); // New |
|
WriteTranslatedFile( pLookup, iIndex, (char *)bufGLSLCode.Base(), "glsl_p" ); // GLSL |
|
// DisassembleShader( pLookup, iIndex, pReadPtr ); // Direct3D |
|
// } |
|
|
|
#if defined( WRITE_ASSEMBLY ) |
|
WriteTranslatedFile( pLookup, iIndex, (char *)bufGLCode.Base(), "afp" ); |
|
#endif |
|
#endif // 0 |
|
|
|
#ifdef DX_TO_GL_ABSTRACTION |
|
// munge the debug label a bit to aid in decoding... catenate the iIndex on the end |
|
char temp[1024]; |
|
sprintf(temp, "%s ps-combo %d", (debugLabel)?debugLabel:"", iIndex ); |
|
debugLabelPtr = temp; |
|
#endif |
|
|
|
// pass binary code to d3d interface, on GL it will invoke the translator back to asm |
|
hardwareShader = CreateD3DPixelShader( reinterpret_cast< DWORD *>( pReadPtr ), pFileCache->m_Header.m_nCentroidMask, nShaderSize, pShaderName, debugLabelPtr ); |
|
} |
|
if ( hardwareShader == INVALID_HARDWARE_SHADER ) |
|
{ |
|
Warning( "failed to create shader\n" ); |
|
Assert( 0 ); |
|
bOK = false; |
|
break; |
|
} |
|
} |
|
pLookup->m_ShaderStaticCombos.m_pHardwareShaders[iIndex] = hardwareShader; |
|
pReadPtr += nShaderSize; |
|
} |
|
} |
|
|
|
delete[] pUnpackBuffer; |
|
|
|
return bOK; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Static method, called by thread, don't call anything non-threadsafe from handler!!! |
|
//----------------------------------------------------------------------------- |
|
void CShaderManager::QueuedLoaderCallback( void *pContext, void *pContext2, const void *pData, int nSize, LoaderError_t loaderError ) |
|
{ |
|
ShaderLookup_t* pLookup = (ShaderLookup_t *)pContext; |
|
|
|
bool bOK = ( loaderError == LOADERERROR_NONE ); |
|
if ( bOK ) |
|
{ |
|
if ( pContext2 ) |
|
{ |
|
// presence denotes diff version |
|
bOK = s_ShaderManager.CreateDynamicCombos_Ver4( pContext, (uint8 *)pData ); |
|
} |
|
else |
|
{ |
|
bOK = s_ShaderManager.CreateDynamicCombos_Ver5( pContext, (uint8 *)pData ); |
|
} |
|
} |
|
if ( !bOK ) |
|
{ |
|
pLookup->m_Flags |= SHADER_FAILED_LOAD; |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Loads all shaders |
|
//----------------------------------------------------------------------------- |
|
bool CShaderManager::LoadAndCreateShaders( ShaderLookup_t &lookup, bool bVertexShader, char *debugLabel ) |
|
{ |
|
const char *pName = m_ShaderSymbolTable.String( lookup.m_Name ); |
|
|
|
// find it in the cache |
|
// a cache hit prevents costly i/o for static components, i.e. header, ref combo, etc. |
|
ShaderFileCache_t fileCacheLookup; |
|
fileCacheLookup.m_Name = lookup.m_Name; |
|
fileCacheLookup.m_bVertexShader = bVertexShader; |
|
intp fileCacheIndex = m_ShaderFileCache.Find( fileCacheLookup ); |
|
if ( fileCacheIndex == m_ShaderFileCache.InvalidIndex() ) |
|
{ |
|
// not found, create a new entry |
|
fileCacheIndex = m_ShaderFileCache.AddToTail(); |
|
} |
|
|
|
lookup.m_hShaderFileCache = fileCacheIndex; |
|
|
|
// fetch from cache |
|
ShaderFileCache_t *pFileCache = &m_ShaderFileCache[fileCacheIndex]; |
|
ShaderHeader_t *pHeader = &pFileCache->m_Header; |
|
|
|
FileHandle_t hFile = FILESYSTEM_INVALID_HANDLE; |
|
if ( pFileCache->IsValid() ) |
|
{ |
|
// using cached header, just open file, no read of header needed |
|
hFile = OpenFileAndLoadHeader( m_ShaderSymbolTable.String( pFileCache->m_Filename ), NULL ); |
|
if ( hFile == FILESYSTEM_INVALID_HANDLE ) |
|
{ |
|
// shouldn't happen |
|
Assert( 0 ); |
|
return false; |
|
} |
|
} |
|
else |
|
{ |
|
V_memset( pHeader, 0, sizeof( ShaderHeader_t ) ); |
|
|
|
// try the vsh/psh dir first |
|
char filename[MAX_PATH]; |
|
Q_snprintf( filename, MAX_PATH, "shaders\\%s\\%s" SHADER_FNAME_EXTENSION, bVertexShader ? "vsh" : "psh", pName ); |
|
hFile = OpenFileAndLoadHeader( filename, pHeader ); |
|
if ( hFile == FILESYSTEM_INVALID_HANDLE ) |
|
{ |
|
#ifdef DYNAMIC_SHADER_COMPILE |
|
// Dynamically compile if it's HLSL. |
|
if ( LoadAndCreateShaders_Dynamic( lookup, bVertexShader ) ) |
|
{ |
|
return true; |
|
} |
|
else |
|
{ |
|
return false; |
|
} |
|
#endif |
|
// next, try the fxc dir |
|
Q_snprintf( filename, MAX_PATH, "shaders\\fxc\\%s" SHADER_FNAME_EXTENSION, pName ); |
|
hFile = OpenFileAndLoadHeader( filename, pHeader ); |
|
if ( hFile == FILESYSTEM_INVALID_HANDLE ) |
|
{ |
|
lookup.m_Flags |= SHADER_FAILED_LOAD; |
|
Warning( "Couldn't load %s shader %s\n", bVertexShader ? "vertex" : "pixel", pName ); |
|
return false; |
|
} |
|
} |
|
|
|
lookup.m_Flags = pHeader->m_nFlags; |
|
|
|
pFileCache->m_Name = lookup.m_Name; |
|
pFileCache->m_Filename = m_ShaderSymbolTable.AddString( filename ); |
|
pFileCache->m_bVertexShader = bVertexShader; |
|
|
|
if ( pFileCache->IsOldVersion() ) |
|
{ |
|
int referenceComboSize = ((ShaderHeader_t_v4 *)pHeader)->m_nDiffReferenceSize; |
|
if ( referenceComboSize ) |
|
{ |
|
// cache the reference combo |
|
pFileCache->m_ReferenceCombo.EnsureCapacity( referenceComboSize ); |
|
g_pFullFileSystem->Read( pFileCache->m_ReferenceCombo.Base(), referenceComboSize, hFile ); |
|
} |
|
} |
|
else |
|
{ |
|
// cache the dictionary |
|
pFileCache->m_StaticComboRecords.EnsureCount( pHeader->m_nNumStaticCombos ); |
|
g_pFullFileSystem->Read( pFileCache->m_StaticComboRecords.Base(), pHeader->m_nNumStaticCombos * sizeof( StaticComboRecord_t ), hFile ); |
|
if ( pFileCache->IsVersion6() ) |
|
{ |
|
// read static combo alias records |
|
int nNumDups; |
|
g_pFullFileSystem->Read( &nNumDups, sizeof( nNumDups ), hFile ); |
|
if ( nNumDups ) |
|
{ |
|
pFileCache->m_StaticComboDupRecords.EnsureCount( nNumDups ); |
|
g_pFullFileSystem->Read( pFileCache->m_StaticComboDupRecords.Base(), nNumDups * sizeof( StaticComboAliasRecord_t ), hFile ); |
|
} |
|
} |
|
|
|
} |
|
} |
|
|
|
// FIXME: should make lookup and ShaderStaticCombos_t are pool allocated. |
|
int i; |
|
lookup.m_ShaderStaticCombos.m_nCount = pHeader->m_nDynamicCombos; |
|
lookup.m_ShaderStaticCombos.m_pHardwareShaders = new HardwareShader_t[pHeader->m_nDynamicCombos]; |
|
if ( IsPC() && m_bCreateShadersOnDemand ) |
|
{ |
|
lookup.m_ShaderStaticCombos.m_pCreationData = new ShaderStaticCombos_t::ShaderCreationData_t[pHeader->m_nDynamicCombos]; |
|
} |
|
for ( i = 0; i < pHeader->m_nDynamicCombos; i++ ) |
|
{ |
|
lookup.m_ShaderStaticCombos.m_pHardwareShaders[i] = INVALID_HARDWARE_SHADER; |
|
} |
|
|
|
int nStartingOffset = 0; |
|
int nEndingOffset = 0; |
|
|
|
if ( pFileCache->IsOldVersion() ) |
|
{ |
|
int nDictionaryOffset = sizeof( ShaderHeader_t ) + ((ShaderHeader_t_v4 *)pHeader)->m_nDiffReferenceSize; |
|
|
|
// read in shader's dynamic combos directory |
|
lookup.m_pComboDictionary = new ShaderDictionaryEntry_t[pHeader->m_nDynamicCombos]; |
|
g_pFullFileSystem->Seek( hFile, nDictionaryOffset + lookup.m_nStaticIndex * sizeof( ShaderDictionaryEntry_t ), FILESYSTEM_SEEK_HEAD ); |
|
g_pFullFileSystem->Read( lookup.m_pComboDictionary, pHeader->m_nDynamicCombos * sizeof( ShaderDictionaryEntry_t ), hFile ); |
|
|
|
// want single read of all this shader's dynamic combos into a target buffer |
|
// shaders are written sequentially, determine starting offset and length |
|
for ( i = 0; i < pHeader->m_nDynamicCombos; i++ ) |
|
{ |
|
if ( lookup.m_pComboDictionary[i].m_Offset == -1 ) |
|
{ |
|
// skipped |
|
continue; |
|
} |
|
|
|
// ensure offsets are in fact sequentially ascending |
|
Assert( lookup.m_pComboDictionary[i].m_Offset >= nStartingOffset && lookup.m_pComboDictionary[i].m_Size >= 0 ); |
|
|
|
if ( !nStartingOffset ) |
|
{ |
|
nStartingOffset = lookup.m_pComboDictionary[i].m_Offset; |
|
} |
|
nEndingOffset = lookup.m_pComboDictionary[i].m_Offset + lookup.m_pComboDictionary[i].m_Size; |
|
} |
|
if ( !nStartingOffset ) |
|
{ |
|
g_pFullFileSystem->Close( hFile ); |
|
Warning( "Shader '%s' - All dynamic combos skipped. This is bad!\n", m_ShaderSymbolTable.String( pFileCache->m_Filename ) ); |
|
return false; |
|
} |
|
} |
|
else |
|
{ |
|
int nStaticComboIdx = pFileCache->FindCombo( lookup.m_nStaticIndex / pFileCache->m_Header.m_nDynamicCombos ); |
|
if ( nStaticComboIdx == -1 ) |
|
{ |
|
g_pFullFileSystem->Close( hFile ); |
|
lookup.m_Flags |= SHADER_FAILED_LOAD; |
|
Warning( "Shader '%s' - Couldn't load combo %d of shader (dyn=%d)\n", m_ShaderSymbolTable.String( pFileCache->m_Filename ), lookup.m_nStaticIndex, pFileCache->m_Header.m_nDynamicCombos ); |
|
return false; |
|
} |
|
|
|
nStartingOffset = pFileCache->m_StaticComboRecords[nStaticComboIdx].m_nFileOffset; |
|
nEndingOffset = pFileCache->m_StaticComboRecords[nStaticComboIdx+1].m_nFileOffset; |
|
} |
|
|
|
// align offsets for unbuffered optimal i/o - fastest i/o possible |
|
unsigned nOffsetAlign, nSizeAlign, nBufferAlign; |
|
g_pFullFileSystem->GetOptimalIOConstraints( hFile, &nOffsetAlign, &nSizeAlign, &nBufferAlign ); |
|
unsigned int nAlignedOffset = AlignValue( ( nStartingOffset - nOffsetAlign ) + 1, nOffsetAlign ); |
|
unsigned int nAlignedBytesToRead = AlignValue( nEndingOffset - nAlignedOffset, nSizeAlign ); |
|
|
|
// used for adjusting provided buffer to actual data |
|
lookup.m_nDataOffset = nStartingOffset - nAlignedOffset; |
|
|
|
bool bOK = true; |
|
if ( IsX360() && g_pQueuedLoader->IsMapLoading() ) |
|
{ |
|
LoaderJob_t loaderJob; |
|
loaderJob.m_pFilename = m_ShaderSymbolTable.String( pFileCache->m_Filename ); |
|
loaderJob.m_pPathID = "GAME"; |
|
loaderJob.m_pCallback = QueuedLoaderCallback; |
|
loaderJob.m_pContext = (void *)&lookup; |
|
loaderJob.m_pContext2 = (void *)pFileCache->IsOldVersion(); |
|
loaderJob.m_Priority = LOADERPRIORITY_DURINGPRELOAD; |
|
loaderJob.m_nBytesToRead = nAlignedBytesToRead; |
|
loaderJob.m_nStartOffset = nAlignedOffset; |
|
g_pQueuedLoader->AddJob( &loaderJob ); |
|
} |
|
else |
|
{ |
|
//printf("\n CShaderManager::LoadAndCreateShaders - reading %d bytes from file offset %d", nAlignedBytesToRead, nAlignedOffset); |
|
// single optimal read of all dynamic combos into monolithic buffer |
|
uint8 *pOptimalBuffer = (uint8 *)g_pFullFileSystem->AllocOptimalReadBuffer( hFile, nAlignedBytesToRead, nAlignedOffset ); |
|
g_pFullFileSystem->Seek( hFile, nAlignedOffset, FILESYSTEM_SEEK_HEAD ); |
|
g_pFullFileSystem->Read( pOptimalBuffer, nAlignedBytesToRead, hFile ); |
|
|
|
if ( pFileCache->IsOldVersion() ) |
|
{ |
|
bOK = CreateDynamicCombos_Ver4( &lookup, pOptimalBuffer ); |
|
} |
|
else |
|
{ |
|
bOK = CreateDynamicCombos_Ver5( &lookup, pOptimalBuffer, debugLabel ); |
|
} |
|
|
|
g_pFullFileSystem->FreeOptimalReadBuffer( pOptimalBuffer ); |
|
} |
|
|
|
g_pFullFileSystem->Close( hFile ); |
|
|
|
if ( !bOK ) |
|
{ |
|
lookup.m_Flags |= SHADER_FAILED_LOAD; |
|
} |
|
|
|
return bOK; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------------old code |
|
|
|
#if 0 |
|
|
|
// Set this convar internally to build or add to the shader cache file |
|
// We really only expect this to work on DX_TO_GL_ABSTRACTION |
|
ConVar mat_cacheshaders( "mat_cacheshaders", "0", FCVAR_DEVELOPMENTONLY ); |
|
|
|
#define SHADER_CACHE_FILE "shader_cache.cfg" |
|
#define PROGRAM_CACHE_FILE "program_cache.cfg" |
|
|
|
static void WriteToShaderCache( const char *pShaderName, const int nIndex ) |
|
{ |
|
#ifndef DX_TO_GL_ABSTRACTION |
|
return; |
|
#endif |
|
|
|
KeyValues *pShaderCache = new KeyValues( "shadercache" ); |
|
// we don't load anything, it starts empty.. pShaderCache->LoadFromFile( g_pFullFileSystem, SHADER_CACHE_FILE, "MOD" ); |
|
|
|
if ( !pShaderCache ) |
|
{ |
|
Warning( "Could not write to shader cache file!\n" ); |
|
return; |
|
} |
|
|
|
// Subkey for specific shader |
|
KeyValues *pShaderKey = pShaderCache->FindKey( pShaderName, true ); |
|
Assert( pShaderKey ); |
|
|
|
bool bFound = false; |
|
int nKeys = 0; |
|
char szIndex[8]; |
|
FOR_EACH_VALUE( pShaderKey, pValues ) |
|
{ |
|
if ( pValues->GetInt() == nIndex ) |
|
{ |
|
bFound = true; |
|
} |
|
nKeys++; |
|
} |
|
|
|
if ( !bFound ) |
|
{ |
|
V_snprintf( szIndex, 8, "%d", nKeys ); |
|
pShaderKey->SetInt( szIndex, nIndex ); |
|
} |
|
|
|
pShaderCache->SaveToFile( g_pFullFileSystem, SHADER_CACHE_FILE, "MOD" ); |
|
pShaderCache->deleteThis(); |
|
} |
|
|
|
void CShaderManager::WarmShaderCache() |
|
{ |
|
#ifndef DX_TO_GL_ABSTRACTION |
|
return; |
|
#endif |
|
|
|
// Don't access the cache if we're building it! |
|
if ( mat_cacheshaders.GetBool() ) |
|
return; |
|
|
|
// Don't warm the cache if we're just going to monkey with the shaders anyway |
|
#ifdef DYNAMIC_SHADER_COMPILE |
|
return; |
|
#endif |
|
|
|
double st = Sys_FloatTime(); |
|
|
|
|
|
// |
|
// First we warm SHADERS =============================================== |
|
// |
|
|
|
KeyValues *pShaderCache = new KeyValues( "shadercache" ); |
|
pShaderCache->LoadFromFile( g_pFullFileSystem, SHADER_CACHE_FILE, "MOD" ); |
|
|
|
if ( !pShaderCache ) |
|
{ |
|
Warning( "Could not find shader cache file!\n" ); |
|
return; |
|
} |
|
|
|
// Run through each shader in the cache |
|
FOR_EACH_SUBKEY( pShaderCache, pShaderKey ) |
|
{ |
|
const char *pShaderName = pShaderKey->GetName(); |
|
bool bVertexShader = Q_stristr( pShaderName, "_vs20" ) || Q_stristr( pShaderName, "_vs30" ); |
|
|
|
FOR_EACH_VALUE( pShaderKey, pValue ) |
|
{ |
|
char temp[1024]; |
|
int staticIndex = pValue->GetInt(); |
|
|
|
if ( bVertexShader ) |
|
{ |
|
V_snprintf( temp, sizeof(temp), "vs-file %s vs-index %d", pShaderName, staticIndex ); |
|
CreateVertexShader( pShaderName, staticIndex, temp ); |
|
} |
|
else |
|
{ |
|
V_snprintf( temp, sizeof(temp), "ps-file %s ps-index %d", pShaderName, staticIndex ); |
|
CreatePixelShader( pShaderName, staticIndex, temp ); |
|
} |
|
} |
|
} |
|
|
|
pShaderCache->deleteThis(); |
|
|
|
|
|
// |
|
// Next, we warm PROGRAMS (which are pairs of shaders) ================= |
|
// |
|
|
|
KeyValues *pProgramCache = new KeyValues( "programcache" ); |
|
pProgramCache->LoadFromFile( g_pFullFileSystem, PROGRAM_CACHE_FILE, "MOD" ); |
|
|
|
if ( !pProgramCache ) |
|
{ |
|
Warning( "Could not find program cache file!\n" ); |
|
return; |
|
} |
|
|
|
// Run through each program in the cache |
|
FOR_EACH_SUBKEY( pProgramCache, pProgramKey ) |
|
{ |
|
KeyValues *pValue = pProgramKey->GetFirstValue(); |
|
const char *pVertexShaderName = pValue->GetString(); |
|
pValue = pValue->GetNextValue(); |
|
const char *pPixelShaderName = pValue->GetString(); |
|
pValue = pValue->GetNextValue(); |
|
int nVertexShaderStaticIndex = pValue->GetInt(); |
|
pValue = pValue->GetNextValue(); |
|
int nPixelShaderStaticIndex = pValue->GetInt(); |
|
pValue = pValue->GetNextValue(); |
|
int nVertexShaderDynamicIndex = pValue->GetInt(); |
|
pValue = pValue->GetNextValue(); |
|
int nPixelShaderDynamicIndex = pValue->GetInt(); |
|
|
|
ShaderLookup_t vshLookup; |
|
vshLookup.m_Name = m_ShaderSymbolTable.AddString( pVertexShaderName ); // TODO: use String() here and catch this odd case |
|
vshLookup.m_nStaticIndex = nVertexShaderStaticIndex; |
|
VertexShader_t vertexShader = m_VertexShaderDict.Find( vshLookup ); |
|
|
|
ShaderLookup_t pshLookup; |
|
pshLookup.m_Name = m_ShaderSymbolTable.AddString( pPixelShaderName ); |
|
pshLookup.m_nStaticIndex = nPixelShaderStaticIndex; |
|
PixelShader_t pixelShader = m_PixelShaderDict.Find( pshLookup ); |
|
|
|
// If we found both shaders, do the link! |
|
if ( ( vertexShader != m_VertexShaderDict.InvalidIndex() ) && ( pixelShader != m_PixelShaderDict.InvalidIndex() ) ) |
|
{ |
|
#ifdef DX_TO_GL_ABSTRACTION |
|
//HardwareShader_t hardwareVertexShader = vshLookup.m_ShaderStaticCombos.m_pHardwareShaders[nVertexShaderDynamicIndex]; |
|
//HardwareShader_t hardwarePixelShader = pshLookup.m_ShaderStaticCombos.m_pHardwareShaders[nPixelShaderDynamicIndex]; |
|
|
|
HardwareShader_t hardwareVertexShader = m_VertexShaderDict[vertexShader].m_ShaderStaticCombos.m_pHardwareShaders[nVertexShaderDynamicIndex]; |
|
HardwareShader_t hardwarePixelShader = m_PixelShaderDict[pixelShader].m_ShaderStaticCombos.m_pHardwareShaders[nPixelShaderDynamicIndex]; |
|
|
|
if ( ( hardwareVertexShader != INVALID_HARDWARE_SHADER ) && ( hardwarePixelShader != INVALID_HARDWARE_SHADER ) ) |
|
{ |
|
if ( S_OK != Dx9Device()->LinkShaderPair( (IDirect3DVertexShader9 *)hardwareVertexShader, (IDirect3DPixelShader9 *)hardwarePixelShader ) ) |
|
{ |
|
Warning( "Could not link OpenGL shaders: %s (%d, %d) : %s (%d, %d)\n", pVertexShaderName, nVertexShaderStaticIndex, nVertexShaderDynamicIndex, pPixelShaderName, nPixelShaderStaticIndex, nPixelShaderDynamicIndex ); |
|
} |
|
} |
|
#endif |
|
} |
|
else |
|
{ |
|
Warning( "Invalid shader linkage: %s (%d, %d) : %s (%d, %d)\n", pVertexShaderName, nVertexShaderStaticIndex, nVertexShaderDynamicIndex, pPixelShaderName, nPixelShaderStaticIndex, nPixelShaderDynamicIndex ); |
|
} |
|
} |
|
|
|
pProgramCache->deleteThis(); |
|
|
|
float elapsed = ( float )( Sys_FloatTime() - st ) * 1000.0; |
|
DevMsg( "WarmShaderCache took %.3f msec\n", elapsed ); |
|
} |
|
|
|
#endif |
|
//----------------------------------------------------------------------------------old code |
|
|
|
#ifdef DX_TO_GL_ABSTRACTION |
|
// if shaders are changed in a way that requires the client-side cache to be invalidated, |
|
// increment this string - such changes include combo changes (skips, adding combos) |
|
const char *k_pszShaderCacheRootKey = "glshadercachev002"; |
|
#endif |
|
|
|
void CShaderManager::SaveShaderCache( char *cacheName ) |
|
{ |
|
#ifdef DX_TO_GL_ABSTRACTION // must ifdef, it uses calls which don't exist in the real DX9 interface |
|
|
|
KeyValues *pProgramCache = new KeyValues( k_pszShaderCacheRootKey ); |
|
|
|
if ( !pProgramCache ) |
|
{ |
|
Warning( "Could not write to program cache file!\n" ); |
|
return; |
|
} |
|
|
|
int i=0; |
|
GLMShaderPairInfo info; |
|
|
|
do |
|
{ |
|
Dx9Device()->QueryShaderPair( i, &info ); |
|
|
|
if (info.m_status==1) |
|
{ |
|
// found one |
|
// extract values of interest which represent a pair of shaders |
|
|
|
if (info.m_vsName[0] && info.m_psName[0] && (info.m_vsDynamicIndex > -1) && (info.m_psDynamicIndex > -1) ) |
|
{ |
|
// make up a key - this thing is really a list of tuples, so need not be keyed by anything particular |
|
KeyValues *pProgramKey = pProgramCache->CreateNewKey(); |
|
Assert( pProgramKey ); |
|
|
|
pProgramKey->SetString ( "vs", info.m_vsName ); |
|
pProgramKey->SetString ( "ps", info.m_psName ); |
|
|
|
pProgramKey->SetInt ( "vs_static", info.m_vsStaticIndex ); |
|
pProgramKey->SetInt ( "ps_static", info.m_psStaticIndex ); |
|
|
|
pProgramKey->SetInt ( "vs_dynamic", info.m_vsDynamicIndex ); |
|
pProgramKey->SetInt ( "ps_dynamic", info.m_psDynamicIndex ); |
|
} |
|
} |
|
i++; |
|
} while( info.m_status >= 0 ); |
|
|
|
pProgramCache->SaveToFile( g_pFullFileSystem, cacheName, "MOD" ); |
|
pProgramCache->deleteThis(); |
|
|
|
// done! whew |
|
#endif |
|
} |
|
|
|
bool CShaderManager::LoadShaderCache( char *cacheName ) |
|
{ |
|
#ifdef DX_TO_GL_ABSTRACTION |
|
KeyValues *pProgramCache = new KeyValues( "" ); |
|
bool found = pProgramCache->LoadFromFile( g_pFullFileSystem, cacheName, "MOD" ); |
|
|
|
if ( !found ) |
|
{ |
|
Warning( "Could not load program cache file %s\n", cacheName ); |
|
return false; |
|
} |
|
|
|
if ( Q_stricmp( pProgramCache->GetName(), k_pszShaderCacheRootKey ) ) |
|
{ |
|
Warning( "Ignoring out-of-date shader cache (%s) with root key %s\n", cacheName, pProgramCache->GetName() ); |
|
return false; |
|
} |
|
|
|
int nTotalLinkedShaders = 0; |
|
int nTotalKeyValues = 0; |
|
|
|
// walk the table.. |
|
FOR_EACH_SUBKEY( pProgramCache, pProgramKey ) |
|
{ |
|
nTotalKeyValues++; |
|
|
|
// extract values decribing the specific active pair |
|
// then see if either stage needs a compilation done |
|
// then proceed to link |
|
|
|
KeyValues *pValue = pProgramKey->GetFirstValue(); |
|
if (!pValue) |
|
continue; |
|
const char *pVertexShaderName = pValue->GetString(); |
|
|
|
pValue = pValue->GetNextValue(); |
|
if (!pValue) |
|
continue; |
|
const char *pPixelShaderName = pValue->GetString(); |
|
|
|
pValue = pValue->GetNextValue(); |
|
if (!pValue) |
|
continue; |
|
int nVertexShaderStaticIndex = pValue->GetInt(); |
|
|
|
pValue = pValue->GetNextValue(); |
|
if (!pValue) |
|
continue; |
|
int nPixelShaderStaticIndex = pValue->GetInt(); |
|
|
|
pValue = pValue->GetNextValue(); |
|
if (!pValue) |
|
continue; |
|
int nVertexShaderDynamicIndex = pValue->GetInt(); |
|
|
|
pValue = pValue->GetNextValue(); |
|
if (!pValue) |
|
continue; |
|
int nPixelShaderDynamicIndex = pValue->GetInt(); |
|
|
|
ShaderLookup_t vshLookup; |
|
vshLookup.m_Name = m_ShaderSymbolTable.AddString( pVertexShaderName ); // TODO: use String() here and catch this odd case |
|
vshLookup.m_nStaticIndex = nVertexShaderStaticIndex; |
|
VertexShader_t vertexShader = m_VertexShaderDict.Find( vshLookup ); |
|
|
|
// if the VS was not found - now is the time to build it |
|
if( vertexShader == m_VertexShaderDict.InvalidIndex()) |
|
{ |
|
char temp[1024]; |
|
|
|
V_snprintf( temp, sizeof(temp), "vs-file %s vs-index %d", pVertexShaderName, nVertexShaderStaticIndex ); |
|
CreateVertexShader( pVertexShaderName, nVertexShaderStaticIndex, temp ); |
|
|
|
// this one should not fail |
|
vertexShader = m_VertexShaderDict.Find( vshLookup ); |
|
Assert( vertexShader != m_VertexShaderDict.InvalidIndex()); |
|
} |
|
|
|
ShaderLookup_t pshLookup; |
|
pshLookup.m_Name = m_ShaderSymbolTable.AddString( pPixelShaderName ); |
|
pshLookup.m_nStaticIndex = nPixelShaderStaticIndex; |
|
PixelShader_t pixelShader = m_PixelShaderDict.Find( pshLookup ); |
|
|
|
if( pixelShader == m_PixelShaderDict.InvalidIndex()) |
|
{ |
|
char temp[1024]; |
|
|
|
V_snprintf( temp, sizeof(temp), "ps-file %s ps-index %d", pPixelShaderName, nPixelShaderStaticIndex ); |
|
CreatePixelShader( pPixelShaderName, nPixelShaderStaticIndex, temp ); |
|
|
|
// this one should not fail |
|
pixelShader = m_PixelShaderDict.Find( pshLookup ); |
|
Assert( pixelShader != m_PixelShaderDict.InvalidIndex()); |
|
} |
|
|
|
// If we found both shaders, do the link! |
|
if ( ( vertexShader != m_VertexShaderDict.InvalidIndex() ) && ( pixelShader != m_PixelShaderDict.InvalidIndex() ) ) |
|
{ |
|
// double check that the hardware shader arrays are actually instantiated.. bail on the attempt if not (odd...) |
|
if (m_VertexShaderDict[vertexShader].m_ShaderStaticCombos.m_pHardwareShaders && m_PixelShaderDict[pixelShader].m_ShaderStaticCombos.m_pHardwareShaders) |
|
{ |
|
// and sanity check the indices.. |
|
if ( (nVertexShaderDynamicIndex>=0) && (nPixelShaderDynamicIndex>=0) ) |
|
{ |
|
HardwareShader_t hardwareVertexShader = m_VertexShaderDict[vertexShader].m_ShaderStaticCombos.m_pHardwareShaders[nVertexShaderDynamicIndex]; |
|
HardwareShader_t hardwarePixelShader = m_PixelShaderDict[pixelShader].m_ShaderStaticCombos.m_pHardwareShaders[nPixelShaderDynamicIndex]; |
|
|
|
if ( ( hardwareVertexShader != INVALID_HARDWARE_SHADER ) && ( hardwarePixelShader != INVALID_HARDWARE_SHADER ) ) |
|
{ |
|
if ( S_OK != Dx9Device()->LinkShaderPair( (IDirect3DVertexShader9 *)hardwareVertexShader, (IDirect3DPixelShader9 *)hardwarePixelShader ) ) |
|
{ |
|
Warning( "Could not link OpenGL shaders: %s (%d, %d) : %s (%d, %d)\n", pVertexShaderName, nVertexShaderStaticIndex, nVertexShaderDynamicIndex, pPixelShaderName, nPixelShaderStaticIndex, nPixelShaderDynamicIndex ); |
|
} |
|
else |
|
{ |
|
nTotalLinkedShaders++; |
|
} |
|
} |
|
} |
|
else |
|
{ |
|
Warning( "nVertexShaderDynamicIndex or nPixelShaderDynamicIndex was negative\n" ); |
|
} |
|
} |
|
else |
|
{ |
|
Warning( "m_pHardwareShaders was null\n" ); |
|
} |
|
} |
|
else |
|
{ |
|
Warning( "Invalid shader linkage: %s (%d, %d) : %s (%d, %d)\n", pVertexShaderName, nVertexShaderStaticIndex, nVertexShaderDynamicIndex, pPixelShaderName, nPixelShaderStaticIndex, nPixelShaderDynamicIndex ); |
|
} |
|
} |
|
|
|
Msg( "Loaded program cache file \"%s\", total keyvalues: %i, total successfully linked: %i\n", cacheName, nTotalKeyValues, nTotalLinkedShaders ); |
|
|
|
return true; |
|
|
|
#else |
|
return false; // have to return a value on Windows build to appease compiler |
|
#endif |
|
} |
|
|
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Creates and destroys vertex shaders |
|
//----------------------------------------------------------------------------- |
|
VertexShader_t CShaderManager::CreateVertexShader( const char *pFileName, int nStaticVshIndex, char *debugLabel ) |
|
{ |
|
MEM_ALLOC_CREDIT(); |
|
|
|
if ( !pFileName ) |
|
{ |
|
return INVALID_SHADER; |
|
} |
|
|
|
#if 0 //old |
|
if ( mat_cacheshaders.GetBool() ) |
|
{ |
|
WriteToShaderCache( pFileName, nStaticVshIndex ); |
|
} |
|
#endif |
|
|
|
VertexShader_t shader; |
|
ShaderLookup_t lookup; |
|
lookup.m_Name = m_ShaderSymbolTable.AddString( pFileName ); |
|
lookup.m_nStaticIndex = nStaticVshIndex; |
|
shader = m_VertexShaderDict.Find( lookup ); |
|
if ( shader == m_VertexShaderDict.InvalidIndex() ) |
|
{ |
|
//printf("\nCShaderManager::CreateVertexShader( filename = %s, staticVshIndex = %d - not in cache", pFileName, nStaticVshIndex ); |
|
|
|
shader = m_VertexShaderDict.AddToTail( lookup ); |
|
if ( !LoadAndCreateShaders( m_VertexShaderDict[shader], true, debugLabel ) ) |
|
{ |
|
return INVALID_SHADER; |
|
} |
|
} |
|
m_VertexShaderDict[shader].IncRefCount(); |
|
return shader; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Create pixel shader |
|
//----------------------------------------------------------------------------- |
|
PixelShader_t CShaderManager::CreatePixelShader( const char *pFileName, int nStaticPshIndex, char *debugLabel ) |
|
{ |
|
MEM_ALLOC_CREDIT(); |
|
|
|
if ( !pFileName ) |
|
{ |
|
return INVALID_SHADER; |
|
} |
|
|
|
#if 0 //old |
|
if ( mat_cacheshaders.GetBool() ) |
|
{ |
|
WriteToShaderCache( pFileName, nStaticPshIndex ); |
|
} |
|
#endif |
|
|
|
PixelShader_t shader; |
|
ShaderLookup_t lookup; |
|
lookup.m_Name = m_ShaderSymbolTable.AddString( pFileName ); |
|
lookup.m_nStaticIndex = nStaticPshIndex; |
|
shader = m_PixelShaderDict.Find( lookup ); |
|
if ( shader == m_PixelShaderDict.InvalidIndex() ) |
|
{ |
|
shader = m_PixelShaderDict.AddToTail( lookup ); |
|
if ( !LoadAndCreateShaders( m_PixelShaderDict[shader], false, debugLabel ) ) |
|
{ |
|
return INVALID_SHADER; |
|
} |
|
} |
|
m_PixelShaderDict[shader].IncRefCount(); |
|
return shader; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Clear the refCounts to zero |
|
//----------------------------------------------------------------------------- |
|
void CShaderManager::ClearVertexAndPixelShaderRefCounts() |
|
{ |
|
for ( VertexShader_t vshIndex = m_VertexShaderDict.Head(); |
|
vshIndex != m_VertexShaderDict.InvalidIndex(); |
|
vshIndex = m_VertexShaderDict.Next( vshIndex ) ) |
|
{ |
|
m_VertexShaderDict[vshIndex].m_nRefCount = 0; |
|
} |
|
|
|
for ( PixelShader_t pshIndex = m_PixelShaderDict.Head(); |
|
pshIndex != m_PixelShaderDict.InvalidIndex(); |
|
pshIndex = m_PixelShaderDict.Next( pshIndex ) ) |
|
{ |
|
m_PixelShaderDict[pshIndex].m_nRefCount = 0; |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Destroy all shaders that have no reference |
|
//----------------------------------------------------------------------------- |
|
void CShaderManager::PurgeUnusedVertexAndPixelShaders() |
|
{ |
|
#ifdef DX_TO_GL_ABSTRACTION |
|
if (mat_autosave_glshaders.GetInt()) |
|
{ |
|
SaveShaderCache("glshaders.cfg"); |
|
} |
|
return; // don't purge shaders, it's too costly to put them back |
|
#endif |
|
|
|
// iterate vertex shaders |
|
for ( VertexShader_t vshIndex = m_VertexShaderDict.Head(); vshIndex != m_VertexShaderDict.InvalidIndex(); ) |
|
{ |
|
Assert( m_VertexShaderDict[vshIndex].m_nRefCount >= 0 ); |
|
|
|
// Get the next one before we potentially delete the current one. |
|
VertexShader_t next = m_VertexShaderDict.Next( vshIndex ); |
|
if ( m_VertexShaderDict[vshIndex].m_nRefCount <= 0 ) |
|
{ |
|
DestroyVertexShader( vshIndex ); |
|
} |
|
vshIndex = next; |
|
} |
|
|
|
// iterate pixel shaders |
|
for ( PixelShader_t pshIndex = m_PixelShaderDict.Head(); pshIndex != m_PixelShaderDict.InvalidIndex(); ) |
|
{ |
|
Assert( m_PixelShaderDict[pshIndex].m_nRefCount >= 0 ); |
|
|
|
// Get the next one before we potentially delete the current one. |
|
PixelShader_t next = m_PixelShaderDict.Next( pshIndex ); |
|
if ( m_PixelShaderDict[pshIndex].m_nRefCount <= 0 ) |
|
{ |
|
DestroyPixelShader( pshIndex ); |
|
} |
|
pshIndex = next; |
|
} |
|
} |
|
|
|
|
|
|
|
void* CShaderManager::GetCurrentVertexShader() |
|
{ |
|
return (void*)m_HardwareVertexShader; |
|
} |
|
|
|
void* CShaderManager::GetCurrentPixelShader() |
|
{ |
|
return (void*)m_HardwarePixelShader; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// The low-level dx call to set the vertex shader state |
|
//----------------------------------------------------------------------------- |
|
void CShaderManager::SetVertexShaderState( HardwareShader_t shader, DataCacheHandle_t hCachedShader ) |
|
{ |
|
if ( m_HardwareVertexShader != shader ) |
|
{ |
|
RECORD_COMMAND( DX8_SET_VERTEX_SHADER, 1 ); |
|
RECORD_INT( ( int )shader ); // hack hack hack |
|
|
|
Dx9Device()->SetVertexShader( (IDirect3DVertexShader9*)shader ); |
|
m_HardwareVertexShader = shader; |
|
} |
|
} |
|
|
|
void CShaderManager::BindVertexShader( VertexShaderHandle_t hVertexShader ) |
|
{ |
|
HardwareShader_t hHardwareShader = m_RawVertexShaderDict[ (VertexShaderIndex_t)(uintp)hVertexShader] ; |
|
SetVertexShaderState( hHardwareShader ); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Sets a particular vertex shader as the current shader |
|
//----------------------------------------------------------------------------- |
|
void CShaderManager::SetVertexShader( VertexShader_t shader ) |
|
{ |
|
// Determine which vertex shader to use... |
|
if ( shader == INVALID_SHADER ) |
|
{ |
|
SetVertexShaderState( 0 ); |
|
return; |
|
} |
|
|
|
int vshIndex = m_nVertexShaderIndex; |
|
Assert( vshIndex >= 0 ); |
|
if( vshIndex < 0 ) |
|
{ |
|
vshIndex = 0; |
|
} |
|
|
|
ShaderLookup_t &vshLookup = m_VertexShaderDict[shader]; |
|
// Warning( "vsh: %s static: %d dynamic: %d\n", m_ShaderSymbolTable.String( vshLookup.m_Name ), |
|
// vshLookup.m_nStaticIndex, m_nVertexShaderIndex ); |
|
|
|
#ifdef DYNAMIC_SHADER_COMPILE |
|
HardwareShader_t &dxshader = m_VertexShaderDict[shader].m_ShaderStaticCombos.m_pHardwareShaders[vshIndex]; |
|
if ( dxshader == INVALID_HARDWARE_SHADER ) |
|
{ |
|
// compile it since we haven't already! |
|
dxshader = CompileShader( m_ShaderSymbolTable.String( vshLookup.m_Name ), vshLookup.m_nStaticIndex, vshIndex, true ); |
|
Assert( dxshader != INVALID_HARDWARE_SHADER ); |
|
|
|
if( IsX360() ) |
|
{ |
|
//360 does not respond well at all to bad shaders or Error() calls. So we're staying here until we get something that compiles |
|
while( dxshader == INVALID_HARDWARE_SHADER ) |
|
{ |
|
Warning( "A dynamically compiled vertex shader has failed to build. Pausing for 5 seconds and attempting rebuild.\n" ); |
|
#ifdef _WIN32 |
|
Sleep( 5000 ); |
|
#elif POSIX |
|
usleep( 5000 ); |
|
#endif |
|
dxshader = CompileShader( m_ShaderSymbolTable.String( vshLookup.m_Name ), vshLookup.m_nStaticIndex, vshIndex, true ); |
|
} |
|
} |
|
} |
|
#else |
|
if ( vshLookup.m_Flags & SHADER_FAILED_LOAD ) |
|
{ |
|
Assert( 0 ); |
|
return; |
|
} |
|
#ifdef _DEBUG |
|
vshDebugIndex = (vshDebugIndex + 1) % MAX_SHADER_HISTORY; |
|
Q_strncpy( vshDebugName[vshDebugIndex], m_ShaderSymbolTable.String( vshLookup.m_Name ), sizeof( vshDebugName[0] ) ); |
|
#endif |
|
Assert( vshIndex < vshLookup.m_ShaderStaticCombos.m_nCount ); |
|
HardwareShader_t dxshader = vshLookup.m_ShaderStaticCombos.m_pHardwareShaders[vshIndex]; |
|
#endif |
|
|
|
if ( IsPC() && ( dxshader == INVALID_HARDWARE_SHADER ) && m_bCreateShadersOnDemand ) |
|
{ |
|
#ifdef DYNAMIC_SHADER_COMPILE |
|
ShaderStaticCombos_t::ShaderCreationData_t *pCreationData = &m_VertexShaderDict[shader].m_ShaderStaticCombos.m_pCreationData[vshIndex]; |
|
#else |
|
ShaderStaticCombos_t::ShaderCreationData_t *pCreationData = &vshLookup.m_ShaderStaticCombos.m_pCreationData[vshIndex]; |
|
#endif |
|
|
|
dxshader = CreateD3DVertexShader( ( DWORD * )pCreationData->ByteCode.Base(), pCreationData->ByteCode.Count(), m_ShaderSymbolTable.String( vshLookup.m_Name ) ); |
|
|
|
#ifdef DYNAMIC_SHADER_COMPILE |
|
// copy the compiled shader handle back to wherever it's supposed to be stored |
|
m_VertexShaderDict[shader].m_ShaderStaticCombos.m_pHardwareShaders[vshIndex] = dxshader; |
|
#else |
|
vshLookup.m_ShaderStaticCombos.m_pHardwareShaders[vshIndex] = dxshader; |
|
#endif |
|
} |
|
|
|
Assert( dxshader ); |
|
|
|
#ifndef DYNAMIC_SHADER_COMPILE |
|
if( !dxshader ) |
|
{ |
|
Error( "!!!!!Using invalid shader combo!!!!! Consult a programmer and tell them to build debug materialsystem.dll and stdshader*.dll. Run with \"mat_bufferprimitives 0\" and look for CMaterial in the call stack and see what m_pDebugName is. You are likely using a shader combo that has been skipped.\n" ); |
|
} |
|
#endif |
|
|
|
SetVertexShaderState( dxshader ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// The low-level dx call to set the pixel shader state |
|
//----------------------------------------------------------------------------- |
|
void CShaderManager::SetPixelShaderState( HardwareShader_t shader, DataCacheHandle_t hCachedShader ) |
|
{ |
|
if ( m_HardwarePixelShader != shader ) |
|
{ |
|
Dx9Device()->SetPixelShader( (IDirect3DPixelShader*)shader ); |
|
m_HardwarePixelShader = shader; |
|
} |
|
} |
|
|
|
void CShaderManager::BindPixelShader( PixelShaderHandle_t hPixelShader ) |
|
{ |
|
HardwareShader_t hHardwareShader = m_RawPixelShaderDict[ (PixelShaderIndex_t)(uintp)hPixelShader ]; |
|
SetPixelShaderState( hHardwareShader ); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Sets a particular pixel shader as the current shader |
|
//----------------------------------------------------------------------------- |
|
void CShaderManager::SetPixelShader( PixelShader_t shader ) |
|
{ |
|
if ( shader == INVALID_SHADER ) |
|
{ |
|
SetPixelShaderState( 0 ); |
|
return; |
|
} |
|
|
|
int pshIndex = m_nPixelShaderIndex; |
|
Assert( pshIndex >= 0 ); |
|
ShaderLookup_t &pshLookup = m_PixelShaderDict[shader]; |
|
// Warning( "psh: %s static: %d dynamic: %d\n", m_ShaderSymbolTable.String( pshLookup.m_Name ), |
|
// pshLookup.m_nStaticIndex, m_nPixelShaderIndex ); |
|
|
|
#ifdef DYNAMIC_SHADER_COMPILE |
|
HardwareShader_t &dxshader = m_PixelShaderDict[shader].m_ShaderStaticCombos.m_pHardwareShaders[pshIndex]; |
|
if ( dxshader == INVALID_HARDWARE_SHADER ) |
|
{ |
|
// compile it since we haven't already! |
|
dxshader = CompileShader( m_ShaderSymbolTable.String( pshLookup.m_Name ), pshLookup.m_nStaticIndex, pshIndex, false ); |
|
// Assert( dxshader != INVALID_HARDWARE_SHADER ); |
|
|
|
if( IsX360() ) |
|
{ |
|
//360 does not respond well at all to bad shaders or Error() calls. So we're staying here until we get something that compiles |
|
while( dxshader == INVALID_HARDWARE_SHADER ) |
|
{ |
|
Warning( "A dynamically compiled pixel shader has failed to build. Pausing for 5 seconds and attempting rebuild.\n" ); |
|
#ifdef _WIN32 |
|
Sleep( 5000 ); |
|
#elif POSIX |
|
usleep( 5000 ); |
|
#endif |
|
dxshader = CompileShader( m_ShaderSymbolTable.String( pshLookup.m_Name ), pshLookup.m_nStaticIndex, pshIndex, false ); |
|
} |
|
} |
|
} |
|
#else |
|
if ( pshLookup.m_Flags & SHADER_FAILED_LOAD ) |
|
{ |
|
Assert( 0 ); |
|
return; |
|
} |
|
#ifdef _DEBUG |
|
pshDebugIndex = (pshDebugIndex + 1) % MAX_SHADER_HISTORY; |
|
Q_strncpy( pshDebugName[pshDebugIndex], m_ShaderSymbolTable.String( pshLookup.m_Name ), sizeof( pshDebugName[0] ) ); |
|
#endif |
|
HardwareShader_t dxshader = pshLookup.m_ShaderStaticCombos.m_pHardwareShaders[pshIndex]; |
|
#endif |
|
|
|
if ( IsPC() && ( dxshader == INVALID_HARDWARE_SHADER ) && m_bCreateShadersOnDemand ) |
|
{ |
|
#ifdef DYNAMIC_SHADER_COMPILE |
|
ShaderStaticCombos_t::ShaderCreationData_t *pCreationData = &m_PixelShaderDict[shader].m_ShaderStaticCombos.m_pCreationData[pshIndex]; |
|
#else |
|
ShaderStaticCombos_t::ShaderCreationData_t *pCreationData = &pshLookup.m_ShaderStaticCombos.m_pCreationData[pshIndex]; |
|
#endif |
|
|
|
const char *pShaderName = m_ShaderSymbolTable.String( pshLookup.m_Name ); |
|
dxshader = CreateD3DPixelShader( ( DWORD * )pCreationData->ByteCode.Base(), pCreationData->iCentroidMask, pCreationData->ByteCode.Count(), pShaderName ); |
|
|
|
#ifdef DYNAMIC_SHADER_COMPILE |
|
// copy the compiled shader handle back to wherever it's supposed to be stored |
|
m_PixelShaderDict[shader].m_ShaderStaticCombos.m_pHardwareShaders[pshIndex] = dxshader; |
|
#else |
|
pshLookup.m_ShaderStaticCombos.m_pHardwareShaders[pshIndex] = dxshader; |
|
#endif |
|
} |
|
|
|
AssertMsg( dxshader != INVALID_HARDWARE_SHADER, "Failed to set pixel shader." ); |
|
SetPixelShaderState( dxshader ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Resets the shader state |
|
//----------------------------------------------------------------------------- |
|
void CShaderManager::ResetShaderState() |
|
{ |
|
// This will force the calls to SetVertexShader + SetPixelShader to actually set the state |
|
m_HardwareVertexShader = (HardwareShader_t)-1; |
|
m_HardwarePixelShader = (HardwareShader_t)-1; |
|
|
|
SetVertexShader( INVALID_SHADER ); |
|
SetPixelShader( INVALID_SHADER ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Destroy a particular vertex shader |
|
//----------------------------------------------------------------------------- |
|
void CShaderManager::DestroyVertexShader( VertexShader_t shader ) |
|
{ |
|
ShaderStaticCombos_t &combos = m_VertexShaderDict[shader].m_ShaderStaticCombos; |
|
int i; |
|
for ( i = 0; i < combos.m_nCount; i++ ) |
|
{ |
|
if ( combos.m_pHardwareShaders[i] != INVALID_HARDWARE_SHADER ) |
|
{ |
|
IDirect3DVertexShader9* pShader = ( IDirect3DVertexShader9 * )combos.m_pHardwareShaders[i]; |
|
UnregisterVS( pShader ); |
|
#ifdef DBGFLAG_ASSERT |
|
int nRetVal = |
|
#endif |
|
pShader->Release(); |
|
Assert( nRetVal == 0 ); |
|
} |
|
} |
|
delete [] combos.m_pHardwareShaders; |
|
combos.m_pHardwareShaders = NULL; |
|
|
|
if ( combos.m_pCreationData != NULL ) |
|
{ |
|
delete [] combos.m_pCreationData; |
|
combos.m_pCreationData = NULL; |
|
} |
|
|
|
m_VertexShaderDict.Remove( shader ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Destroy a particular pixel shader |
|
//----------------------------------------------------------------------------- |
|
void CShaderManager::DestroyPixelShader( PixelShader_t pixelShader ) |
|
{ |
|
ShaderStaticCombos_t &combos = m_PixelShaderDict[pixelShader].m_ShaderStaticCombos; |
|
int i; |
|
for ( i = 0; i < combos.m_nCount; i++ ) |
|
{ |
|
if ( combos.m_pHardwareShaders[i] != INVALID_HARDWARE_SHADER ) |
|
{ |
|
IDirect3DPixelShader* pShader = ( IDirect3DPixelShader * )combos.m_pHardwareShaders[i]; |
|
UnregisterPS( pShader ); |
|
#ifdef DBGFLAG_ASSERT |
|
int nRetVal = |
|
#endif |
|
pShader->Release(); |
|
Assert( nRetVal == 0 ); |
|
} |
|
} |
|
delete [] combos.m_pHardwareShaders; |
|
combos.m_pHardwareShaders = NULL; |
|
|
|
if ( combos.m_pCreationData != NULL ) |
|
{ |
|
delete [] combos.m_pCreationData; |
|
combos.m_pCreationData = NULL; |
|
} |
|
|
|
m_PixelShaderDict.Remove( pixelShader ); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Destroys all shaders |
|
//----------------------------------------------------------------------------- |
|
void CShaderManager::DestroyAllShaders( void ) |
|
{ |
|
// Remarking this out because it's conflicting with dxabstract's shutdown resource leak detection code (we leak thousands of shaders at shutdown with this in place). |
|
// I see no reason why we would want to do this in D3D9 but not GL? |
|
//#ifdef DX_TO_GL_ABSTRACTION |
|
// return; |
|
//#endif |
|
|
|
for ( VertexShader_t vshIndex = m_VertexShaderDict.Head(); |
|
vshIndex != m_VertexShaderDict.InvalidIndex(); ) |
|
{ |
|
Assert( m_VertexShaderDict[vshIndex].m_nRefCount >= 0 ); |
|
VertexShader_t next = m_VertexShaderDict.Next( vshIndex ); |
|
DestroyVertexShader( vshIndex ); |
|
vshIndex = next; |
|
} |
|
|
|
for ( PixelShader_t pshIndex = m_PixelShaderDict.Head(); |
|
pshIndex != m_PixelShaderDict.InvalidIndex(); ) |
|
{ |
|
Assert( m_PixelShaderDict[pshIndex].m_nRefCount >= 0 ); |
|
PixelShader_t next = m_PixelShaderDict.Next( pshIndex ); |
|
DestroyPixelShader( pshIndex ); |
|
pshIndex = next; |
|
} |
|
|
|
// invalidate the file cache |
|
m_ShaderFileCache.Purge(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// print all vertex and pixel shaders along with refcounts to the console |
|
//----------------------------------------------------------------------------- |
|
void CShaderManager::SpewVertexAndPixelShaders( void ) |
|
{ |
|
// only spew a populated shader file cache |
|
Msg( "\nShader File Cache:\n" ); |
|
for ( intp cacheIndex = m_ShaderFileCache.Head(); |
|
cacheIndex != m_ShaderFileCache.InvalidIndex(); |
|
cacheIndex = m_ShaderFileCache.Next( cacheIndex ) ) |
|
{ |
|
ShaderFileCache_t *pCache = &m_ShaderFileCache[cacheIndex]; |
|
Msg( "Total Combos:%9d Static:%9d Dynamic:%7d SeekTable:%7d Ver:%d '%s'\n", |
|
pCache->m_Header.m_nTotalCombos, |
|
pCache->m_Header.m_nTotalCombos/pCache->m_Header.m_nDynamicCombos, |
|
pCache->m_Header.m_nDynamicCombos, |
|
pCache->IsOldVersion() ? 0 : pCache->m_Header.m_nNumStaticCombos, |
|
pCache->m_Header.m_nVersion, |
|
m_ShaderSymbolTable.String( pCache->m_Filename ) ); |
|
} |
|
Msg( "\n" ); |
|
|
|
// spew vertex shader dictionary |
|
int totalVertexShaders = 0; |
|
int totalVertexShaderSets = 0; |
|
for ( VertexShader_t vshIndex = m_VertexShaderDict.Head(); |
|
vshIndex != m_VertexShaderDict.InvalidIndex(); |
|
vshIndex = m_VertexShaderDict.Next( vshIndex ) ) |
|
{ |
|
const ShaderLookup_t &lookup = m_VertexShaderDict[vshIndex]; |
|
const char *pName = m_ShaderSymbolTable.String( lookup.m_Name ); |
|
Msg( "vsh 0x%8.8x: static combo:%9d dynamic combos:%6d refcount:%4d \"%s\"\n", vshIndex, |
|
( int )lookup.m_nStaticIndex, ( int )lookup.m_ShaderStaticCombos.m_nCount, |
|
lookup.m_nRefCount, pName ); |
|
totalVertexShaders += lookup.m_ShaderStaticCombos.m_nCount; |
|
totalVertexShaderSets++; |
|
} |
|
|
|
// spew pixel shader dictionary |
|
int totalPixelShaders = 0; |
|
int totalPixelShaderSets = 0; |
|
for ( PixelShader_t pshIndex = m_PixelShaderDict.Head(); |
|
pshIndex != m_PixelShaderDict.InvalidIndex(); |
|
pshIndex = m_PixelShaderDict.Next( pshIndex ) ) |
|
{ |
|
const ShaderLookup_t &lookup = m_PixelShaderDict[pshIndex]; |
|
const char *pName = m_ShaderSymbolTable.String( lookup.m_Name ); |
|
Msg( "psh 0x%8.8x: static combo:%9d dynamic combos:%6d refcount:%4d \"%s\"\n", pshIndex, |
|
( int )lookup.m_nStaticIndex, ( int )lookup.m_ShaderStaticCombos.m_nCount, |
|
lookup.m_nRefCount, pName ); |
|
totalPixelShaders += lookup.m_ShaderStaticCombos.m_nCount; |
|
totalPixelShaderSets++; |
|
} |
|
|
|
Msg( "Total unique vertex shaders: %d\n", totalVertexShaders ); |
|
Msg( "Total vertex shader sets: %d\n", totalVertexShaderSets ); |
|
Msg( "Total unique pixel shaders: %d\n", totalPixelShaders ); |
|
Msg( "Total pixel shader sets: %d\n", totalPixelShaderSets ); |
|
} |
|
|
|
CON_COMMAND( mat_spewvertexandpixelshaders, "Print all vertex and pixel shaders currently loaded to the console" ) |
|
{ |
|
( ( CShaderManager * )ShaderManager() )->SpewVertexAndPixelShaders(); |
|
} |
|
|
|
const char *CShaderManager::GetActiveVertexShaderName() |
|
{ |
|
#if !defined( _DEBUG ) |
|
return ""; |
|
#else |
|
if ( !m_HardwareVertexShader ) |
|
{ |
|
return "NULL"; |
|
} |
|
return vshDebugName[vshDebugIndex]; |
|
#endif |
|
} |
|
|
|
const char *CShaderManager::GetActivePixelShaderName() |
|
{ |
|
#if !defined( _DEBUG ) |
|
return ""; |
|
#else |
|
if ( !m_HardwarePixelShader ) |
|
{ |
|
return "NULL"; |
|
} |
|
return pshDebugName[pshDebugIndex]; |
|
#endif |
|
} |
|
|
|
#ifdef DYNAMIC_SHADER_COMPILE |
|
void CShaderManager::FlushShaders( void ) |
|
{ |
|
for( VertexShader_t shader = m_VertexShaderDict.Head(); |
|
shader != m_VertexShaderDict.InvalidIndex(); |
|
shader = m_VertexShaderDict.Next( shader ) ) |
|
{ |
|
int i; |
|
ShaderStaticCombos_t &combos = m_VertexShaderDict[shader].m_ShaderStaticCombos; |
|
for( i = 0; i < combos.m_nCount; i++ ) |
|
{ |
|
if( combos.m_pHardwareShaders[i] != INVALID_HARDWARE_SHADER ) |
|
{ |
|
#ifdef _DEBUG |
|
int nRetVal= |
|
#endif |
|
( ( IDirect3DVertexShader9 * )combos.m_pHardwareShaders[i] )->Release(); |
|
Assert( nRetVal == 0 ); |
|
} |
|
combos.m_pHardwareShaders[i] = INVALID_HARDWARE_SHADER; |
|
} |
|
} |
|
|
|
for( PixelShader_t shader = m_PixelShaderDict.Head(); |
|
shader != m_PixelShaderDict.InvalidIndex(); |
|
shader = m_PixelShaderDict.Next( shader ) ) |
|
{ |
|
int i; |
|
ShaderStaticCombos_t &combos = m_PixelShaderDict[shader].m_ShaderStaticCombos; |
|
for( i = 0; i < combos.m_nCount; i++ ) |
|
{ |
|
if( combos.m_pHardwareShaders[i] != INVALID_HARDWARE_SHADER ) |
|
{ |
|
#ifdef _DEBUG |
|
int nRetVal = |
|
#endif |
|
( ( IDirect3DPixelShader * )combos.m_pHardwareShaders[i] )->Release(); |
|
Assert( nRetVal == 0 ); |
|
} |
|
combos.m_pHardwareShaders[i] = INVALID_HARDWARE_SHADER; |
|
} |
|
} |
|
|
|
// invalidate the file cache |
|
m_ShaderFileCache.Purge(); |
|
} |
|
#endif |
|
|
|
#ifdef DYNAMIC_SHADER_COMPILE |
|
static void MatFlushShaders( void ) |
|
{ |
|
#if defined( _X360 ) |
|
XBX_rSyncShaderCache(); |
|
#endif |
|
( ( CShaderManager * )ShaderManager() )->FlushShaders(); |
|
} |
|
#endif |
|
|
|
#ifdef DYNAMIC_SHADER_COMPILE |
|
CON_COMMAND( mat_flushshaders, "flush all hardware shaders when using DYNAMIC_SHADER_COMPILE" ) |
|
{ |
|
MatFlushShaders(); |
|
} |
|
#endif |
|
|
|
CON_COMMAND( mat_shadercount, "display count of all shaders and reset that count" ) |
|
{ |
|
Warning( "Num Pixel Shaders = %d Vertex Shaders=%d\n", s_NumPixelShadersCreated, s_NumVertexShadersCreated ); |
|
s_NumVertexShadersCreated = 0; |
|
s_NumPixelShadersCreated = 0; |
|
} |
|
|
|
#if defined( DX_TO_GL_ABSTRACTION ) |
|
void CShaderManager::DoStartupShaderPreloading() |
|
{ |
|
#ifdef ANDROID // Too slow |
|
return; |
|
#endif |
|
|
|
if (mat_autoload_glshaders.GetInt()) |
|
{ |
|
double flStartTime = Plat_FloatTime(); |
|
|
|
s_NumVertexShadersCreated = s_NumPixelShadersCreated = 0; |
|
|
|
// try base file |
|
#ifdef OSX |
|
if ( !LoadShaderCache("glbaseshaders_osx.cfg") ) // factory cache |
|
#else |
|
if ( !LoadShaderCache("glbaseshaders.cfg") ) // factory cache |
|
#endif |
|
{ |
|
Warning( "Could not find base GL shader cache file\n" ); |
|
} |
|
|
|
if ( !LoadShaderCache("glshaders.cfg") ) // user mutable cache |
|
{ |
|
Warning( "Could not find user GL shader cache file\n" ); |
|
} |
|
|
|
double flEndTime = Plat_FloatTime(); |
|
Msg( "Precache: Took %d ms, Vertex %d, Pixel %d\n", ( int )( ( flEndTime - flStartTime ) * 1000.0 ), s_NumVertexShadersCreated, s_NumPixelShadersCreated ); |
|
} |
|
} |
|
#endif |
|
|
|
|