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.
2056 lines
64 KiB
2056 lines
64 KiB
//========= Copyright Valve Corporation, All rights reserved. ============// |
|
// |
|
// Purpose: |
|
// |
|
//===========================================================================// |
|
|
|
#include "shadersystem.h" |
|
#include <stdlib.h> |
|
#include "materialsystem_global.h" |
|
#include "filesystem.h" |
|
#include "tier1/utldict.h" |
|
#include "shaderlib/ShaderDLL.h" |
|
#include "texturemanager.h" |
|
#include "itextureinternal.h" |
|
#include "IHardwareConfigInternal.h" |
|
#include "tier1/utlstack.h" |
|
#include "tier1/utlbuffer.h" |
|
#include "mathlib/vmatrix.h" |
|
#include "imaterialinternal.h" |
|
#include "tier1/strtools.h" |
|
#include "tier0/icommandline.h" |
|
#include "shaderlib/cshader.h" |
|
#include "tier1/convar.h" |
|
#include "tier1/KeyValues.h" |
|
#include "shader_dll_verify.h" |
|
#include "tier0/vprof.h" |
|
|
|
// NOTE: This must be the last file included! |
|
#include "tier0/memdbgon.h" |
|
#include "mat_stub.h" |
|
|
|
|
|
//#define DEBUG_DEPTH 1 |
|
|
|
//----------------------------------------------------------------------------- |
|
// Lovely convars |
|
//----------------------------------------------------------------------------- |
|
static ConVar mat_showenvmapmask( "mat_showenvmapmask", "0" ); |
|
static ConVar mat_debugdepth( "mat_debugdepth", "0" ); |
|
extern ConVar mat_supportflashlight; |
|
|
|
//----------------------------------------------------------------------------- |
|
// Implementation of the shader system |
|
//----------------------------------------------------------------------------- |
|
class CShaderSystem : public IShaderSystemInternal |
|
{ |
|
public: |
|
CShaderSystem(); |
|
|
|
// Methods of IShaderSystem |
|
virtual ShaderAPITextureHandle_t GetShaderAPITextureBindHandle( ITexture *pTexture, int nFrameVar, int nTextureChannel = 0 ); |
|
|
|
virtual void BindTexture( Sampler_t sampler1, ITexture *pTexture, int nFrame = 0 ); |
|
virtual void BindTexture( Sampler_t sampler1, Sampler_t sampler2, ITexture *pTexture, int nFrame = 0 ); |
|
|
|
virtual void TakeSnapshot( ); |
|
virtual void DrawSnapshot( bool bMakeActualDrawCall = true ); |
|
virtual bool IsUsingGraphics() const; |
|
virtual bool CanUseEditorMaterials() const; |
|
|
|
// Methods of IShaderSystemInternal |
|
virtual void Init(); |
|
virtual void Shutdown(); |
|
virtual void ModInit(); |
|
virtual void ModShutdown(); |
|
|
|
virtual bool LoadShaderDLL( const char *pFullPath ); |
|
virtual bool LoadShaderDLL( const char *pFullPath, const char *pPathID, bool bModShaderDLL ); |
|
virtual void UnloadShaderDLL( const char *pFullPath ); |
|
|
|
virtual IShader* FindShader( char const* pShaderName ); |
|
virtual void CreateDebugMaterials(); |
|
virtual void CleanUpDebugMaterials(); |
|
virtual char const* ShaderStateString( int i ) const; |
|
virtual int ShaderStateCount( ) const; |
|
|
|
virtual void InitShaderParameters( IShader *pShader, IMaterialVar **params, const char *pMaterialName ); |
|
virtual void InitShaderInstance( IShader *pShader, IMaterialVar **params, const char *pMaterialName, const char *pTextureGroupName ); |
|
virtual bool InitRenderState( IShader *pShader, int numParams, IMaterialVar **params, ShaderRenderState_t* pRenderState, char const* pMaterialName ); |
|
virtual void CleanupRenderState( ShaderRenderState_t* pRenderState ); |
|
virtual void DrawElements( IShader *pShader, IMaterialVar **params, ShaderRenderState_t* pShaderState, VertexCompressionType_t vertexCompression, uint32 nVarChangeID ); |
|
|
|
// Used to iterate over all shaders for editing purposes |
|
virtual int ShaderCount() const; |
|
virtual int GetShaders( int nFirstShader, int nMaxCount, IShader **ppShaderList ) const; |
|
|
|
// Methods of IShaderInit |
|
virtual void LoadTexture( IMaterialVar *pTextureVar, const char *pTextureGroupName, int nAdditionalCreationFlags = 0 ); |
|
virtual void LoadBumpMap( IMaterialVar *pTextureVar, const char *pTextureGroupName ); |
|
virtual void LoadCubeMap( IMaterialVar **ppParams, IMaterialVar *pTextureVar, int nAdditionalCreationFlags = 0 ); |
|
|
|
// Used to prevent re-entrant rendering from warning messages |
|
void BufferSpew( SpewType_t spewType, const Color &c, const char *pMsg ); |
|
|
|
private: |
|
struct ShaderDLLInfo_t |
|
{ |
|
char *m_pFileName; |
|
CSysModule *m_hInstance; |
|
IShaderDLLInternal *m_pShaderDLL; |
|
ShaderDLL_t m_hShaderDLL; |
|
|
|
// True if this is a mod's shader DLL, in which case it's not allowed to |
|
// override any existing shader names. |
|
bool m_bModShaderDLL; |
|
CUtlDict< IShader *, unsigned short > m_ShaderDict; |
|
}; |
|
|
|
private: |
|
// hackhack: remove this when VAC2 is online. |
|
void VerifyBaseShaderDLL( CSysModule *pModule ); |
|
|
|
// Load up the shader DLLs... |
|
void LoadAllShaderDLLs(); |
|
|
|
// Load the "mshader_" DLLs. |
|
void LoadModShaderDLLs( int dxSupportLevel ); |
|
|
|
// Unload all the shader DLLs... |
|
void UnloadAllShaderDLLs(); |
|
|
|
// Sets up the shader dictionary. |
|
void SetupShaderDictionary( int nShaderDLLIndex ); |
|
|
|
// Cleans up the shader dictionary. |
|
void CleanupShaderDictionary( int nShaderDLLIndex ); |
|
|
|
// Finds an already loaded shader DLL |
|
int FindShaderDLL( const char *pFullPath ); |
|
|
|
// Unloads a particular shader DLL |
|
void UnloadShaderDLL( int nShaderDLLIndex ); |
|
|
|
// Sets up the current ShaderState_t for rendering |
|
void PrepForShaderDraw( IShader *pShader, IMaterialVar** ppParams, |
|
ShaderRenderState_t* pRenderState, int modulation ); |
|
void DoneWithShaderDraw(); |
|
|
|
// Initializes state snapshots |
|
void InitStateSnapshots( IShader *pShader, IMaterialVar **params, ShaderRenderState_t* pRenderState ); |
|
|
|
// Compute snapshots for all combinations of alpha + color modulation |
|
void InitRenderStateFlags( ShaderRenderState_t* pRenderState, int numParams, IMaterialVar **params ); |
|
|
|
// Computes flags from a particular snapshot |
|
void ComputeRenderStateFlagsFromSnapshot( ShaderRenderState_t* pRenderState ); |
|
|
|
// Computes vertex format + usage from a particular snapshot |
|
bool ComputeVertexFormatFromSnapshot( IMaterialVar **params, ShaderRenderState_t* pRenderState ); |
|
|
|
// Used to prevent re-entrant rendering from warning messages |
|
void PrintBufferedSpew( void ); |
|
|
|
// Gets at the current snapshot |
|
StateSnapshot_t CurrentStateSnapshot(); |
|
|
|
// Draws using a particular material.. |
|
void DrawUsingMaterial( IMaterialInternal *pMaterial, VertexCompressionType_t vertexCompression ); |
|
|
|
// Copies material vars |
|
void CopyMaterialVarToDebugShader( IMaterialInternal *pDebugMaterial, IShader *pShader, IMaterialVar **ppParams, const char *pSrcVarName, const char *pDstVarName = NULL ); |
|
|
|
// Debugging draw methods... |
|
void DrawMeasureFillRate( ShaderRenderState_t* pRenderState, int mod, VertexCompressionType_t vertexCompression ); |
|
void DrawNormalMap( IShader *pShader, IMaterialVar **ppParams, VertexCompressionType_t vertexCompression ); |
|
bool DrawEnvmapMask( IShader *pShader, IMaterialVar **ppParams, ShaderRenderState_t* pRenderState, VertexCompressionType_t vertexCompression ); |
|
|
|
int GetModulationSnapshotCount( IMaterialVar **params ); |
|
|
|
private: |
|
// List of all DLLs containing shaders |
|
CUtlVector< ShaderDLLInfo_t > m_ShaderDLLs; |
|
|
|
// Used to prevent re-entrant rendering from warning messages |
|
SpewOutputFunc_t m_SaveSpewOutput; |
|
|
|
CUtlBuffer m_StoredSpew; |
|
|
|
// Render state we're drawing with |
|
ShaderRenderState_t* m_pRenderState; |
|
unsigned short m_hShaderDLL; |
|
unsigned char m_nModulation; |
|
unsigned char m_nRenderPass; |
|
|
|
// Debugging materials |
|
// If you add to this, add to the list of debug shader names (s_pDebugShaderName) below |
|
enum |
|
{ |
|
MATERIAL_FILL_RATE = 0, |
|
MATERIAL_DEBUG_NORMALMAP, |
|
MATERIAL_DEBUG_ENVMAPMASK, |
|
MATERIAL_DEBUG_DEPTH, |
|
MATERIAL_DEBUG_DEPTH_DECAL, |
|
MATERIAL_DEBUG_WIREFRAME, |
|
|
|
MATERIAL_DEBUG_COUNT, |
|
}; |
|
|
|
IMaterialInternal* m_pDebugMaterials[MATERIAL_DEBUG_COUNT]; |
|
static const char *s_pDebugShaderName[MATERIAL_DEBUG_COUNT]; |
|
|
|
bool m_bForceUsingGraphicsReturnTrue; |
|
}; |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Singleton |
|
//----------------------------------------------------------------------------- |
|
static CShaderSystem s_ShaderSystem; |
|
IShaderSystemInternal *g_pShaderSystem = &s_ShaderSystem; |
|
EXPOSE_SINGLE_INTERFACE_GLOBALVAR( CShaderSystem, IShaderSystem, |
|
SHADERSYSTEM_INTERFACE_VERSION, s_ShaderSystem ); |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Debugging shader names |
|
//----------------------------------------------------------------------------- |
|
const char *CShaderSystem::s_pDebugShaderName[MATERIAL_DEBUG_COUNT] = |
|
{ |
|
"FillRate", |
|
"DebugNormalMap", |
|
"DebugDrawEnvmapMask", |
|
"DebugDepth", |
|
"DebugDepth", |
|
"Wireframe_DX9" |
|
}; |
|
|
|
//----------------------------------------------------------------------------- |
|
// Constructor |
|
//----------------------------------------------------------------------------- |
|
CShaderSystem::CShaderSystem() : m_StoredSpew( 0, 512, 0 ), m_bForceUsingGraphicsReturnTrue( false ) |
|
{ |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Initialization, shutdown |
|
//----------------------------------------------------------------------------- |
|
void CShaderSystem::Init() |
|
{ |
|
m_SaveSpewOutput = NULL; |
|
|
|
m_bForceUsingGraphicsReturnTrue = false; |
|
if ( CommandLine()->FindParm( "-noshaderapi" ) || |
|
CommandLine()->FindParm( "-makereslists" ) ) |
|
{ |
|
m_bForceUsingGraphicsReturnTrue = true; |
|
} |
|
|
|
for ( int i = 0; i < MATERIAL_DEBUG_COUNT; ++i ) |
|
{ |
|
m_pDebugMaterials[i] = NULL; |
|
} |
|
|
|
LoadAllShaderDLLs(); |
|
} |
|
|
|
void CShaderSystem::Shutdown() |
|
{ |
|
UnloadAllShaderDLLs(); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Load/unload mod-specific shader DLLs |
|
//----------------------------------------------------------------------------- |
|
void CShaderSystem::ModInit() |
|
{ |
|
// Load up standard shader DLLs... |
|
int dxSupportLevel = HardwareConfig()->GetMaxDXSupportLevel(); |
|
Assert( dxSupportLevel >= 60 ); |
|
dxSupportLevel /= 10; |
|
|
|
LoadModShaderDLLs( dxSupportLevel ); |
|
} |
|
|
|
|
|
void CShaderSystem::ModShutdown() |
|
{ |
|
// Unload only MOD dlls |
|
for ( int i = m_ShaderDLLs.Count(); --i >= 0; ) |
|
{ |
|
if ( m_ShaderDLLs[i].m_bModShaderDLL ) |
|
{ |
|
UnloadShaderDLL(i); |
|
delete[] m_ShaderDLLs[i].m_pFileName; |
|
m_ShaderDLLs.Remove( i ); |
|
} |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Load up the shader DLLs... |
|
//----------------------------------------------------------------------------- |
|
void CShaderSystem::LoadAllShaderDLLs( ) |
|
{ |
|
UnloadAllShaderDLLs(); |
|
|
|
GetShaderDLLInternal()->Connect( Sys_GetFactoryThis(), true ); |
|
|
|
// Loads local defined or statically linked shaders |
|
int i = m_ShaderDLLs.AddToHead(); |
|
|
|
m_ShaderDLLs[i].m_pFileName = new char[1]; |
|
m_ShaderDLLs[i].m_pFileName[0] = 0; |
|
m_ShaderDLLs[i].m_hInstance = NULL; |
|
m_ShaderDLLs[i].m_pShaderDLL = GetShaderDLLInternal(); |
|
m_ShaderDLLs[i].m_bModShaderDLL = false; |
|
|
|
// Add the shaders to the dictionary of shaders... |
|
SetupShaderDictionary( i ); |
|
|
|
// 360 has the the debug shaders in its dx9 dll |
|
if ( IsPC() || !IsX360() ) |
|
{ |
|
// Always need the debug shaders |
|
LoadShaderDLL( "stdshader_dbg" DLL_EXT_STRING ); |
|
} |
|
|
|
// Load up standard shader DLLs... |
|
int dxSupportLevel = HardwareConfig()->GetMaxDXSupportLevel(); |
|
Assert( dxSupportLevel >= 60 ); |
|
dxSupportLevel /= 10; |
|
|
|
// 360 only supports its dx9 dll |
|
int dxStart = IsX360() ? 9 : 6; |
|
char buf[32]; |
|
for ( i = dxStart; i <= dxSupportLevel; ++i ) |
|
{ |
|
Q_snprintf( buf, sizeof( buf ), "stdshader_dx%d%s", i, DLL_EXT_STRING ); |
|
LoadShaderDLL( buf ); |
|
} |
|
|
|
const char *pShaderName = NULL; |
|
#ifdef _DEBUG |
|
pShaderName = CommandLine()->ParmValue( "-shader" ); |
|
#endif |
|
if ( !pShaderName ) |
|
{ |
|
pShaderName = HardwareConfig()->GetHWSpecificShaderDLLName(); |
|
} |
|
if ( pShaderName ) |
|
{ |
|
LoadShaderDLL( pShaderName ); |
|
} |
|
|
|
#ifdef _DEBUG |
|
// For fast-iteration debugging |
|
if ( CommandLine()->FindParm( "-testshaders" ) ) |
|
{ |
|
LoadShaderDLL( "shader_test" DLL_EXT_STRING ); |
|
} |
|
#endif |
|
} |
|
|
|
const char *COM_GetModDirectory() |
|
{ |
|
static char modDir[MAX_PATH]; |
|
if ( Q_strlen( modDir ) == 0 ) |
|
{ |
|
const char *gamedir = CommandLine()->ParmValue("-game", CommandLine()->ParmValue( "-defaultgamedir", "hl2" ) ); |
|
Q_strncpy( modDir, gamedir, sizeof(modDir) ); |
|
if ( strchr( modDir, '/' ) || strchr( modDir, '\\' ) ) |
|
{ |
|
Q_StripLastDir( modDir, sizeof(modDir) ); |
|
int dirlen = Q_strlen( modDir ); |
|
Q_strncpy( modDir, gamedir + dirlen, sizeof(modDir) - dirlen ); |
|
} |
|
} |
|
|
|
return modDir; |
|
} |
|
|
|
void CShaderSystem::LoadModShaderDLLs( int dxSupportLevel ) |
|
{ |
|
if ( IsX360() ) |
|
return; |
|
|
|
// Don't do this for Valve mods. They don't need them, and attempting to load them is an opportunity for cheaters to get their code into the process |
|
const char *pGameDir = COM_GetModDirectory(); |
|
if ( !Q_stricmp( pGameDir, "hl2" ) || !Q_stricmp( pGameDir, "cstrike" ) || !Q_stricmp( pGameDir, "cstrike_beta" ) || |
|
!Q_stricmp( pGameDir, "hl2mp" ) || !Q_stricmp( pGameDir, "lostcoast" ) || !Q_stricmp( pGameDir, "episodic" ) || |
|
!Q_stricmp( pGameDir, "portal" ) || !Q_stricmp( pGameDir, "ep2" ) || !Q_stricmp( pGameDir, "dod" ) || |
|
!Q_stricmp( pGameDir, "tf" ) || !Q_stricmp( pGameDir, "tf_beta" ) || !Q_stricmp( pGameDir, "hl1" ) ) |
|
{ |
|
return; |
|
} |
|
|
|
const char *pModShaderPathID = "GAMEBIN"; |
|
|
|
// First load the ones with dx_ prefix. |
|
char buf[256]; |
|
|
|
int dxStart = 6; |
|
for ( int i = dxStart; i <= dxSupportLevel; ++i ) |
|
{ |
|
Q_snprintf( buf, sizeof( buf ), "game_shader_dx%d%s", i, DLL_EXT_STRING ); |
|
LoadShaderDLL( buf, pModShaderPathID, true ); |
|
} |
|
|
|
// Now load the ones with any dx_ prefix. |
|
FileFindHandle_t findHandle; |
|
const char *pFilename = g_pFullFileSystem->FindFirstEx( "game_shader_generic*", pModShaderPathID, &findHandle ); |
|
while ( pFilename ) |
|
{ |
|
Q_snprintf( buf, sizeof( buf ), "%s%s", pFilename, DLL_EXT_STRING ); |
|
LoadShaderDLL( buf, pModShaderPathID, true ); |
|
|
|
pFilename = g_pFullFileSystem->FindNext( findHandle ); |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Unload all the shader DLLs... |
|
//----------------------------------------------------------------------------- |
|
void CShaderSystem::UnloadAllShaderDLLs() |
|
{ |
|
if ( m_ShaderDLLs.Count() == 0 ) |
|
return; |
|
|
|
for ( int i = m_ShaderDLLs.Count(); --i >= 0; ) |
|
{ |
|
UnloadShaderDLL(i); |
|
delete[] m_ShaderDLLs[i].m_pFileName; |
|
} |
|
|
|
m_ShaderDLLs.RemoveAll(); |
|
} |
|
|
|
bool CShaderSystem::LoadShaderDLL( const char *pFullPath ) |
|
{ |
|
return LoadShaderDLL( pFullPath, NULL, false ); |
|
} |
|
|
|
// HACKHACK: remove me when VAC2 is online. |
|
#if defined( _WIN32 ) && !defined( _X360 ) |
|
// Instead of including windows.h |
|
extern "C" |
|
{ |
|
extern void * __stdcall GetProcAddress( void *hModule, const char *pszProcName ); |
|
}; |
|
#endif |
|
|
|
void CShaderSystem::VerifyBaseShaderDLL( CSysModule *pModule ) |
|
{ |
|
#if defined( _WIN32 ) && !defined( _X360 ) |
|
const char *pErrorStr = "Corrupt save data settings."; |
|
|
|
unsigned char *testData1 = new unsigned char[SHADER_DLL_VERIFY_DATA_LEN1]; |
|
|
|
ShaderDLLVerifyFn fn = (ShaderDLLVerifyFn)GetProcAddress( (void *)pModule, SHADER_DLL_FNNAME_1 ); |
|
if ( !fn ) |
|
Error( pErrorStr ); |
|
|
|
IShaderDLLVerification *pVerify; |
|
char *pPtr = (char*)(void*)&pVerify; |
|
pPtr -= SHADER_DLL_VERIFY_DATA_PTR_OFFSET; |
|
fn( pPtr ); |
|
|
|
// Test the first CRC. |
|
CRC32_t testCRC; |
|
CRC32_Init( &testCRC ); |
|
CRC32_ProcessBuffer( &testCRC, testData1, SHADER_DLL_VERIFY_DATA_LEN1 ); |
|
CRC32_ProcessBuffer( &testCRC, &pModule, 4 ); |
|
CRC32_ProcessBuffer( &testCRC, &pVerify, 4 ); |
|
CRC32_Final( &testCRC ); |
|
if ( testCRC != pVerify->Function1( testData1 - SHADER_DLL_VERIFY_DATA_PTR_OFFSET ) ) |
|
Error( pErrorStr ); |
|
|
|
// Test the next one. |
|
unsigned char digest[MD5_DIGEST_LENGTH]; |
|
MD5Context_t md5Context; |
|
MD5Init( &md5Context ); |
|
MD5Update( &md5Context, testData1 + SHADER_DLL_VERIFY_DATA_PTR_OFFSET, SHADER_DLL_VERIFY_DATA_LEN1 - SHADER_DLL_VERIFY_DATA_PTR_OFFSET ); |
|
MD5Final( digest, &md5Context ); |
|
pVerify->Function2( 2, 3, 3 ); // fn2 is supposed to place the result in testData1. |
|
if ( memcmp( digest, testData1, MD5_DIGEST_LENGTH ) != 0 ) |
|
Error( pErrorStr ); |
|
|
|
pVerify->Function5(); |
|
|
|
delete [] testData1; |
|
#endif |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Methods related to reading in shader DLLs |
|
//----------------------------------------------------------------------------- |
|
bool CShaderSystem::LoadShaderDLL( const char *pFullPath, const char *pPathID, bool bModShaderDLL ) |
|
{ |
|
if ( !pFullPath && !pFullPath[0] ) |
|
return true; |
|
|
|
// Load the new shader |
|
bool bValidatedDllOnly = true; |
|
if ( bModShaderDLL ) |
|
bValidatedDllOnly = false; |
|
|
|
CSysModule *hInstance = g_pFullFileSystem->LoadModule( pFullPath, pPathID, bValidatedDllOnly ); |
|
if ( !hInstance ) |
|
return false; |
|
|
|
// Get at the shader DLL interface |
|
CreateInterfaceFn factory = Sys_GetFactory( hInstance ); |
|
if (!factory) |
|
{ |
|
g_pFullFileSystem->UnloadModule( hInstance ); |
|
return false; |
|
} |
|
|
|
IShaderDLLInternal *pShaderDLL = (IShaderDLLInternal*)factory( SHADER_DLL_INTERFACE_VERSION, NULL ); |
|
if ( !pShaderDLL ) |
|
{ |
|
g_pFullFileSystem->UnloadModule( hInstance ); |
|
return false; |
|
} |
|
|
|
// Make sure it's a valid base shader DLL if necessary. |
|
//HACKHACK get rid of this when VAC2 comes online. |
|
if ( !bModShaderDLL ) |
|
{ |
|
VerifyBaseShaderDLL( hInstance ); |
|
} |
|
|
|
// Allow the DLL to try to connect to interfaces it needs |
|
if ( !pShaderDLL->Connect( Sys_GetFactoryThis(), false ) ) |
|
{ |
|
g_pFullFileSystem->UnloadModule( hInstance ); |
|
return false; |
|
} |
|
|
|
// FIXME: We need to do some sort of shader validation here for anticheat. |
|
|
|
// Now replace any existing shader |
|
int nShaderDLLIndex = FindShaderDLL( pFullPath ); |
|
if ( nShaderDLLIndex >= 0 ) |
|
{ |
|
UnloadShaderDLL( nShaderDLLIndex ); |
|
} |
|
else |
|
{ |
|
nShaderDLLIndex = m_ShaderDLLs.AddToTail(); |
|
int nLen = Q_strlen(pFullPath) + 1; |
|
m_ShaderDLLs[nShaderDLLIndex].m_pFileName = new char[ nLen ]; |
|
Q_strncpy( m_ShaderDLLs[nShaderDLLIndex].m_pFileName, pFullPath, nLen ); |
|
} |
|
|
|
// Ok, the shader DLL's good! |
|
m_ShaderDLLs[nShaderDLLIndex].m_hInstance = hInstance; |
|
m_ShaderDLLs[nShaderDLLIndex].m_pShaderDLL = pShaderDLL; |
|
m_ShaderDLLs[nShaderDLLIndex].m_bModShaderDLL = bModShaderDLL; |
|
|
|
// Add the shaders to the dictionary of shaders... |
|
SetupShaderDictionary( nShaderDLLIndex ); |
|
|
|
// FIXME: Fix up existing materials that were using shaders that have |
|
// been reloaded? |
|
|
|
return true; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Finds an already loaded shader DLL |
|
//----------------------------------------------------------------------------- |
|
int CShaderSystem::FindShaderDLL( const char *pFullPath ) |
|
{ |
|
for ( int i = m_ShaderDLLs.Count(); --i >= 0; ) |
|
{ |
|
if ( !Q_stricmp( pFullPath, m_ShaderDLLs[i].m_pFileName ) ) |
|
return i; |
|
} |
|
|
|
return -1; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Unloads a particular shader DLL |
|
//----------------------------------------------------------------------------- |
|
void CShaderSystem::UnloadShaderDLL( int nShaderDLLIndex ) |
|
{ |
|
if ( nShaderDLLIndex < 0 ) |
|
return; |
|
|
|
// FIXME: Do some sort of fixup of materials to determine which |
|
// materials are referencing shaders in this DLL? |
|
CleanupShaderDictionary( nShaderDLLIndex ); |
|
IShaderDLLInternal *pShaderDLL = m_ShaderDLLs[nShaderDLLIndex].m_pShaderDLL; |
|
pShaderDLL->Disconnect( pShaderDLL == GetShaderDLLInternal() ); |
|
if ( m_ShaderDLLs[nShaderDLLIndex].m_hInstance ) |
|
{ |
|
g_pFullFileSystem->UnloadModule( m_ShaderDLLs[nShaderDLLIndex].m_hInstance ); |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Unloads a particular shader DLL |
|
//----------------------------------------------------------------------------- |
|
void CShaderSystem::UnloadShaderDLL( const char *pFullPath ) |
|
{ |
|
int nShaderDLLIndex = FindShaderDLL( pFullPath ); |
|
if ( nShaderDLLIndex >= 0 ) |
|
{ |
|
UnloadShaderDLL( nShaderDLLIndex ); |
|
delete[] m_ShaderDLLs[nShaderDLLIndex].m_pFileName; |
|
m_ShaderDLLs.Remove( nShaderDLLIndex ); |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Make sure these match the bits in imaterial.h |
|
//----------------------------------------------------------------------------- |
|
static const char* s_pShaderStateString[] = |
|
{ |
|
"$debug", |
|
"$no_fullbright", |
|
"$no_draw", |
|
"$use_in_fillrate_mode", |
|
|
|
"$vertexcolor", |
|
"$vertexalpha", |
|
"$selfillum", |
|
"$additive", |
|
"$alphatest", |
|
"$multipass", |
|
"$znearer", |
|
"$model", |
|
"$flat", |
|
"$nocull", |
|
"$nofog", |
|
"$ignorez", |
|
"$decal", |
|
"$envmapsphere", |
|
"$noalphamod", |
|
"$envmapcameraspace", |
|
"$basealphaenvmapmask", |
|
"$translucent", |
|
"$normalmapalphaenvmapmask", |
|
"$softwareskin", |
|
"$opaquetexture", |
|
"$envmapmode", |
|
"$nodecal", |
|
"$halflambert", |
|
"$wireframe", |
|
"$allowalphatocoverage", |
|
|
|
"" // last one must be null |
|
}; |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// returns strings associated with the shader state flags... |
|
// If you modify this, make sure and modify MaterialVarFlags_t in imaterial.h |
|
//----------------------------------------------------------------------------- |
|
int CShaderSystem::ShaderStateCount( ) const |
|
{ |
|
return sizeof( s_pShaderStateString ) / sizeof( char* ) - 1; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// returns strings associated with the shader state flags... |
|
// If you modify this, make sure and modify MaterialVarFlags_t in imaterial.h |
|
//----------------------------------------------------------------------------- |
|
char const* CShaderSystem::ShaderStateString( int i ) const |
|
{ |
|
return s_pShaderStateString[i]; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Sets up the shader dictionary. |
|
//----------------------------------------------------------------------------- |
|
void CShaderSystem::SetupShaderDictionary( int nShaderDLLIndex ) |
|
{ |
|
// We could have put the shader dictionary into each shader DLL |
|
// I'm not sure if that makes this system any less secure than it already is |
|
int i; |
|
ShaderDLLInfo_t &info = m_ShaderDLLs[nShaderDLLIndex]; |
|
int nCount = info.m_pShaderDLL->ShaderCount(); |
|
for ( i = 0; i < nCount; ++i ) |
|
{ |
|
IShader *pShader = info.m_pShaderDLL->GetShader( i ); |
|
const char *pShaderName = pShader->GetName(); |
|
|
|
#ifdef POSIX |
|
if (CommandLine()->FindParm("-glmspew")) |
|
printf("CShaderSystem::SetupShaderDictionary: %s", pShaderName ); |
|
#endif |
|
|
|
// Make sure it doesn't try to override another shader DLL's names. |
|
if ( info.m_bModShaderDLL ) |
|
{ |
|
for ( int iTestDLL=0; iTestDLL < m_ShaderDLLs.Count(); iTestDLL++ ) |
|
{ |
|
ShaderDLLInfo_t *pTestDLL = &m_ShaderDLLs[iTestDLL]; |
|
if ( !pTestDLL->m_bModShaderDLL ) |
|
{ |
|
if ( pTestDLL->m_ShaderDict.Find( pShaderName ) != pTestDLL->m_ShaderDict.InvalidIndex() ) |
|
{ |
|
Error( "Game shader '%s' trying to override a base shader '%s'.", info.m_pFileName, pShaderName ); |
|
} |
|
} |
|
} |
|
} |
|
|
|
info.m_ShaderDict.Insert( pShaderName, pShader ); |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Cleans up the shader dictionary. |
|
//----------------------------------------------------------------------------- |
|
void CShaderSystem::CleanupShaderDictionary( int nShaderDLLIndex ) |
|
{ |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Finds a shader in the shader dictionary |
|
//----------------------------------------------------------------------------- |
|
IShader* CShaderSystem::FindShader( char const* pShaderName ) |
|
{ |
|
// FIXME: What kind of search order should we use here? |
|
// I'm currently assuming last added, first searched. |
|
for (int i = m_ShaderDLLs.Count(); --i >= 0; ) |
|
{ |
|
ShaderDLLInfo_t &info = m_ShaderDLLs[i]; |
|
unsigned short idx = info.m_ShaderDict.Find( pShaderName ); |
|
if ( idx != info.m_ShaderDict.InvalidIndex() ) |
|
{ |
|
return info.m_ShaderDict[idx]; |
|
} |
|
} |
|
|
|
return NULL; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Used to iterate over all shaders for editing purposes |
|
//----------------------------------------------------------------------------- |
|
int CShaderSystem::ShaderCount() const |
|
{ |
|
return GetShaders( 0, 65536, NULL ); |
|
} |
|
|
|
int CShaderSystem::GetShaders( int nFirstShader, int nMaxCount, IShader **ppShaderList ) const |
|
{ |
|
CUtlSymbolTable uniqueNames( 0, 512, true ); |
|
|
|
int nCount = 0; |
|
int nActualCount = 0; |
|
for ( int i = m_ShaderDLLs.Count(); --i >= 0; ) |
|
{ |
|
const ShaderDLLInfo_t &info = m_ShaderDLLs[i]; |
|
for ( unsigned short j = info.m_ShaderDict.First(); |
|
j != info.m_ShaderDict.InvalidIndex(); |
|
j = info.m_ShaderDict.Next( j ) ) |
|
{ |
|
// Don't add shaders twice |
|
const char *pShaderName = info.m_ShaderDict.GetElementName( j ); |
|
if ( uniqueNames.Find( pShaderName ) != UTL_INVAL_SYMBOL ) |
|
continue; |
|
|
|
// Indicate we've seen this shader |
|
uniqueNames.AddString( pShaderName ); |
|
|
|
++nActualCount; |
|
if ( nActualCount > nFirstShader ) |
|
{ |
|
if ( ppShaderList ) |
|
{ |
|
ppShaderList[ nCount ] = info.m_ShaderDict[j]; |
|
} |
|
++nCount; |
|
if ( nCount >= nMaxCount ) |
|
return nCount; |
|
} |
|
} |
|
} |
|
|
|
return nCount; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// |
|
// Methods of IShaderInit lie below |
|
// |
|
//----------------------------------------------------------------------------- |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Gets at the render pass info for this pass... |
|
//----------------------------------------------------------------------------- |
|
inline StateSnapshot_t CShaderSystem::CurrentStateSnapshot() |
|
{ |
|
Assert( m_pRenderState ); |
|
Assert( m_nRenderPass < MAX_RENDER_PASSES ); |
|
Assert( m_nRenderPass < m_pRenderState->m_pSnapshots[m_nModulation].m_nPassCount ); |
|
return m_pRenderState->m_pSnapshots[m_nModulation].m_Snapshot[m_nRenderPass]; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Create debugging materials |
|
//----------------------------------------------------------------------------- |
|
void CShaderSystem::CreateDebugMaterials() |
|
{ |
|
if (m_pDebugMaterials[0]) |
|
return; |
|
|
|
KeyValues *pVMTKeyValues[MATERIAL_DEBUG_COUNT]; |
|
|
|
int i; |
|
for ( i = 0; i < MATERIAL_DEBUG_COUNT; ++i ) |
|
{ |
|
pVMTKeyValues[i] = new KeyValues( s_pDebugShaderName[i] ); |
|
} |
|
|
|
pVMTKeyValues[MATERIAL_DEBUG_DEPTH_DECAL]->SetInt( "$decal", 1 ); |
|
|
|
for ( i = 0; i < MATERIAL_DEBUG_COUNT; ++i ) |
|
{ |
|
char shaderName[64]; |
|
Q_snprintf( shaderName, sizeof( shaderName ), "___%s_%d.vmt", s_pDebugShaderName[i], i ); |
|
m_pDebugMaterials[i] = static_cast<IMaterialInternal*>(MaterialSystem()->CreateMaterial( shaderName, pVMTKeyValues[i] )); |
|
if( m_pDebugMaterials[i] ) |
|
m_pDebugMaterials[i] = m_pDebugMaterials[i]->GetRealTimeVersion(); |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Cleans up the debugging materials |
|
//----------------------------------------------------------------------------- |
|
void CShaderSystem::CleanUpDebugMaterials() |
|
{ |
|
if (m_pDebugMaterials[0]) |
|
{ |
|
for ( int i = 0; i < MATERIAL_DEBUG_COUNT; ++i ) |
|
{ |
|
m_pDebugMaterials[i]->DecrementReferenceCount(); |
|
if ( m_pDebugMaterials[i]->InMaterialPage() ) |
|
{ |
|
MaterialSystem()->RemoveMaterialSubRect( m_pDebugMaterials[i] ); |
|
} |
|
else |
|
{ |
|
MaterialSystem()->RemoveMaterial( m_pDebugMaterials[i] ); |
|
} |
|
m_pDebugMaterials[i] = NULL; |
|
} |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Deal with buffering of spew while doing shader draw so that we don't get |
|
// recursive spew during precache due to fonts not being loaded, etc. |
|
//----------------------------------------------------------------------------- |
|
CThreadFastMutex g_StgoredSpewMutex; |
|
void CShaderSystem::BufferSpew( SpewType_t spewType, const Color &c, const char *pMsg ) |
|
{ |
|
AUTO_LOCK( g_StgoredSpewMutex ); |
|
m_StoredSpew.PutInt( spewType ); |
|
m_StoredSpew.PutChar( c.r() ); |
|
m_StoredSpew.PutChar( c.g() ); |
|
m_StoredSpew.PutChar( c.b() ); |
|
m_StoredSpew.PutChar( c.a() ); |
|
m_StoredSpew.PutString( pMsg ); |
|
} |
|
|
|
void CShaderSystem::PrintBufferedSpew( void ) |
|
{ |
|
AUTO_LOCK( g_StgoredSpewMutex ); |
|
while ( m_StoredSpew.GetBytesRemaining() > 0 ) |
|
{ |
|
SpewType_t spewType = (SpewType_t)m_StoredSpew.GetInt(); |
|
|
|
unsigned char r, g, b, a; |
|
r = m_StoredSpew.GetChar(); |
|
g = m_StoredSpew.GetChar(); |
|
b = m_StoredSpew.GetChar(); |
|
a = m_StoredSpew.GetChar(); |
|
|
|
Color c( r, g, b, a ); |
|
|
|
int nLen = m_StoredSpew.PeekStringLength(); |
|
if ( nLen ) |
|
{ |
|
char *pBuf = (char*)_alloca( nLen ); |
|
m_StoredSpew.GetStringManualCharCount( pBuf, nLen ); |
|
ColorSpewMessage( spewType, &c, "%s", pBuf ); |
|
} |
|
else |
|
{ |
|
break; |
|
} |
|
} |
|
|
|
m_StoredSpew.Clear(); |
|
} |
|
|
|
static SpewRetval_t MySpewOutputFunc( SpewType_t spewType, char const *pMsg ) |
|
{ |
|
AUTO_LOCK( g_StgoredSpewMutex ); |
|
Color c = *GetSpewOutputColor(); |
|
s_ShaderSystem.BufferSpew( spewType, c, pMsg ); |
|
|
|
switch( spewType ) |
|
{ |
|
case SPEW_MESSAGE: |
|
case SPEW_WARNING: |
|
case SPEW_LOG: |
|
return SPEW_CONTINUE; |
|
|
|
case SPEW_ASSERT: |
|
case SPEW_ERROR: |
|
default: |
|
return SPEW_DEBUGGER; |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Deals with shader draw |
|
//----------------------------------------------------------------------------- |
|
void CShaderSystem::PrepForShaderDraw( IShader *pShader, |
|
IMaterialVar** ppParams, ShaderRenderState_t* pRenderState, int nModulation ) |
|
{ |
|
Assert( !m_pRenderState ); |
|
|
|
// 360 runs the console remotely, spew cannot cause the matsys to be reentrant |
|
// 360 sidesteps the other negative affect that *all* buffered spew redirects as warning text |
|
if ( IsPC() || !IsX360() ) |
|
{ |
|
Assert( !m_SaveSpewOutput ); |
|
m_SaveSpewOutput = GetSpewOutputFunc(); |
|
SpewOutputFunc( MySpewOutputFunc ); |
|
} |
|
|
|
m_pRenderState = pRenderState; |
|
m_nModulation = nModulation; |
|
m_nRenderPass = 0; |
|
} |
|
|
|
void CShaderSystem::DoneWithShaderDraw() |
|
{ |
|
if ( IsPC() || !IsX360() ) |
|
{ |
|
SpewOutputFunc( m_SaveSpewOutput ); |
|
PrintBufferedSpew(); |
|
m_SaveSpewOutput = NULL; |
|
} |
|
|
|
m_pRenderState = NULL; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Call the SHADER_PARAM_INIT block of the shaders |
|
//----------------------------------------------------------------------------- |
|
void CShaderSystem::InitShaderParameters( IShader *pShader, IMaterialVar **params, const char *pMaterialName ) |
|
{ |
|
// Let the derived class do its thing |
|
PrepForShaderDraw( pShader, params, 0, 0 ); |
|
pShader->InitShaderParams( params, pMaterialName ); |
|
DoneWithShaderDraw(); |
|
|
|
// Set up color + alpha defaults |
|
if (!params[COLOR]->IsDefined()) |
|
{ |
|
params[COLOR]->SetVecValue( 1.0f, 1.0f, 1.0f ); |
|
} |
|
|
|
if (!params[ALPHA]->IsDefined()) |
|
{ |
|
params[ALPHA]->SetFloatValue( 1.0f ); |
|
} |
|
|
|
// Initialize all shader params based on their type... |
|
int i; |
|
for ( i = pShader->GetNumParams(); --i >= 0; ) |
|
{ |
|
// Don't initialize parameters that are already set up |
|
if (params[i]->IsDefined()) |
|
continue; |
|
|
|
int type = pShader->GetParamType( i ); |
|
switch( type ) |
|
{ |
|
case SHADER_PARAM_TYPE_TEXTURE: |
|
// Do nothing; we'll be loading in a string later |
|
break; |
|
case SHADER_PARAM_TYPE_STRING: |
|
// Do nothing; we'll be loading in a string later |
|
break; |
|
case SHADER_PARAM_TYPE_MATERIAL: |
|
params[i]->SetMaterialValue( NULL ); |
|
break; |
|
case SHADER_PARAM_TYPE_BOOL: |
|
case SHADER_PARAM_TYPE_INTEGER: |
|
params[i]->SetIntValue( 0 ); |
|
break; |
|
case SHADER_PARAM_TYPE_COLOR: |
|
params[i]->SetVecValue( 1.0f, 1.0f, 1.0f ); |
|
break; |
|
case SHADER_PARAM_TYPE_VEC2: |
|
params[i]->SetVecValue( 0.0f, 0.0f ); |
|
break; |
|
case SHADER_PARAM_TYPE_VEC3: |
|
params[i]->SetVecValue( 0.0f, 0.0f, 0.0f ); |
|
break; |
|
case SHADER_PARAM_TYPE_VEC4: |
|
params[i]->SetVecValue( 0.0f, 0.0f, 0.0f, 0.0f ); |
|
break; |
|
case SHADER_PARAM_TYPE_FLOAT: |
|
params[i]->SetFloatValue( 0 ); |
|
break; |
|
case SHADER_PARAM_TYPE_FOURCC: |
|
params[i]->SetFourCCValue( 0, 0 ); |
|
break; |
|
case SHADER_PARAM_TYPE_MATRIX: |
|
{ |
|
VMatrix identity; |
|
MatrixSetIdentity( identity ); |
|
params[i]->SetMatrixValue( identity ); |
|
} |
|
break; |
|
case SHADER_PARAM_TYPE_MATRIX4X2: |
|
{ |
|
VMatrix identity; |
|
MatrixSetIdentity( identity ); |
|
params[i]->SetMatrixValue( identity ); |
|
} |
|
break; |
|
|
|
|
|
default: |
|
Assert(0); |
|
} |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Call the SHADER_INIT block of the shaders |
|
//----------------------------------------------------------------------------- |
|
void CShaderSystem::InitShaderInstance( IShader *pShader, IMaterialVar **params, const char *pMaterialName, const char *pTextureGroupName ) |
|
{ |
|
// Let the derived class do its thing |
|
PrepForShaderDraw( pShader, params, 0, 0 ); |
|
pShader->InitShaderInstance( params, ShaderSystem(), pMaterialName, pTextureGroupName ); |
|
DoneWithShaderDraw(); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Compute snapshots for all combinations of alpha + color modulation |
|
//----------------------------------------------------------------------------- |
|
void CShaderSystem::InitRenderStateFlags( ShaderRenderState_t* pRenderState, int numParams, IMaterialVar **params ) |
|
{ |
|
// Compute vertex format and flags |
|
pRenderState->m_Flags = 0; |
|
|
|
// Make sure the shader don't force these flags. . they are automatically computed. |
|
Assert( !( pRenderState->m_Flags & SHADER_OPACITY_TRANSLUCENT ) ); |
|
Assert( !( pRenderState->m_Flags & SHADER_OPACITY_ALPHATEST ) ); |
|
Assert( !( pRenderState->m_Flags & SHADER_OPACITY_OPAQUE ) ); |
|
|
|
// If we are in release mode, just go ahead and clear in case the above is screwed up. |
|
pRenderState->m_Flags &= ~SHADER_OPACITY_MASK; |
|
|
|
/* |
|
// HACK: Also kind of gross; turn off bump lightmapping for low-end |
|
if (g_config.bUseGraphics && !HardwareConfig()->SupportsVertexAndPixelShaders()) |
|
{ |
|
pRenderState->m_Flags &= ~SHADER_NEEDS_BUMPED_LIGHTMAPS; |
|
} |
|
*/ |
|
/* |
|
// HACK: more grossness!!! turn off bump lightmapping if we don't have a bumpmap |
|
// Shaders should specify SHADER_NEEDS_BUMPED_LIGHTMAPS if they might need a bumpmap, |
|
// and this'll take care of getting rid of it if it isn't there. |
|
if( pRenderState->m_Flags & SHADER_NEEDS_BUMPED_LIGHTMAPS ) |
|
{ |
|
pRenderState->m_Flags &= ~SHADER_NEEDS_BUMPED_LIGHTMAPS; |
|
for( int i = 0; i < numParams; i++ ) |
|
{ |
|
if( stricmp( params[i]->GetName(), "$bumpmap" ) == 0 ) |
|
{ |
|
if( params[i]->IsDefined() ) |
|
{ |
|
const char *blah = params[i]->GetStringValue(); |
|
pRenderState->m_Flags |= SHADER_NEEDS_BUMPED_LIGHTMAPS; |
|
break; |
|
} |
|
} |
|
} |
|
} |
|
*/ |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Computes flags from a particular snapshot |
|
//----------------------------------------------------------------------------- |
|
void CShaderSystem::ComputeRenderStateFlagsFromSnapshot( ShaderRenderState_t* pRenderState ) |
|
{ |
|
// When computing the flags, use the snapshot that has no alpha or color |
|
// modulation. When asking for translucency, we'll have to check for |
|
// alpha modulation in addition to checking the TRANSLUCENT flag. |
|
|
|
// I have to do it this way because I'm really wanting to treat alpha |
|
// modulation as a dynamic state, even though it's being used to compute |
|
// shadow state. I still want to use it to compute shadow state though |
|
// because it's somewhat complicated code that I'd rather precache. |
|
|
|
StateSnapshot_t snapshot = pRenderState->m_pSnapshots[0].m_Snapshot[0]; |
|
|
|
// Automatically compute if the snapshot is transparent or not |
|
if ( g_pShaderAPI->IsTranslucent( snapshot ) ) |
|
{ |
|
pRenderState->m_Flags |= SHADER_OPACITY_TRANSLUCENT; |
|
} |
|
else |
|
{ |
|
if ( g_pShaderAPI->IsAlphaTested( snapshot ) ) |
|
{ |
|
pRenderState->m_Flags |= SHADER_OPACITY_ALPHATEST; |
|
} |
|
else |
|
{ |
|
pRenderState->m_Flags |= SHADER_OPACITY_OPAQUE; |
|
} |
|
} |
|
|
|
#ifdef _DEBUG |
|
if( pRenderState->m_Flags & SHADER_OPACITY_TRANSLUCENT ) |
|
{ |
|
Assert( !( pRenderState->m_Flags & SHADER_OPACITY_ALPHATEST ) ); |
|
Assert( !( pRenderState->m_Flags & SHADER_OPACITY_OPAQUE ) ); |
|
} |
|
if( pRenderState->m_Flags & SHADER_OPACITY_ALPHATEST ) |
|
{ |
|
Assert( !( pRenderState->m_Flags & SHADER_OPACITY_TRANSLUCENT ) ); |
|
Assert( !( pRenderState->m_Flags & SHADER_OPACITY_OPAQUE ) ); |
|
} |
|
if( pRenderState->m_Flags & SHADER_OPACITY_OPAQUE ) |
|
{ |
|
Assert( !( pRenderState->m_Flags & SHADER_OPACITY_ALPHATEST ) ); |
|
Assert( !( pRenderState->m_Flags & SHADER_OPACITY_TRANSLUCENT ) ); |
|
} |
|
#endif |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Initializes state snapshots |
|
//----------------------------------------------------------------------------- |
|
#ifdef _DEBUG |
|
#pragma warning (disable:4189) |
|
#endif |
|
|
|
int CShaderSystem::GetModulationSnapshotCount( IMaterialVar **params ) |
|
{ |
|
int nSnapshotCount = SnapshotTypeCount(); |
|
if ( !MaterialSystem()->CanUseEditorMaterials() ) |
|
{ |
|
if( !IsFlag2Set( params, MATERIAL_VAR2_NEEDS_BAKED_LIGHTING_SNAPSHOTS ) ) |
|
{ |
|
nSnapshotCount /= 2; |
|
} |
|
} |
|
|
|
return nSnapshotCount; |
|
} |
|
|
|
void CShaderSystem::InitStateSnapshots( IShader *pShader, IMaterialVar **params, ShaderRenderState_t* pRenderState ) |
|
{ |
|
#ifdef _DEBUG |
|
if ( IsFlagSet( params, MATERIAL_VAR_DEBUG ) ) |
|
{ |
|
// Putcher breakpoint here to catch the rendering of a material |
|
// marked for debugging ($debug = 1 in a .vmt file) shadow state version |
|
int x = 0; |
|
} |
|
#endif |
|
|
|
// Store off the current alpha + color modulations |
|
float alpha; |
|
float color[3]; |
|
params[COLOR]->GetVecValue( color, 3 ); |
|
alpha = params[ALPHA]->GetFloatValue( ); |
|
bool bBakedLighting = IsFlag2Set( params, MATERIAL_VAR2_USE_FIXED_FUNCTION_BAKED_LIGHTING ); |
|
bool bFlashlight = IsFlag2Set( params, MATERIAL_VAR2_USE_FLASHLIGHT ); |
|
bool bEditor = IsFlag2Set( params, MATERIAL_VAR2_USE_EDITOR ); |
|
// bool bSupportsFlashlight = IsFlag2Set( params, MATERIAL_VAR2_SUPPORTS_FLASHLIGHT ); |
|
float white[3] = { 1, 1, 1 }; |
|
float grey[3] = { .5, .5, .5 }; |
|
|
|
int nSnapshotCount = GetModulationSnapshotCount( params ); |
|
|
|
// If the current mod does not use the flashlight, skip all flashlight snapshots (saves a ton of memory) |
|
bool bModUsesFlashlight = ( mat_supportflashlight.GetInt() != 0 ); |
|
|
|
for (int i = 0; i < nSnapshotCount; ++i) |
|
{ |
|
if ( ( i & SHADER_USING_FLASHLIGHT ) && |
|
!bModUsesFlashlight ) |
|
{ |
|
pRenderState->m_pSnapshots[i].m_nPassCount = 0; |
|
continue; |
|
} |
|
|
|
// Set modulation to force particular code paths |
|
if (i & SHADER_USING_COLOR_MODULATION) |
|
{ |
|
params[COLOR]->SetVecValue( grey, 3 ); |
|
} |
|
else |
|
{ |
|
params[COLOR]->SetVecValue( white, 3 ); |
|
} |
|
|
|
if (i & SHADER_USING_ALPHA_MODULATION) |
|
{ |
|
params[ALPHA]->SetFloatValue( grey[0] ); |
|
} |
|
else |
|
{ |
|
params[ALPHA]->SetFloatValue( white[0] ); |
|
} |
|
|
|
if ( i & SHADER_USING_FLASHLIGHT ) |
|
{ |
|
// if ( !bSupportsFlashlight ) |
|
// { |
|
// pRenderState->m_pSnapshots[i].m_nPassCount = 0; |
|
// continue; |
|
// } |
|
SET_FLAGS2( MATERIAL_VAR2_USE_FLASHLIGHT ); |
|
} |
|
else |
|
{ |
|
CLEAR_FLAGS2( MATERIAL_VAR2_USE_FLASHLIGHT ); |
|
} |
|
|
|
if ( i & SHADER_USING_EDITOR ) |
|
{ |
|
SET_FLAGS2( MATERIAL_VAR2_USE_EDITOR ); |
|
} |
|
else |
|
{ |
|
CLEAR_FLAGS2( MATERIAL_VAR2_USE_EDITOR ); |
|
} |
|
|
|
if ( i & SHADER_USING_FIXED_FUNCTION_BAKED_LIGHTING ) |
|
{ |
|
SET_FLAGS2( MATERIAL_VAR2_USE_FIXED_FUNCTION_BAKED_LIGHTING ); |
|
} |
|
else |
|
{ |
|
CLEAR_FLAGS2( MATERIAL_VAR2_USE_FIXED_FUNCTION_BAKED_LIGHTING ); |
|
} |
|
|
|
PrepForShaderDraw( pShader, params, pRenderState, i ); |
|
|
|
// Now snapshot how we're going to draw |
|
pRenderState->m_pSnapshots[i].m_nPassCount = 0; |
|
pShader->DrawElements( params, i, g_pShaderShadow, 0, VERTEX_COMPRESSION_NONE, &(pRenderState->m_pSnapshots[i].m_pContextData[0] ) ); |
|
DoneWithShaderDraw(); |
|
} |
|
|
|
// Restore alpha + color modulation |
|
params[COLOR]->SetVecValue( color, 3 ); |
|
params[ALPHA]->SetFloatValue( alpha ); |
|
if( bBakedLighting ) |
|
{ |
|
SET_FLAGS2( MATERIAL_VAR2_USE_FIXED_FUNCTION_BAKED_LIGHTING ); |
|
} |
|
else |
|
{ |
|
CLEAR_FLAGS2( MATERIAL_VAR2_USE_FIXED_FUNCTION_BAKED_LIGHTING ); |
|
} |
|
|
|
if( bEditor ) |
|
{ |
|
SET_FLAGS2( MATERIAL_VAR2_USE_EDITOR ); |
|
} |
|
else |
|
{ |
|
CLEAR_FLAGS2( MATERIAL_VAR2_USE_EDITOR ); |
|
} |
|
|
|
if( bFlashlight ) |
|
{ |
|
SET_FLAGS2( MATERIAL_VAR2_USE_FLASHLIGHT ); |
|
} |
|
else |
|
{ |
|
CLEAR_FLAGS2( MATERIAL_VAR2_USE_FLASHLIGHT ); |
|
} |
|
} |
|
|
|
#ifdef _DEBUG |
|
#pragma warning (default:4189) |
|
#endif |
|
|
|
//----------------------------------------------------------------------------- |
|
// Helper to count texture coordinates |
|
//----------------------------------------------------------------------------- |
|
static int NumTextureCoordinates( VertexFormat_t vertexFormat ) |
|
{ |
|
// FIXME: this is a duplicate of the function in meshdx8.cpp |
|
int nTexCoordCount = 0; |
|
for ( int i = 0; i < VERTEX_MAX_TEXTURE_COORDINATES; ++i ) |
|
{ |
|
if ( TexCoordSize( i, vertexFormat ) == 0 ) |
|
continue; |
|
++nTexCoordCount; |
|
} |
|
return nTexCoordCount; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Displays the vertex format |
|
//----------------------------------------------------------------------------- |
|
static void OutputVertexFormat( VertexFormat_t format ) |
|
{ |
|
// FIXME: this is a duplicate of the function in meshdx8.cpp |
|
VertexCompressionType_t compressionType = CompressionType( format ); |
|
|
|
if( format & VERTEX_POSITION ) |
|
{ |
|
Warning( "VERTEX_POSITION|" ); |
|
} |
|
if( format & VERTEX_NORMAL ) |
|
{ |
|
if ( compressionType == VERTEX_COMPRESSION_ON ) |
|
Warning( "VERTEX_NORMAL[COMPRESSED]|" ); |
|
else |
|
Warning( "VERTEX_NORMAL|" ); |
|
} |
|
if( format & VERTEX_COLOR ) |
|
{ |
|
Warning( "VERTEX_COLOR|" ); |
|
} |
|
if( format & VERTEX_SPECULAR ) |
|
{ |
|
Warning( "VERTEX_SPECULAR|" ); |
|
} |
|
if( format & VERTEX_TANGENT_S ) |
|
{ |
|
Warning( "VERTEX_TANGENT_S|" ); |
|
} |
|
if( format & VERTEX_TANGENT_T ) |
|
{ |
|
Warning( "VERTEX_TANGENT_T|" ); |
|
} |
|
if( format & VERTEX_BONE_INDEX ) |
|
{ |
|
Warning( "VERTEX_BONE_INDEX|" ); |
|
} |
|
if( format & VERTEX_FORMAT_VERTEX_SHADER ) |
|
{ |
|
Warning( "VERTEX_FORMAT_VERTEX_SHADER|" ); |
|
} |
|
Warning( "\nBone weights: %d\n", NumBoneWeights( format ) ); |
|
Warning( "user data size: %d (%s)\n", UserDataSize( format ), |
|
( CompressionType( format ) == VERTEX_COMPRESSION_ON ? "compressed" : "uncompressed" ) ); |
|
Warning( "num tex coords: %d\n", NumTextureCoordinates( format ) ); |
|
// NOTE: This doesn't print texcoord sizes. |
|
} |
|
|
|
|
|
#ifdef _DEBUG |
|
static bool IsVertexFormatSubsetOfVertexformat( VertexFormat_t subset, VertexFormat_t superset ) |
|
{ |
|
subset &= ~VERTEX_FORMAT_USE_EXACT_FORMAT; |
|
superset &= ~VERTEX_FORMAT_USE_EXACT_FORMAT; |
|
|
|
// Test the flags |
|
if( VertexFlags( subset ) & VertexFlags( ~superset ) ) |
|
return false; |
|
|
|
// Test bone weights |
|
if( NumBoneWeights( subset ) > NumBoneWeights( superset ) ) |
|
return false; |
|
|
|
// Test user data size |
|
if( UserDataSize( subset ) > UserDataSize( superset ) ) |
|
return false; |
|
|
|
// Test the texcoord dimensions |
|
for( int i = 0; i < VERTEX_MAX_TEXTURE_COORDINATES; i++ ) |
|
{ |
|
if( TexCoordSize( i, subset ) > TexCoordSize( i, superset ) ) |
|
return false; |
|
} |
|
|
|
return true; |
|
} |
|
#endif |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Adds state snapshots to the render list |
|
//----------------------------------------------------------------------------- |
|
static void AddSnapshotsToList( RenderPassList_t *pPassList, int &nSnapshotID, StateSnapshot_t *pSnapshots ) |
|
{ |
|
int nNumPassSnapshots = pPassList->m_nPassCount; |
|
for( int i = 0; i < nNumPassSnapshots; ++i ) |
|
{ |
|
pSnapshots[nSnapshotID] = pPassList->m_Snapshot[i]; |
|
nSnapshotID++; |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Computes vertex format + usage from a particular snapshot |
|
//----------------------------------------------------------------------------- |
|
bool CShaderSystem::ComputeVertexFormatFromSnapshot( IMaterialVar **params, ShaderRenderState_t* pRenderState ) |
|
{ |
|
// When computing the usage, use the snapshot that has no alpha or color |
|
// modulation. We need the usage + format to be the same for all |
|
// combinations of alpha + color modulation, though, or we are asking for |
|
// trouble. |
|
int nModulationSnapshotCount = GetModulationSnapshotCount( params ); |
|
int numSnapshots = pRenderState->m_pSnapshots[0].m_nPassCount; |
|
if (nModulationSnapshotCount >= SHADER_USING_FLASHLIGHT) |
|
{ |
|
numSnapshots += pRenderState->m_pSnapshots[SHADER_USING_FLASHLIGHT].m_nPassCount; |
|
} |
|
if ( MaterialSystem()->CanUseEditorMaterials() ) |
|
{ |
|
numSnapshots += pRenderState->m_pSnapshots[SHADER_USING_EDITOR].m_nPassCount; |
|
} |
|
|
|
StateSnapshot_t* pSnapshots = (StateSnapshot_t*)stackalloc( |
|
numSnapshots * sizeof(StateSnapshot_t) ); |
|
|
|
int snapshotID = 0; |
|
AddSnapshotsToList( &pRenderState->m_pSnapshots[0], snapshotID, pSnapshots ); |
|
if (nModulationSnapshotCount >= SHADER_USING_FLASHLIGHT) |
|
{ |
|
AddSnapshotsToList( &pRenderState->m_pSnapshots[SHADER_USING_FLASHLIGHT], snapshotID, pSnapshots ); |
|
} |
|
if ( MaterialSystem()->CanUseEditorMaterials() ) |
|
{ |
|
AddSnapshotsToList( &pRenderState->m_pSnapshots[SHADER_USING_EDITOR], snapshotID, pSnapshots ); |
|
} |
|
|
|
Assert( snapshotID == numSnapshots ); |
|
|
|
pRenderState->m_VertexUsage = g_pShaderAPI->ComputeVertexUsage( numSnapshots, pSnapshots ); |
|
pRenderState->m_MorphFormat = g_pShaderAPI->ComputeMorphFormat( numSnapshots, pSnapshots ); |
|
|
|
#ifdef _DEBUG |
|
// Make sure all modulation combinations match vertex usage |
|
for ( int mod = 1; mod < nModulationSnapshotCount; ++mod ) |
|
{ |
|
int numSnapshotsTest = pRenderState->m_pSnapshots[mod].m_nPassCount; |
|
StateSnapshot_t* pSnapshotsTest = (StateSnapshot_t*)_alloca( |
|
numSnapshotsTest * sizeof(StateSnapshot_t) ); |
|
|
|
for (int i = 0; i < numSnapshotsTest; ++i) |
|
{ |
|
pSnapshotsTest[i] = pRenderState->m_pSnapshots[mod].m_Snapshot[i]; |
|
} |
|
|
|
VertexFormat_t usageTest = g_pShaderAPI->ComputeVertexUsage( numSnapshotsTest, pSnapshotsTest ); |
|
Assert( IsVertexFormatSubsetOfVertexformat( usageTest, pRenderState->m_VertexUsage ) ); |
|
} |
|
#endif |
|
|
|
if ( IsPC() ) |
|
{ |
|
pRenderState->m_VertexFormat = g_pShaderAPI->ComputeVertexFormat( numSnapshots, pSnapshots ); |
|
} |
|
else |
|
{ |
|
pRenderState->m_VertexFormat = pRenderState->m_VertexUsage; |
|
} |
|
|
|
return true; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// go through each param and make sure it is the right type, load textures, |
|
// compute state snapshots and vertex types, etc. |
|
//----------------------------------------------------------------------------- |
|
bool CShaderSystem::InitRenderState( IShader *pShader, int numParams, IMaterialVar **params, ShaderRenderState_t* pRenderState, char const* pMaterialName ) |
|
{ |
|
Assert( !m_pRenderState ); |
|
|
|
// Initialize render state flags |
|
InitRenderStateFlags( pRenderState, numParams, params ); |
|
|
|
// Compute state snapshots for each combination of alpha + color |
|
InitStateSnapshots( pShader, params, pRenderState ); |
|
|
|
// Compute other infomation for the render state based on snapshots |
|
if (pRenderState->m_pSnapshots[0].m_nPassCount == 0) |
|
{ |
|
Warning( "Material \"%s\":\n No render states in shader \"%s\"\n", pMaterialName, pShader->GetName() ); |
|
return false; |
|
} |
|
|
|
// Set a couple additional flags based on the render state |
|
ComputeRenderStateFlagsFromSnapshot( pRenderState ); |
|
|
|
// Compute the vertex format + usage from the snapshot |
|
if ( !ComputeVertexFormatFromSnapshot( params, pRenderState ) ) |
|
{ |
|
// warn.. return a null render state... |
|
Warning("Material \"%s\":\n Shader \"%s\" can't be used with models!\n", pMaterialName, pShader->GetName() ); |
|
CleanupRenderState( pRenderState ); |
|
return false; |
|
} |
|
return true; |
|
} |
|
|
|
// When you're done with the shader, be sure to call this to clean up |
|
void CShaderSystem::CleanupRenderState( ShaderRenderState_t* pRenderState ) |
|
{ |
|
if (pRenderState) |
|
{ |
|
int nSnapshotCount = SnapshotTypeCount(); |
|
// kill context data |
|
// Indicate no passes for any of the snapshot lists |
|
RenderPassList_t *pTemp = pRenderState->m_pSnapshots; |
|
for(int i = 0; i < nSnapshotCount; i++ ) |
|
{ |
|
for(int j = 0 ; j < pRenderState->m_pSnapshots[i].m_nPassCount; j++ ) |
|
if ( pTemp[i].m_pContextData[j] ) |
|
{ |
|
delete pTemp[i].m_pContextData[j]; |
|
pTemp[i].m_pContextData[j] = NULL; |
|
} |
|
pRenderState->m_pSnapshots[i].m_nPassCount = 0; |
|
} |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Does the rendering! |
|
//----------------------------------------------------------------------------- |
|
void CShaderSystem::DrawElements( IShader *pShader, IMaterialVar **params, |
|
ShaderRenderState_t* pRenderState, |
|
VertexCompressionType_t vertexCompression, |
|
uint32 nMaterialVarChangeTimeStamp ) |
|
{ |
|
VPROF("CShaderSystem::DrawElements"); |
|
|
|
g_pShaderAPI->InvalidateDelayedShaderConstants(); |
|
// Compute modulation... |
|
int mod = pShader->ComputeModulationFlags( params, g_pShaderAPI ); |
|
|
|
// No snapshots? do nothing. |
|
if ( pRenderState->m_pSnapshots[mod].m_nPassCount == 0 ) |
|
return; |
|
|
|
// If we're rendering a model, gotta have skinning matrices |
|
int materialVarFlags = params[FLAGS]->GetIntValue(); |
|
if ( (( materialVarFlags & MATERIAL_VAR_MODEL ) != 0) || |
|
( IsFlag2Set( params, MATERIAL_VAR2_SUPPORTS_HW_SKINNING ) && ( g_pShaderAPI->GetCurrentNumBones() > 0 )) ) |
|
{ |
|
g_pShaderAPI->SetSkinningMatrices( ); |
|
} |
|
|
|
// FIXME: need one conditional that we calculate once a frame for debug or not with everything debug under that. |
|
#ifndef DX_TO_GL_ABSTRACTION |
|
if ( ( ( g_config.bMeasureFillRate || g_config.bVisualizeFillRate ) && |
|
( ( materialVarFlags & MATERIAL_VAR_USE_IN_FILLRATE_MODE ) == 0 ) ) ) |
|
{ |
|
DrawMeasureFillRate( pRenderState, mod, vertexCompression ); |
|
} |
|
else |
|
#endif |
|
if( ( g_config.bShowNormalMap || g_config.nShowMipLevels == 2 ) && |
|
( IsFlag2Set( params, MATERIAL_VAR2_LIGHTING_BUMPED_LIGHTMAP ) || |
|
IsFlag2Set( params, MATERIAL_VAR2_DIFFUSE_BUMPMAPPED_MODEL ) ) ) |
|
{ |
|
DrawNormalMap( pShader, params, vertexCompression ); |
|
} |
|
#if defined(DEBUG_DEPTH) |
|
else if ( mat_debugdepth.GetInt() && ((materialVarFlags & MATERIAL_VAR_NO_DEBUG_OVERRIDE) == 0) ) |
|
{ |
|
int nIndex = 0; |
|
if ( IsFlagSet( params, MATERIAL_VAR_DECAL ) ) |
|
{ |
|
nIndex |= 0x1; |
|
} |
|
IMaterialInternal *pDebugMaterial = m_pDebugMaterials[ MATERIAL_DEBUG_DEPTH + nIndex ]; |
|
if ( !g_pShaderAPI->IsDepthWriteEnabled( pRenderState->m_Snapshots[mod].m_Snapshot[0] ) ) |
|
{ |
|
pDebugMaterial = m_pDebugMaterials[MATERIAL_DEBUG_WIREFRAME]; |
|
} |
|
|
|
DrawUsingMaterial( pDebugMaterial, vertexCompression ); |
|
} |
|
#endif |
|
else |
|
{ |
|
g_pShaderAPI->SetDefaultState(); |
|
|
|
// If we're rendering flat, turn on flat mode... |
|
if (materialVarFlags & MATERIAL_VAR_FLAT) |
|
{ |
|
g_pShaderAPI->ShadeMode( SHADER_FLAT ); |
|
} |
|
|
|
PrepForShaderDraw( pShader, params, pRenderState, mod ); |
|
g_pShaderAPI->BeginPass( CurrentStateSnapshot() ); |
|
|
|
CBasePerMaterialContextData ** pContextDataPtr = |
|
&( m_pRenderState->m_pSnapshots[m_nModulation].m_pContextData[m_nRenderPass] ); |
|
|
|
if ( *pContextDataPtr && ( (*pContextDataPtr)->m_nVarChangeID != nMaterialVarChangeTimeStamp ) ) |
|
{ |
|
(*pContextDataPtr)->m_bMaterialVarsChanged = true; |
|
(*pContextDataPtr)->m_nVarChangeID = nMaterialVarChangeTimeStamp; |
|
} |
|
|
|
pShader->DrawElements( |
|
params, mod, 0, g_pShaderAPI, vertexCompression, |
|
&( m_pRenderState->m_pSnapshots[m_nModulation].m_pContextData[m_nRenderPass] ) ); |
|
DoneWithShaderDraw(); |
|
} |
|
|
|
MaterialSystem()->ForceDepthFuncEquals( false ); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Are we using graphics? |
|
//----------------------------------------------------------------------------- |
|
bool CShaderSystem::IsUsingGraphics() const |
|
{ |
|
// YWB Hack if running with -noshaderapi/-makereslists this forces materials to "precache" which means they will resolve their .vtf files for |
|
// things like normal/height/dudv maps... |
|
if ( m_bForceUsingGraphicsReturnTrue ) |
|
return true; |
|
|
|
return g_pShaderDevice->IsUsingGraphics(); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Are we using the editor materials? |
|
//----------------------------------------------------------------------------- |
|
bool CShaderSystem::CanUseEditorMaterials() const |
|
{ |
|
return MaterialSystem()->CanUseEditorMaterials(); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Takes a snapshot |
|
//----------------------------------------------------------------------------- |
|
void CShaderSystem::TakeSnapshot( ) |
|
{ |
|
Assert( m_pRenderState ); |
|
Assert( m_nModulation < SnapshotTypeCount() ); |
|
|
|
if( g_pHardwareConfig->SupportsPixelShaders_2_b() ) |
|
{ |
|
//enable linear->gamma srgb conversion lookup texture |
|
g_pShaderShadow->EnableTexture( SHADER_SAMPLER15, true ); |
|
g_pShaderShadow->EnableSRGBRead( SHADER_SAMPLER15, true ); |
|
} |
|
|
|
RenderPassList_t& snapshotList = m_pRenderState->m_pSnapshots[m_nModulation]; |
|
|
|
// Take a snapshot... |
|
snapshotList.m_Snapshot[snapshotList.m_nPassCount] = g_pShaderAPI->TakeSnapshot(); |
|
++snapshotList.m_nPassCount; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Draws a snapshot |
|
//----------------------------------------------------------------------------- |
|
void CShaderSystem::DrawSnapshot( bool bMakeActualDrawCall ) |
|
{ |
|
Assert( m_pRenderState ); |
|
RenderPassList_t& snapshotList = m_pRenderState->m_pSnapshots[m_nModulation]; |
|
|
|
int nPassCount = snapshotList.m_nPassCount; |
|
Assert( m_nRenderPass < nPassCount ); |
|
|
|
if ( bMakeActualDrawCall ) |
|
{ |
|
g_pShaderAPI->RenderPass( m_nRenderPass, nPassCount ); |
|
} |
|
|
|
g_pShaderAPI->InvalidateDelayedShaderConstants(); |
|
if (++m_nRenderPass < nPassCount) |
|
{ |
|
g_pShaderAPI->BeginPass( CurrentStateSnapshot() ); |
|
} |
|
} |
|
|
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// |
|
// Debugging material methods below |
|
// |
|
//----------------------------------------------------------------------------- |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Draws a using a particular material.. |
|
//----------------------------------------------------------------------------- |
|
void CShaderSystem::DrawUsingMaterial( IMaterialInternal *pMaterial, VertexCompressionType_t vertexCompression ) |
|
{ |
|
ShaderRenderState_t *pRenderState = pMaterial->GetRenderState(); |
|
g_pShaderAPI->SetDefaultState( ); |
|
|
|
IShader *pShader = pMaterial->GetShader(); |
|
int nMod = pShader->ComputeModulationFlags( pMaterial->GetShaderParams(), g_pShaderAPI ); |
|
PrepForShaderDraw( pShader, pMaterial->GetShaderParams(), pRenderState, nMod ); |
|
g_pShaderAPI->BeginPass( pRenderState->m_pSnapshots[nMod].m_Snapshot[0] ); |
|
pShader->DrawElements( pMaterial->GetShaderParams(), nMod, 0, g_pShaderAPI, vertexCompression, |
|
&( pRenderState->m_pSnapshots[nMod].m_pContextData[0] ) ); |
|
DoneWithShaderDraw( ); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Copies material vars |
|
//----------------------------------------------------------------------------- |
|
void CShaderSystem::CopyMaterialVarToDebugShader( IMaterialInternal *pDebugMaterial, IShader *pShader, IMaterialVar **ppParams, const char *pSrcVarName, const char *pDstVarName ) |
|
{ |
|
bool bFound; |
|
IMaterialVar *pMaterialVar = pDebugMaterial->FindVar( pDstVarName ? pDstVarName : pSrcVarName, &bFound ); |
|
Assert( bFound ); |
|
|
|
for( int i = pShader->GetNumParams(); --i >= 0; ) |
|
{ |
|
if( !Q_stricmp( ppParams[i]->GetName( ), pSrcVarName ) ) |
|
{ |
|
pMaterialVar->CopyFrom( ppParams[i] ); |
|
return; |
|
} |
|
} |
|
|
|
pMaterialVar->SetUndefined(); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Draws the puppy in fill rate mode... |
|
//----------------------------------------------------------------------------- |
|
void CShaderSystem::DrawMeasureFillRate( ShaderRenderState_t* pRenderState, int mod, VertexCompressionType_t vertexCompression ) |
|
{ |
|
int nPassCount = pRenderState->m_pSnapshots[mod].m_nPassCount; |
|
|
|
// We require the use of a vertex shader rather than fixed function transforms |
|
Assert( (VertexFlags(pRenderState->m_VertexFormat) & VERTEX_FORMAT_VERTEX_SHADER) != 0 ); |
|
|
|
IMaterialInternal *pMaterial = m_pDebugMaterials[ MATERIAL_FILL_RATE ]; |
|
|
|
bool bFound; |
|
IMaterialVar *pMaterialVar = pMaterial->FindVar( "$passcount", &bFound ); |
|
pMaterialVar->SetIntValue( nPassCount ); |
|
DrawUsingMaterial( pMaterial, vertexCompression ); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Draws normalmaps |
|
//----------------------------------------------------------------------------- |
|
void CShaderSystem::DrawNormalMap( IShader *pShader, IMaterialVar **ppParams, VertexCompressionType_t vertexCompression ) |
|
{ |
|
IMaterialInternal *pDebugMaterial = m_pDebugMaterials[MATERIAL_DEBUG_NORMALMAP]; |
|
|
|
if( !g_config.m_bFastNoBump ) |
|
{ |
|
CopyMaterialVarToDebugShader( pDebugMaterial, pShader, ppParams, "$bumpmap" ); |
|
CopyMaterialVarToDebugShader( pDebugMaterial, pShader, ppParams, "$bumpframe" ); |
|
CopyMaterialVarToDebugShader( pDebugMaterial, pShader, ppParams, "$bumptransform" ); |
|
} |
|
else |
|
{ |
|
bool bFound; |
|
IMaterialVar *pMaterialVar = pDebugMaterial->FindVar( "$bumpmap", &bFound ); |
|
Assert( bFound ); |
|
|
|
pMaterialVar->SetUndefined(); |
|
} |
|
|
|
DrawUsingMaterial( pDebugMaterial, vertexCompression ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Draws envmapmask |
|
//----------------------------------------------------------------------------- |
|
bool CShaderSystem::DrawEnvmapMask( IShader *pShader, IMaterialVar **ppParams, |
|
ShaderRenderState_t* pRenderState, VertexCompressionType_t vertexCompression ) |
|
{ |
|
// FIXME! Make this work with fixed function. |
|
int vertexFormat = pRenderState->m_VertexFormat; |
|
bool bUsesVertexShader = (VertexFlags(vertexFormat) & VERTEX_FORMAT_VERTEX_SHADER) != 0; |
|
if( !bUsesVertexShader ) |
|
{ |
|
Assert( 0 ); |
|
return false; |
|
} |
|
IMaterialInternal *pDebugMaterial = m_pDebugMaterials[ MATERIAL_DEBUG_ENVMAPMASK ]; |
|
|
|
bool bFound; |
|
IMaterialVar *pMaterialVar = pDebugMaterial->FindVar( "$showalpha", &bFound ); |
|
Assert( bFound ); |
|
|
|
if( IsFlagSet( ppParams, MATERIAL_VAR_NORMALMAPALPHAENVMAPMASK ) ) |
|
{ |
|
// $bumpmap |
|
CopyMaterialVarToDebugShader( pDebugMaterial, pShader, ppParams, "$bumpmap", "$basetexture" ); |
|
CopyMaterialVarToDebugShader( pDebugMaterial, pShader, ppParams, "$bumpframe", "$frame" ); |
|
CopyMaterialVarToDebugShader( pDebugMaterial, pShader, ppParams, "$bumptransform", "$basetexturetransform" ); |
|
pMaterialVar->SetIntValue( 1 ); |
|
} |
|
else if( IsFlagSet( ppParams, MATERIAL_VAR_BASEALPHAENVMAPMASK ) ) |
|
{ |
|
// $basealphaenvmapmask |
|
CopyMaterialVarToDebugShader( pDebugMaterial, pShader, ppParams, "$basetexture" ); |
|
CopyMaterialVarToDebugShader( pDebugMaterial, pShader, ppParams, "$frame" ); |
|
CopyMaterialVarToDebugShader( pDebugMaterial, pShader, ppParams, "$basetexturetransform" ); |
|
pMaterialVar->SetIntValue( 1 ); |
|
} |
|
else |
|
{ |
|
// $envmapmask |
|
CopyMaterialVarToDebugShader( pDebugMaterial, pShader, ppParams, "$envmapmask", "$basetexture" ); |
|
CopyMaterialVarToDebugShader( pDebugMaterial, pShader, ppParams, "$envmapmaskframe", "$frame" ); |
|
CopyMaterialVarToDebugShader( pDebugMaterial, pShader, ppParams, "$envmapmasktransform", "$basetexturetransform" ); |
|
pMaterialVar->SetIntValue( 0 ); |
|
} |
|
|
|
if( pDebugMaterial->FindVar( "$basetexture", NULL )->IsTexture() ) |
|
{ |
|
DrawUsingMaterial( pDebugMaterial, vertexCompression ); |
|
return true; |
|
} |
|
else |
|
{ |
|
return false; |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// |
|
// Methods of IShaderSystem lie below |
|
// |
|
//----------------------------------------------------------------------------- |
|
|
|
|
|
ShaderAPITextureHandle_t CShaderSystem::GetShaderAPITextureBindHandle( ITexture *pTexture, int nFrame, int nTextureChannel ) |
|
{ |
|
Assert( !IsTextureInternalEnvCubemap( static_cast<ITextureInternal*>(pTexture) ) ); |
|
|
|
// Bind away baby |
|
if( pTexture ) |
|
{ |
|
// This is ugly. Basically, this is yet another way that textures can be bound. They don't get bound here, |
|
// but the return is only used to bind them for semistatic command buffer building, which doesn't go through |
|
// CTexture::Bind for whatever reason. So let's request the mipmaps here. If you run into this, in a situation |
|
// where we shouldn't be doing the request, we could relocate this code to the appropriate callsites instead. |
|
ITextureInternal* pTex = assert_cast< ITextureInternal* >( pTexture ); |
|
TextureManager()->RequestAllMipmaps( pTex ); |
|
|
|
return pTex->GetTextureHandle( nFrame, nTextureChannel ); |
|
} |
|
else |
|
return INVALID_SHADERAPI_TEXTURE_HANDLE; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Binds a texture |
|
//----------------------------------------------------------------------------- |
|
void CShaderSystem::BindTexture( Sampler_t sampler1, ITexture *pTexture, int nFrame /* = 0 */ ) |
|
{ |
|
// The call to IMaterialVar::GetTextureValue should have converted this to a real thing |
|
Assert( !IsTextureInternalEnvCubemap( static_cast<ITextureInternal*>(pTexture) ) ); |
|
|
|
// Bind away baby |
|
if( pTexture ) |
|
{ |
|
static_cast<ITextureInternal*>(pTexture)->Bind( sampler1, nFrame ); |
|
} |
|
} |
|
|
|
|
|
void CShaderSystem::BindTexture( Sampler_t sampler1, Sampler_t sampler2, ITexture *pTexture, int nFrame /* = 0 */ ) |
|
{ |
|
// The call to IMaterialVar::GetTextureValue should have converted this to a real thing |
|
Assert( !IsTextureInternalEnvCubemap( static_cast<ITextureInternal*>(pTexture) ) ); |
|
|
|
// Bind away baby |
|
if( pTexture ) |
|
{ |
|
if ( sampler2 == Sampler_t(-1) ) |
|
{ |
|
static_cast<ITextureInternal*>(pTexture)->Bind( sampler1, nFrame ); |
|
} |
|
else |
|
{ |
|
static_cast<ITextureInternal*>(pTexture)->Bind( sampler1, nFrame, sampler2 ); |
|
} |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// |
|
// Methods of IShaderInit lie below |
|
// |
|
//----------------------------------------------------------------------------- |
|
|
|
//----------------------------------------------------------------------------- |
|
// Loads a texture |
|
//----------------------------------------------------------------------------- |
|
void CShaderSystem::LoadTexture( IMaterialVar *pTextureVar, const char *pTextureGroupName, int nAdditionalCreationFlags /* = 0 */ ) |
|
{ |
|
if (pTextureVar->GetType() != MATERIAL_VAR_TYPE_STRING) |
|
{ |
|
// This here will cause 'UNDEFINED' material vars |
|
if (pTextureVar->GetType() != MATERIAL_VAR_TYPE_TEXTURE) |
|
{ |
|
pTextureVar->SetTextureValue( TextureManager()->ErrorTexture() ); |
|
} |
|
return; |
|
} |
|
|
|
// In this case, we have to convert the string into a texture value |
|
const char *pName = pTextureVar->GetStringValue(); |
|
|
|
// Fix cases where people stupidly put a slash at the front of the vtf filename in a vmt. Causes trouble elsewhere. |
|
if ( pName[0] == CORRECT_PATH_SEPARATOR || pName[1] == CORRECT_PATH_SEPARATOR ) |
|
++pName; |
|
|
|
ITextureInternal *pTexture; |
|
|
|
// Force local cubemaps when using the editor |
|
if ( MaterialSystem()->CanUseEditorMaterials() && ( stricmp( pName, "env_cubemap" ) == 0 ) ) |
|
{ |
|
pTexture = (ITextureInternal*)-1; |
|
} |
|
else |
|
{ |
|
pTexture = static_cast< ITextureInternal * >( MaterialSystem()->FindTexture( pName, pTextureGroupName, false, nAdditionalCreationFlags ) ); |
|
} |
|
|
|
if( !pTexture ) |
|
{ |
|
if( !g_pShaderDevice->IsUsingGraphics() && ( stricmp( pName, "env_cubemap" ) != 0 ) ) |
|
{ |
|
Warning( "Shader_t::LoadTexture: texture \"%s.vtf\" doesn't exist\n", pName ); |
|
} |
|
pTexture = TextureManager()->ErrorTexture(); |
|
} |
|
|
|
pTextureVar->SetTextureValue( pTexture ); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Loads a bumpmap |
|
//----------------------------------------------------------------------------- |
|
void CShaderSystem::LoadBumpMap( IMaterialVar *pTextureVar, const char *pTextureGroupName ) |
|
{ |
|
Assert( pTextureVar ); |
|
|
|
if (pTextureVar->GetType() != MATERIAL_VAR_TYPE_STRING) |
|
{ |
|
// This here will cause 'UNDEFINED' material vars |
|
if (pTextureVar->GetType() != MATERIAL_VAR_TYPE_TEXTURE) |
|
{ |
|
pTextureVar->SetTextureValue( TextureManager()->ErrorTexture() ); |
|
} |
|
return; |
|
} |
|
|
|
// Convert a string to the actual texture |
|
ITexture *pTexture; |
|
pTexture = MaterialSystem()->FindTexture( pTextureVar->GetStringValue(), pTextureGroupName, false, 0 ); |
|
|
|
// FIXME: Make a bumpmap error texture |
|
if (!pTexture) |
|
{ |
|
pTexture = TextureManager()->ErrorTexture(); |
|
} |
|
|
|
pTextureVar->SetTextureValue( pTexture ); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Loads a cubemap |
|
//----------------------------------------------------------------------------- |
|
void CShaderSystem::LoadCubeMap( IMaterialVar **ppParams, IMaterialVar *pTextureVar, int nAdditionalCreationFlags /* = 0 */ ) |
|
{ |
|
if ( !HardwareConfig()->SupportsCubeMaps() ) |
|
return; |
|
|
|
if ( pTextureVar->GetType() != MATERIAL_VAR_TYPE_STRING ) |
|
{ |
|
// This here will cause 'UNDEFINED' material vars |
|
if (pTextureVar->GetType() != MATERIAL_VAR_TYPE_TEXTURE) |
|
{ |
|
pTextureVar->SetTextureValue( TextureManager()->ErrorTexture() ); |
|
} |
|
return; |
|
} |
|
|
|
if ( stricmp( pTextureVar->GetStringValue(), "env_cubemap" ) == 0 ) |
|
{ |
|
// don't have to load anything here. . just set the texture value to DummyTexture |
|
// special that says to use the cubemap entity. |
|
pTextureVar->SetTextureValue( &g_DummyTexture ); |
|
SetFlags2( ppParams, MATERIAL_VAR2_USES_ENV_CUBEMAP ); |
|
} |
|
else |
|
{ |
|
ITexture *pTexture; |
|
char textureName[MAX_PATH]; |
|
Q_strncpy( textureName, pTextureVar->GetStringValue(), MAX_PATH ); |
|
if ( HardwareConfig()->GetHDRType() != HDR_TYPE_NONE ) |
|
{ |
|
// Overload the texture name to ".hdr.vtf" (instead of .vtf) if we are running with |
|
// HDR enabled. |
|
Q_strncat( textureName, ".hdr", MAX_PATH, COPY_ALL_CHARACTERS ); |
|
} |
|
pTexture = MaterialSystem()->FindTexture( textureName, TEXTURE_GROUP_CUBE_MAP, false, nAdditionalCreationFlags ); |
|
|
|
// FIXME: Make a cubemap error texture |
|
if ( !pTexture ) |
|
{ |
|
pTexture = TextureManager()->ErrorTexture(); |
|
} |
|
|
|
pTextureVar->SetTextureValue( pTexture ); |
|
} |
|
} |
|
|
|
|
|
|
|
|