summaryrefslogtreecommitdiff
path: root/materialsystem/cmaterialsystem.cpp
diff options
context:
space:
mode:
authorFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
committerFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
commit3bf9df6b2785fa6d951086978a3e66f49427166a (patch)
tree2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /materialsystem/cmaterialsystem.cpp
downloadarchived-source-engine-2018-hl2-src-3bf9df6b2785fa6d951086978a3e66f49427166a.tar.xz
archived-source-engine-2018-hl2-src-3bf9df6b2785fa6d951086978a3e66f49427166a.zip
Diffstat (limited to 'materialsystem/cmaterialsystem.cpp')
-rw-r--r--materialsystem/cmaterialsystem.cpp5547
1 files changed, 5547 insertions, 0 deletions
diff --git a/materialsystem/cmaterialsystem.cpp b/materialsystem/cmaterialsystem.cpp
new file mode 100644
index 0000000..3d19a16
--- /dev/null
+++ b/materialsystem/cmaterialsystem.cpp
@@ -0,0 +1,5547 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//===========================================================================//
+
+#include "pch_materialsystem.h"
+
+#define MATSYS_INTERNAL
+
+#include "cmaterialsystem.h"
+
+#include "colorspace.h"
+#include "materialsystem/materialsystem_config.h"
+#include "IHardwareConfigInternal.h"
+#include "shadersystem.h"
+#include "texturemanager.h"
+#include "shaderlib/ShaderDLL.h"
+#include "tier1/callqueue.h"
+#include "vstdlib/jobthread.h"
+#include "cmatnullrendercontext.h"
+#include "filesystem/IQueuedLoader.h"
+#include "datacache/idatacache.h"
+#include "materialsystem/imaterialproxy.h"
+#include "vstdlib/IKeyValuesSystem.h"
+#include "ctexturecompositor.h"
+
+#if defined( _X360 )
+#include "xbox/xbox_console.h"
+#include "xbox/xbox_win32stubs.h"
+#endif
+
+// NOTE: This must be the last file included!!!
+#include "tier0/memdbgon.h"
+
+#ifdef POSIX
+#define _finite finite
+#endif
+
+// this is hooked into the engines convar
+ConVar mat_debugalttab( "mat_debugalttab", "0", FCVAR_CHEAT );
+
+ConVar mat_forcemanagedtextureintohardware( "mat_forcemanagedtextureintohardware", "1", FCVAR_HIDDEN | FCVAR_ALLOWED_IN_COMPETITIVE );
+
+ConVar mat_supportflashlight( "mat_supportflashlight", "-1", FCVAR_HIDDEN, "0 - do not support flashlight (don't load flashlight shader combos), 1 - flashlight is supported" );
+#ifdef OSX
+#define CV_FRAME_SWAP_WORKAROUND_DEFAULT "1"
+#else
+#define CV_FRAME_SWAP_WORKAROUND_DEFAULT "0"
+#endif
+ConVar mat_texture_reload_frame_swap_workaround( "mat_texture_reload_frame_swap_workaround", CV_FRAME_SWAP_WORKAROUND_DEFAULT, FCVAR_INTERNAL_USE,
+ "Workaround certain GL drivers holding unnecessary amounts of data when loading many materials by forcing synthetic frame swaps" );
+
+// This ConVar allows us to skip ~40% of our map load time, but it doesn't work on GPUs older
+// than ~2005. We set it automatically and don't expose it to players.
+ConVar mat_requires_rt_alloc_first( "mat_requires_rt_alloc_first", "0", FCVAR_HIDDEN );
+
+// Make sure this convar gets created before videocfg.lib is initialized, so it can be driven by dxsupport.cfg
+static ConVar mat_tonemapping_occlusion_use_stencil( "mat_tonemapping_occlusion_use_stencil", "0" );
+
+#ifdef DX_TO_GL_ABSTRACTION
+// In GL mode, we currently require mat_dxlevel to be between 90-92
+static ConVar mat_dxlevel( "mat_dxlevel", "92", 0, "", true, 90, true, 92, NULL );
+#else
+static ConVar mat_dxlevel( "mat_dxlevel", "0", 0, "Current DirectX Level. Competitive play requires at least mat_dxlevel 90", false, 0, false, 0, true, 90, false, 0, NULL );
+#endif
+
+IMaterialInternal *g_pErrorMaterial = NULL;
+
+CreateInterfaceFn g_fnMatSystemConnectCreateInterface = NULL;
+
+static int ReadListFromFile(CUtlVector<char*>* outReplacementMaterials, const char *pszPathName);
+
+//#define PERF_TESTING 1
+
+//-----------------------------------------------------------------------------
+// Implementational structures
+//-----------------------------------------------------------------------------
+
+
+//-----------------------------------------------------------------------------
+// Singleton instance exposed to the engine
+//-----------------------------------------------------------------------------
+
+CMaterialSystem g_MaterialSystem;
+EXPOSE_SINGLE_INTERFACE_GLOBALVAR( CMaterialSystem, IMaterialSystem,
+ MATERIAL_SYSTEM_INTERFACE_VERSION, g_MaterialSystem );
+
+// Expose this to the external shader DLLs
+MaterialSystem_Config_t g_config;
+EXPOSE_SINGLE_INTERFACE_GLOBALVAR( MaterialSystem_Config_t, MaterialSystem_Config_t, MATERIALSYSTEM_CONFIG_VERSION, g_config );
+
+//-----------------------------------------------------------------------------
+
+CThreadFastMutex g_MatSysMutex;
+
+//-----------------------------------------------------------------------------
+// Purpose: additional materialsystem information, internal use only
+//-----------------------------------------------------------------------------
+#ifndef _X360
+struct MaterialSystem_Config_Internal_t
+{
+ int r_waterforceexpensive;
+};
+MaterialSystem_Config_Internal_t g_config_internal;
+#endif
+
+//-----------------------------------------------------------------------------
+// Necessary to allow the shader DLLs to get ahold of IMaterialSystemHardwareConfig
+//-----------------------------------------------------------------------------
+IHardwareConfigInternal* g_pHWConfig = 0;
+static void *GetHardwareConfig()
+{
+ if ( g_pHWConfig )
+ return (IMaterialSystemHardwareConfig*)g_pHWConfig;
+
+ // can't call QueryShaderAPI here because it calls a factory function
+ // and we end up in an infinite recursion
+ return NULL;
+}
+EXPOSE_INTERFACE_FN( GetHardwareConfig, IMaterialSystemHardwareConfig, MATERIALSYSTEM_HARDWARECONFIG_INTERFACE_VERSION );
+
+
+//-----------------------------------------------------------------------------
+// Necessary to allow the shader DLLs to get ahold of ICvar
+//-----------------------------------------------------------------------------
+static void *GetICVar()
+{
+ return g_pCVar;
+}
+EXPOSE_INTERFACE_FN( GetICVar, ICVar, CVAR_INTERFACE_VERSION );
+
+//-----------------------------------------------------------------------------
+// Accessor to get at the material system
+//-----------------------------------------------------------------------------
+IMaterialSystemInternal *g_pInternalMaterialSystem = &g_MaterialSystem;
+IShaderUtil *g_pShaderUtil = &g_MaterialSystem;
+
+#if defined(USE_SDL)
+#include "appframework/ilaunchermgr.h"
+ILauncherMgr *g_pLauncherMgr = NULL; // set in CMaterialSystem::Connect
+#endif
+
+//-----------------------------------------------------------------------------
+// Factory used to get at internal interfaces (used by shaderapi + shader dlls)
+//-----------------------------------------------------------------------------
+void *ShaderFactory( const char *pName, int *pReturnCode )
+{
+ if (pReturnCode)
+ {
+ *pReturnCode = IFACE_OK;
+ }
+
+ if ( !Q_stricmp( pName, FILESYSTEM_INTERFACE_VERSION ))
+ return g_pFullFileSystem;
+
+ if ( !Q_stricmp( pName, QUEUEDLOADER_INTERFACE_VERSION ))
+ return g_pQueuedLoader;
+
+ if ( !Q_stricmp( pName, SHADER_UTIL_INTERFACE_VERSION ))
+ return g_pShaderUtil;
+
+#ifdef USE_SDL
+ if ( !Q_stricmp( pName, "SDLMgrInterface001" /*SDLMGR_INTERFACE_VERSION*/ ))
+ return g_pLauncherMgr;
+#endif
+
+ void * pInterface = g_MaterialSystem.QueryInterface( pName );
+ if ( pInterface )
+ return pInterface;
+
+ if ( pReturnCode )
+ {
+ *pReturnCode = IFACE_FAILED;
+ }
+ return NULL;
+}
+
+//-----------------------------------------------------------------------------
+// Resource preloading for materials.
+//-----------------------------------------------------------------------------
+class CResourcePreloadMaterial : public CResourcePreload
+{
+ virtual bool CreateResource( const char *pName )
+ {
+ IMaterial *pMaterial = g_MaterialSystem.FindMaterial( pName, TEXTURE_GROUP_WORLD, false );
+ IMaterialInternal *pMatInternal = static_cast< IMaterialInternal * >( pMaterial );
+ if ( pMatInternal )
+ {
+ // always work with the realtime material internally
+ pMatInternal = pMatInternal->GetRealTimeVersion();
+
+ // tag these for later identification (prevents an unwanted purge)
+ pMatInternal->MarkAsPreloaded( true );
+ if ( !pMatInternal->IsErrorMaterial() )
+ {
+ // force material's textures to create now
+ pMatInternal->Precache();
+ return true;
+ }
+ else
+ {
+ if ( IsPosix() )
+ {
+ printf("\n ##### CResourcePreloadMaterial::CreateResource can't find material %s\n", pName);
+ }
+ }
+ }
+
+ return false;
+ }
+
+ //-----------------------------------------------------------------------------
+ // Called before queued loader i/o jobs are actually performed. Must free up memory
+ // to ensure i/o requests have enough memory to succeed. The materials that were
+ // touched by the CreateResource() are inhibited from purging (as is their textures,
+ // by virtue of ref counts), all others are candidates. The preloaded materials
+ // are by definition zero ref'd until owned by the normal loading process. Any material
+ // that stays zero ref'd is a candidate for the post load purge.
+ //-----------------------------------------------------------------------------
+ virtual void PurgeUnreferencedResources()
+ {
+ bool bSpew = ( g_pQueuedLoader->GetSpewDetail() & LOADER_DETAIL_PURGES ) != 0;
+
+ bool bDidUncacheMaterial = false;
+ MaterialHandle_t hNext;
+ for ( MaterialHandle_t hMaterial = g_MaterialSystem.FirstMaterial(); hMaterial != g_MaterialSystem.InvalidMaterial(); hMaterial = hNext )
+ {
+ hNext = g_MaterialSystem.NextMaterial( hMaterial );
+
+ IMaterialInternal *pMatInternal = g_MaterialSystem.GetMaterialInternal( hMaterial );
+ Assert( pMatInternal->GetReferenceCount() >= 0 );
+
+ // preloaded materials are safe from this pre-purge
+ if ( !pMatInternal->IsPreloaded() )
+ {
+ // undo any possible artifical ref count
+ pMatInternal->ArtificialRelease();
+ if ( pMatInternal->GetReferenceCount() <= 0 )
+ {
+ if ( bSpew )
+ {
+ Msg( "CResourcePreloadMaterial: Purging: %s (%d)\n", pMatInternal->GetName(), pMatInternal->GetReferenceCount() );
+ }
+ bDidUncacheMaterial = true;
+ pMatInternal->Uncache();
+ pMatInternal->DeleteIfUnreferenced();
+ }
+ }
+ else
+ {
+ // clear the bit
+ pMatInternal->MarkAsPreloaded( false );
+ }
+ }
+
+ // purged materials unreference their textures
+ // purge any zero ref'd textures
+ TextureManager()->RemoveUnusedTextures();
+
+ // fixup any excluded textures, may cause some new batch requests
+ MaterialSystem()->UpdateExcludedTextures();
+ }
+
+ virtual void PurgeAll()
+ {
+ bool bSpew = ( g_pQueuedLoader->GetSpewDetail() & LOADER_DETAIL_PURGES ) != 0;
+
+ bool bDidUncacheMaterial = false;
+ MaterialHandle_t hNext;
+ for ( MaterialHandle_t hMaterial = g_MaterialSystem.FirstMaterial(); hMaterial != g_MaterialSystem.InvalidMaterial(); hMaterial = hNext )
+ {
+ hNext = g_MaterialSystem.NextMaterial( hMaterial );
+
+ IMaterialInternal *pMatInternal = g_MaterialSystem.GetMaterialInternal( hMaterial );
+ Assert( pMatInternal->GetReferenceCount() >= 0 );
+
+ pMatInternal->MarkAsPreloaded( false );
+ // undo any possible artifical ref count
+ pMatInternal->ArtificialRelease();
+ if ( pMatInternal->GetReferenceCount() <= 0 )
+ {
+ if ( bSpew )
+ {
+ Msg( "CResourcePreloadMaterial: Purging: %s (%d)\n", pMatInternal->GetName(), pMatInternal->GetReferenceCount() );
+ }
+ bDidUncacheMaterial = true;
+ pMatInternal->Uncache();
+ pMatInternal->DeleteIfUnreferenced();
+ }
+ }
+
+ // purged materials unreference their textures
+ // purge any zero ref'd textures
+ TextureManager()->RemoveUnusedTextures();
+ }
+};
+
+static CResourcePreloadMaterial s_ResourcePreloadMaterial;
+
+//-----------------------------------------------------------------------------
+// Resource preloading for cubemaps.
+//-----------------------------------------------------------------------------
+class CResourcePreloadCubemap : public CResourcePreload
+{
+ virtual bool CreateResource( const char *pName )
+ {
+ ITexture *pTexture = g_MaterialSystem.FindTexture( pName, TEXTURE_GROUP_CUBE_MAP, true );
+ ITextureInternal *pTexInternal = static_cast< ITextureInternal * >( pTexture );
+ if ( pTexInternal )
+ {
+ // There can be cubemaps that are unbound by materials. To prevent an unwanted purge,
+ // mark and increase the ref count. Otherwise the pre-purge discards these zero
+ // ref'd textures, and then the normal loading process hitches on the miss.
+ // The zombie cubemaps DO get discarded after the normal loading process completes
+ // if no material references them.
+ pTexInternal->MarkAsPreloaded( true );
+ pTexInternal->IncrementReferenceCount();
+ if ( !IsErrorTexture( pTexInternal ) )
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ //-----------------------------------------------------------------------------
+ // All valid cubemaps should have been owned by their materials. Undo the preloaded
+ // cubemap locks. Any zero ref'd cubemaps will be purged by the normal loading path conclusion.
+ //-----------------------------------------------------------------------------
+ virtual void OnEndMapLoading( bool bAbort )
+ {
+ int iIndex = -1;
+ for ( ;; )
+ {
+ ITextureInternal *pTexInternal;
+ iIndex = TextureManager()->FindNext( iIndex, &pTexInternal );
+ if ( iIndex == -1 || !pTexInternal )
+ {
+ // end of list
+ break;
+ }
+
+ if ( pTexInternal->IsPreloaded() )
+ {
+ // undo the artificial increase
+ pTexInternal->MarkAsPreloaded( false );
+ pTexInternal->DecrementReferenceCount();
+ }
+ }
+ }
+};
+static CResourcePreloadCubemap s_ResourcePreloadCubemap;
+
+//-----------------------------------------------------------------------------
+// Creates the debugging materials
+//-----------------------------------------------------------------------------
+void CMaterialSystem::CreateDebugMaterials()
+{
+ if ( !m_pDrawFlatMaterial )
+ {
+ KeyValues *pVMTKeyValues = new KeyValues( "UnlitGeneric" );
+ pVMTKeyValues->SetInt( "$model", 1 );
+ pVMTKeyValues->SetFloat( "$decalscale", 0.05f );
+ pVMTKeyValues->SetString( "$basetexture", "error" ); // This is the "error texture"
+ g_pErrorMaterial = static_cast<IMaterialInternal*>(CreateMaterial( "___error.vmt", pVMTKeyValues ))->GetRealTimeVersion();
+
+ pVMTKeyValues = new KeyValues( "UnlitGeneric" );
+ pVMTKeyValues->SetInt( "$flat", 1 );
+ pVMTKeyValues->SetInt( "$vertexcolor", 1 );
+ m_pDrawFlatMaterial = static_cast<IMaterialInternal*>(CreateMaterial( "___flat.vmt", pVMTKeyValues ))->GetRealTimeVersion();
+
+ pVMTKeyValues = new KeyValues( "BufferClearObeyStencil" );
+ pVMTKeyValues->SetInt( "$nocull", 1 );
+ m_pBufferClearObeyStencil[BUFFER_CLEAR_NONE] = static_cast<IMaterialInternal*>(CreateMaterial( "___buffer_clear_obey_stencil0.vmt", pVMTKeyValues ))->GetRealTimeVersion();
+
+ pVMTKeyValues = new KeyValues( "BufferClearObeyStencil" );
+ pVMTKeyValues->SetInt( "$nocull", 1 );
+ pVMTKeyValues->SetInt( "$clearcolor", 1 );
+ pVMTKeyValues->SetInt( "$vertexcolor", 1 );
+ m_pBufferClearObeyStencil[BUFFER_CLEAR_COLOR] = static_cast<IMaterialInternal*>(CreateMaterial( "___buffer_clear_obey_stencil1.vmt", pVMTKeyValues ))->GetRealTimeVersion();
+
+ pVMTKeyValues = new KeyValues( "BufferClearObeyStencil" );
+ pVMTKeyValues->SetInt( "$nocull", 1 );
+ pVMTKeyValues->SetInt( "$clearalpha", 1 );
+ pVMTKeyValues->SetInt( "$vertexcolor", 1 );
+ m_pBufferClearObeyStencil[BUFFER_CLEAR_ALPHA] = static_cast<IMaterialInternal*>(CreateMaterial( "___buffer_clear_obey_stencil2.vmt", pVMTKeyValues ))->GetRealTimeVersion();
+
+ pVMTKeyValues = new KeyValues( "BufferClearObeyStencil" );
+ pVMTKeyValues->SetInt( "$nocull", 1 );
+ pVMTKeyValues->SetInt( "$clearcolor", 1 );
+ pVMTKeyValues->SetInt( "$clearalpha", 1 );
+ pVMTKeyValues->SetInt( "$vertexcolor", 1 );
+ m_pBufferClearObeyStencil[BUFFER_CLEAR_COLOR_AND_ALPHA] = static_cast<IMaterialInternal*>(CreateMaterial( "___buffer_clear_obey_stencil3.vmt", pVMTKeyValues ))->GetRealTimeVersion();
+
+ pVMTKeyValues = new KeyValues( "BufferClearObeyStencil" );
+ pVMTKeyValues->SetInt( "$nocull", 1 );
+ pVMTKeyValues->SetInt( "$cleardepth", 1 );
+ m_pBufferClearObeyStencil[BUFFER_CLEAR_DEPTH] = static_cast<IMaterialInternal*>(CreateMaterial( "___buffer_clear_obey_stencil4.vmt", pVMTKeyValues ))->GetRealTimeVersion();
+
+ pVMTKeyValues = new KeyValues( "BufferClearObeyStencil" );
+ pVMTKeyValues->SetInt( "$nocull", 1 );
+ pVMTKeyValues->SetInt( "$cleardepth", 1 );
+ pVMTKeyValues->SetInt( "$clearcolor", 1 );
+ pVMTKeyValues->SetInt( "$vertexcolor", 1 );
+ m_pBufferClearObeyStencil[BUFFER_CLEAR_COLOR_AND_DEPTH] = static_cast<IMaterialInternal*>(CreateMaterial( "___buffer_clear_obey_stencil5.vmt", pVMTKeyValues ))->GetRealTimeVersion();
+
+ pVMTKeyValues = new KeyValues( "BufferClearObeyStencil" );
+ pVMTKeyValues->SetInt( "$nocull", 1 );
+ pVMTKeyValues->SetInt( "$cleardepth", 1 );
+ pVMTKeyValues->SetInt( "$clearalpha", 1 );
+ pVMTKeyValues->SetInt( "$vertexcolor", 1 );
+ m_pBufferClearObeyStencil[BUFFER_CLEAR_ALPHA_AND_DEPTH] = static_cast<IMaterialInternal*>(CreateMaterial( "___buffer_clear_obey_stencil6.vmt", pVMTKeyValues ))->GetRealTimeVersion();
+
+ pVMTKeyValues = new KeyValues( "BufferClearObeyStencil" );
+ pVMTKeyValues->SetInt( "$nocull", 1 );
+ pVMTKeyValues->SetInt( "$cleardepth", 1 );
+ pVMTKeyValues->SetInt( "$clearcolor", 1 );
+ pVMTKeyValues->SetInt( "$clearalpha", 1 );
+ pVMTKeyValues->SetInt( "$vertexcolor", 1 );
+ m_pBufferClearObeyStencil[BUFFER_CLEAR_COLOR_AND_ALPHA_AND_DEPTH] = static_cast<IMaterialInternal*>(CreateMaterial( "___buffer_clear_obey_stencil7.vmt", pVMTKeyValues ))->GetRealTimeVersion();
+
+ if ( IsX360() )
+ {
+ pVMTKeyValues = new KeyValues( "RenderTargetBlit_X360" );
+ m_pRenderTargetBlitMaterial = static_cast<IMaterialInternal*>(CreateMaterial( "___renderTargetBlit.vmt", pVMTKeyValues ))->GetRealTimeVersion();
+ }
+
+ ShaderSystem()->CreateDebugMaterials();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Creates compositor materials
+//-----------------------------------------------------------------------------
+void CMaterialSystem::CreateCompositorMaterials()
+{
+ // precache composite materials
+ for ( int i = ECO_FirstPrecacheMaterial; i < ECO_LastPrecacheMaterial; i++ )
+ {
+ const char *pszMaterial = GetCombinedMaterialName( ( ECombineOperation ) i );
+ if ( pszMaterial[ 0 ] == '\0' )
+ continue;
+
+ IMaterialInternal *pMatqf = assert_cast< IMaterialInternal* >( FindMaterial( pszMaterial, TEXTURE_GROUP_RUNTIME_COMPOSITE ) );
+ Assert( pMatqf );
+ Assert( !pMatqf->IsErrorMaterial() );
+ IMaterialInternal *pMatrt = pMatqf->GetRealTimeVersion();
+ Assert( pMatrt );
+ pMatrt->IncrementReferenceCount(); // Hold a ref.
+
+ m_pCompositorMaterials.AddToTail( pMatrt );
+ }
+
+}
+
+//-----------------------------------------------------------------------------
+// Cleanup compositor materials
+//-----------------------------------------------------------------------------
+void CMaterialSystem::CleanUpCompositorMaterials()
+{
+ FOR_EACH_VEC( m_pCompositorMaterials, i )
+ {
+ if ( m_pCompositorMaterials[ i ] == NULL )
+ continue;
+
+ m_pCompositorMaterials[ i ]->DecrementReferenceCount();
+ RemoveMaterial( m_pCompositorMaterials[ i ] );
+ }
+
+ m_pCompositorMaterials.RemoveAll();
+}
+
+//-----------------------------------------------------------------------------
+// Creates the debugging materials
+//-----------------------------------------------------------------------------
+void CMaterialSystem::CleanUpDebugMaterials()
+{
+ if ( m_pDrawFlatMaterial )
+ {
+ m_pDrawFlatMaterial->DecrementReferenceCount();
+ RemoveMaterial( m_pDrawFlatMaterial );
+ m_pDrawFlatMaterial = NULL;
+
+ for ( int i = BUFFER_CLEAR_NONE; i < BUFFER_CLEAR_TYPE_COUNT; ++i )
+ {
+ m_pBufferClearObeyStencil[i]->DecrementReferenceCount();
+ RemoveMaterial( m_pBufferClearObeyStencil[i] );
+ m_pBufferClearObeyStencil[i] = NULL;
+ }
+
+ if ( IsX360() )
+ {
+ m_pRenderTargetBlitMaterial->DecrementReferenceCount();
+ RemoveMaterial( m_pRenderTargetBlitMaterial );
+ m_pRenderTargetBlitMaterial = NULL;
+ }
+
+ ShaderSystem()->CleanUpDebugMaterials();
+ }
+}
+
+
+void CMaterialSystem::CleanUpErrorMaterial()
+{
+ // Destruction of g_pErrorMaterial is deferred until after CMaterialDict::Shutdown.
+ // The global g_pErrorMaterial is set to NULL so that IsErrorMaterial() will return false and
+ // RemoveMaterial() / DestroyMaterial() will delete it.
+ IMaterialInternal *pErrorMaterial = g_pErrorMaterial;
+ g_pErrorMaterial = NULL;
+ pErrorMaterial->DecrementReferenceCount();
+ RemoveMaterial( pErrorMaterial );
+}
+
+//-----------------------------------------------------------------------------
+// Constructor
+//-----------------------------------------------------------------------------
+CMaterialSystem::CMaterialSystem()
+{
+ m_nRenderThreadID = 0xFFFFFFFF;
+ m_hAsyncLoadFileCache = NULL;
+ m_ShaderHInst = 0;
+ m_pMaterialProxyFactory = NULL;
+ m_nAdapter = 0;
+ m_nAdapterFlags = 0;
+ m_bRequestedEditorMaterials = false;
+ m_bCanUseEditorMaterials = false;
+ m_StandardTexturesAllocated = false;
+ m_bInFrame = false;
+ m_bThreadHasOwnership = false;
+ m_ThreadOwnershipID = 0;
+ m_pShaderDLL = NULL;
+ m_FullbrightLightmapTextureHandle = INVALID_SHADERAPI_TEXTURE_HANDLE;
+ m_FullbrightBumpedLightmapTextureHandle = INVALID_SHADERAPI_TEXTURE_HANDLE;
+ m_BlackTextureHandle = INVALID_SHADERAPI_TEXTURE_HANDLE;
+ m_FlatNormalTextureHandle = INVALID_SHADERAPI_TEXTURE_HANDLE;
+ m_GreyTextureHandle = INVALID_SHADERAPI_TEXTURE_HANDLE;
+ m_GreyAlphaZeroTextureHandle = INVALID_SHADERAPI_TEXTURE_HANDLE;
+ m_WhiteTextureHandle = INVALID_SHADERAPI_TEXTURE_HANDLE;
+ m_LinearToGammaTableTextureHandle = INVALID_SHADERAPI_TEXTURE_HANDLE;
+ m_LinearToGammaIdentityTableTextureHandle = INVALID_SHADERAPI_TEXTURE_HANDLE;
+ m_MaxDepthTextureHandle = INVALID_SHADERAPI_TEXTURE_HANDLE;
+
+ m_bInStubMode = false;
+ m_pForcedTextureLoadPathID = NULL;
+ m_bAllocatingRenderTargets = false;
+ m_pRenderContext.Set( &m_HardwareRenderContext );
+ m_iCurQueuedContext = 0;
+#if defined(DEDICATED)
+ m_bThreadingNotAvailable = true;
+ m_bForcedSingleThreaded = true;
+ m_bAllowQueuedRendering = false;
+#else
+ m_bThreadingNotAvailable = false;
+ m_bForcedSingleThreaded = false;
+ m_bAllowQueuedRendering = true;
+#endif
+ m_bGeneratedConfig = false;
+ m_pActiveAsyncJob = NULL;
+ m_pMatQueueThreadPool = NULL;
+ m_IdealThreadMode = m_ThreadMode = MATERIAL_SINGLE_THREADED;
+ m_nServiceThread = 0;
+ m_nRenderTargetFrameBufferHeightOverride = m_nRenderTargetFrameBufferWidthOverride = 0;
+
+ m_bReplacementFilesValid = false;
+}
+
+CMaterialSystem::~CMaterialSystem()
+{
+ if (m_pShaderDLL)
+ {
+ delete[] m_pShaderDLL;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Creates/destroys the shader implementation for the selected API
+//-----------------------------------------------------------------------------
+CreateInterfaceFn CMaterialSystem::CreateShaderAPI( char const* pShaderDLL )
+{
+ if ( !pShaderDLL )
+ return 0;
+
+ // Clean up the old shader
+ DestroyShaderAPI();
+
+ // Load the new shader
+ m_ShaderHInst = Sys_LoadModule( pShaderDLL );
+
+ // Error loading the shader
+ if ( !m_ShaderHInst )
+ return 0;
+
+ // Get our class factory methods...
+ return Sys_GetFactory( m_ShaderHInst );
+}
+
+void CMaterialSystem::DestroyShaderAPI()
+{
+ if (m_ShaderHInst)
+ {
+ // NOTE: By unloading the library, this will destroy m_pShaderAPI
+ Sys_UnloadModule( m_ShaderHInst );
+ g_pShaderAPI = 0;
+ g_pHWConfig = 0;
+ g_pShaderShadow = 0;
+ m_ShaderHInst = 0;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Sets which shader we should be using. Has to be done before connect!
+//-----------------------------------------------------------------------------
+void CMaterialSystem::SetShaderAPI( char const *pShaderAPIDLL )
+{
+ if ( m_ShaderAPIFactory )
+ {
+ Error( "Cannot set the shader API twice!\n" );
+ }
+
+ if ( !pShaderAPIDLL )
+ {
+ pShaderAPIDLL = "shaderapidx9";
+ }
+
+ // m_pShaderDLL is needed to spew driver info
+ Assert( pShaderAPIDLL );
+ int len = Q_strlen( pShaderAPIDLL ) + 1;
+ m_pShaderDLL = new char[len];
+ memcpy( m_pShaderDLL, pShaderAPIDLL, len );
+
+ m_ShaderAPIFactory = CreateShaderAPI( pShaderAPIDLL );
+ if ( !m_ShaderAPIFactory )
+ {
+ DestroyShaderAPI();
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Connect/disconnect
+//-----------------------------------------------------------------------------
+
+bool CMaterialSystem::Connect( CreateInterfaceFn factory )
+{
+// __stop__();
+
+ if ( !factory )
+ return false;
+
+ if ( !BaseClass::Connect( factory ) )
+ return false;
+
+ if ( !g_pFullFileSystem )
+ {
+ Warning( "The material system requires the filesystem to run!\n" );
+ return false;
+ }
+
+ // Get at the interfaces exported by the shader DLL
+ g_pShaderDeviceMgr = (IShaderDeviceMgr*)m_ShaderAPIFactory( SHADER_DEVICE_MGR_INTERFACE_VERSION, 0 );
+ if ( !g_pShaderDeviceMgr )
+ return false;
+ g_pHWConfig = (IHardwareConfigInternal*)m_ShaderAPIFactory( MATERIALSYSTEM_HARDWARECONFIG_INTERFACE_VERSION, 0 );
+ if ( !g_pHWConfig )
+ return false;
+
+#if !defined(DEDICATED)
+#if defined( USE_SDL )
+ g_pLauncherMgr = (ILauncherMgr *)factory( "SDLMgrInterface001" /*SDL_MGR_INTERFACE_VERSION*/, NULL );
+ if ( !g_pLauncherMgr )
+ {
+ return false;
+ }
+#endif // USE_SDL
+#endif // !DEDICATED
+
+
+ // FIXME: ShaderAPI, ShaderDevice, and ShaderShadow should only come in after setting mode
+ g_pShaderAPI = (IShaderAPI*)m_ShaderAPIFactory( SHADERAPI_INTERFACE_VERSION, 0 );
+ if ( !g_pShaderAPI )
+ return false;
+ g_pShaderDevice = (IShaderDevice*)m_ShaderAPIFactory( SHADER_DEVICE_INTERFACE_VERSION, 0 );
+ if ( !g_pShaderDevice )
+ return false;
+ g_pShaderShadow = (IShaderShadow*)m_ShaderAPIFactory( SHADERSHADOW_INTERFACE_VERSION, 0 );
+ if ( !g_pShaderShadow )
+ return false;
+
+ // Remember the factory for connect
+ g_fnMatSystemConnectCreateInterface = factory;
+
+ return g_pShaderDeviceMgr->Connect( ShaderFactory );
+}
+
+void CMaterialSystem::Disconnect()
+{
+ // Forget the factory for connect
+ g_fnMatSystemConnectCreateInterface = NULL;
+
+ if ( g_pShaderDeviceMgr )
+ {
+ g_pShaderDeviceMgr->Disconnect();
+ g_pShaderDeviceMgr = NULL;
+
+ // Unload the DLL
+ DestroyShaderAPI();
+ }
+ g_pShaderAPI = NULL;
+ g_pHWConfig = NULL;
+ g_pShaderShadow = NULL;
+ g_pShaderDevice = NULL;
+ BaseClass::Disconnect();
+}
+
+
+//-----------------------------------------------------------------------------
+// Used to enable editor materials. Must be called before Init.
+//-----------------------------------------------------------------------------
+void CMaterialSystem::EnableEditorMaterials()
+{
+ m_bRequestedEditorMaterials = true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Method to get at interfaces supported by the SHADDERAPI
+//-----------------------------------------------------------------------------
+void *CMaterialSystem::QueryShaderAPI( const char *pInterfaceName )
+{
+ // Returns various interfaces supported by the shader API dll
+ void *pInterface = NULL;
+ if (m_ShaderAPIFactory)
+ {
+ pInterface = m_ShaderAPIFactory( pInterfaceName, NULL );
+ }
+ return pInterface;
+}
+
+
+//-----------------------------------------------------------------------------
+// Method to get at different interfaces supported by the material system
+//-----------------------------------------------------------------------------
+void *CMaterialSystem::QueryInterface( const char *pInterfaceName )
+{
+ // Returns various interfaces supported by the shader API dll
+ void *pInterface = QueryShaderAPI( pInterfaceName );
+ if ( pInterface )
+ return pInterface;
+
+ CreateInterfaceFn factory = Sys_GetFactoryThis(); // This silly construction is necessary
+ return factory( pInterfaceName, NULL ); // to prevent the LTCG compiler from crashing.
+}
+
+
+//-----------------------------------------------------------------------------
+// Must be called before Init(), if you're going to call it at all...
+//-----------------------------------------------------------------------------
+void CMaterialSystem::SetAdapter( int nAdapter, int nAdapterFlags )
+{
+ m_nAdapter = nAdapter;
+ m_nAdapterFlags = nAdapterFlags;
+}
+
+
+//-----------------------------------------------------------------------------
+// Initializes the color correction terms
+//-----------------------------------------------------------------------------
+void CMaterialSystem::InitColorCorrection( )
+{
+ if ( ColorCorrectionSystem() )
+ {
+ ColorCorrectionSystem()->Init();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Initialization + shutdown of the material system
+//-----------------------------------------------------------------------------
+InitReturnVal_t CMaterialSystem::Init()
+{
+ InitReturnVal_t nRetVal = BaseClass::Init();
+ if ( nRetVal != INIT_OK )
+ return nRetVal;
+
+ // NOTE! : Overbright is 1.0 so that Hammer will work properly with the white bumped and unbumped lightmaps.
+ MathLib_Init( 2.2f, 2.2f, 0.0f, 2.0f );
+
+ g_pShaderDeviceMgr->SetAdapter( m_nAdapter, m_nAdapterFlags );
+ if ( g_pShaderDeviceMgr->Init( ) != INIT_OK )
+ {
+ DestroyShaderAPI();
+ return INIT_FAILED;
+ }
+
+ // Texture manager...
+ TextureManager()->Init( m_nAdapterFlags );
+
+ // Shader system!
+ ShaderSystem()->Init();
+
+#if defined( WIN32 ) && !defined( _X360 )
+ // HACKHACK: <sigh> This horrible hack is possibly the only way to reliably detect an old
+ // version of hammer initializing the material system. We need to know this so that we set
+ // up the editor materials properly. If we don't do this, we never allocate the white lightmap,
+ // for example. We can remove this when we update the SDK!!
+ char szExeName[_MAX_PATH];
+ if ( ::GetModuleFileName( ( HINSTANCE )GetModuleHandle( NULL ), szExeName, sizeof( szExeName ) ) )
+ {
+ char szRight[20];
+ Q_StrRight( szExeName, 11, szRight, sizeof( szRight ) );
+ if ( !Q_stricmp( szRight, "\\hammer.exe" ) )
+ {
+ m_bRequestedEditorMaterials = true;
+ }
+ }
+#endif // WIN32
+
+ m_bCanUseEditorMaterials = m_bRequestedEditorMaterials;
+
+ InitColorCorrection();
+
+ // Set up debug materials...
+ CreateDebugMaterials();
+
+#if !defined(DEDICATED)
+ CreateCompositorMaterials();
+#endif
+
+ if ( IsX360() )
+ {
+ g_pQueuedLoader->InstallLoader( RESOURCEPRELOAD_MATERIAL, &s_ResourcePreloadMaterial );
+ g_pQueuedLoader->InstallLoader( RESOURCEPRELOAD_CUBEMAP, &s_ResourcePreloadCubemap );
+ }
+
+ // Set up a default material system config
+// GenerateConfigFromConfigKeyValues( &g_config, false );
+// UpdateConfig( false );
+
+ // JAY: Added this command line parameter to force creating <32x32 mips
+ // to test for reported performance regressions on some systems
+ if ( CommandLine()->FindParm("-forceallmips") )
+ {
+ extern bool g_bForceTextureAllMips;
+ g_bForceTextureAllMips = true;
+ }
+
+#if defined(DEDICATED)
+ m_bThreadingNotAvailable = true;
+#else
+ for ( int i = 0; i < ARRAYSIZE( m_QueuedRenderContexts ); i++ )
+ {
+ if ( !m_QueuedRenderContexts[i].IsInitialized() )
+ {
+ if ( !m_QueuedRenderContexts[i].Init( this, &m_HardwareRenderContext ) )
+ {
+ m_bThreadingNotAvailable = true;
+ break;
+ }
+ }
+ }
+#endif
+
+ return m_HardwareRenderContext.Init( this );
+}
+
+//-----------------------------------------------------------------------------
+// For backwards compatability
+//-----------------------------------------------------------------------------
+static CreateInterfaceFn s_TempCVarFactory;
+static CreateInterfaceFn s_TempFileSystemFactory;
+
+void* TempCreateInterface( const char *pName, int *pReturnCode )
+{
+ void *pRetVal = NULL;
+
+ if ( s_TempCVarFactory )
+ {
+ pRetVal = s_TempCVarFactory( pName, pReturnCode );
+ if (pRetVal)
+ return pRetVal;
+ }
+
+ pRetVal = s_TempFileSystemFactory( pName, pReturnCode );
+ if (pRetVal)
+ return pRetVal;
+
+ return NULL;
+}
+
+
+//-----------------------------------------------------------------------------
+// Initializes and shuts down the shader API
+//-----------------------------------------------------------------------------
+CreateInterfaceFn CMaterialSystem::Init( char const* pShaderAPIDLL,
+ IMaterialProxyFactory *pMaterialProxyFactory,
+ CreateInterfaceFn fileSystemFactory,
+ CreateInterfaceFn cvarFactory )
+{
+ SetShaderAPI( pShaderAPIDLL );
+
+ s_TempCVarFactory = cvarFactory;
+ s_TempFileSystemFactory = fileSystemFactory;
+ if ( !Connect( TempCreateInterface ) )
+ return 0;
+
+ if (Init() != INIT_OK)
+ return NULL;
+
+ // save the proxy factory
+ m_pMaterialProxyFactory = pMaterialProxyFactory;
+
+ return m_ShaderAPIFactory;
+}
+
+void CMaterialSystem::Shutdown( )
+{
+ DestroyMatQueueThreadPool();
+
+ m_HardwareRenderContext.Shutdown();
+
+ // Clean up standard textures
+ ReleaseStandardTextures();
+
+ CleanUpCompositorMaterials();
+
+ // Clean up the debug materials
+ CleanUpDebugMaterials();
+
+ g_pMorphMgr->FreeMaterials();
+ g_pOcclusionQueryMgr->FreeOcclusionQueryObjects();
+
+ GetLightmaps()->Shutdown();
+ m_MaterialDict.Shutdown();
+
+ CleanUpErrorMaterial();
+
+ // Shader system!
+ ShaderSystem()->Shutdown();
+
+ // Texture manager...
+ TextureManager()->Shutdown();
+
+ if (g_pShaderDeviceMgr)
+ {
+ g_pShaderDeviceMgr->Shutdown();
+ }
+
+ BaseClass::Shutdown();
+}
+
+void CMaterialSystem::ModInit()
+{
+ // Set up a default material system config
+ GenerateConfigFromConfigKeyValues( &g_config, false );
+ UpdateConfig( false );
+
+ // Shader system!
+ ShaderSystem()->ModInit();
+}
+
+void CMaterialSystem::ModShutdown()
+{
+ // Shader system!
+ ShaderSystem()->ModShutdown();
+
+ // HACK - this is here to unhook ourselves from the client interface, since we're not actually notified when it happens
+ m_pMaterialProxyFactory = NULL;
+}
+
+//-----------------------------------------------------------------------------
+// Returns the current adapter in use
+//-----------------------------------------------------------------------------
+IMaterialSystemHardwareConfig *CMaterialSystem::GetHardwareConfig( const char *pVersion, int *returnCode )
+{
+ return ( IMaterialSystemHardwareConfig * )m_ShaderAPIFactory( pVersion, returnCode );
+}
+
+
+//-----------------------------------------------------------------------------
+// Returns the current adapter in use
+//-----------------------------------------------------------------------------
+int CMaterialSystem::GetCurrentAdapter() const
+{
+ return g_pShaderDevice->GetCurrentAdapter();
+}
+
+
+//-----------------------------------------------------------------------------
+// Returns the device name for the current adapter
+//-----------------------------------------------------------------------------
+char *CMaterialSystem::GetDisplayDeviceName() const
+{
+ return g_pShaderDevice->GetDisplayDeviceName();
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+void CMaterialSystem::SetThreadMode( MaterialThreadMode_t nextThreadMode, int nServiceThread )
+{
+ m_IdealThreadMode = nextThreadMode;
+ m_nServiceThread = nServiceThread;
+}
+
+MaterialThreadMode_t CMaterialSystem::GetThreadMode()
+{
+ return m_ThreadMode;
+}
+
+
+bool CMaterialSystem::IsRenderThreadSafe( )
+{
+ return ( m_ThreadMode != MATERIAL_QUEUED_THREADED && ThreadInMainThread() ) ||
+ ( m_ThreadMode == MATERIAL_QUEUED_THREADED && m_nRenderThreadID == ThreadGetCurrentId() );
+}
+
+
+bool CMaterialSystem::AllowThreading( bool bAllow, int nServiceThread )
+{
+#if defined(DEDICATED)
+ return false;
+#else
+ if ( CommandLine()->ParmValue( "-threads", 2 ) < 2 ) // if -threads specified on command line to restrict all the pools then obey and not turn on QMS
+ bAllow = false;
+
+ bool bOldAllow = m_bAllowQueuedRendering;
+
+ if ( GetCPUInformation()->m_nPhysicalProcessors >= 2 )
+ {
+ m_bAllowQueuedRendering = bAllow;
+ bool bQueued = m_IdealThreadMode != MATERIAL_SINGLE_THREADED;
+ if ( bAllow && !bQueued )
+ {
+ // go into queued mode
+ DevMsg( "Queued Material System: ENABLED!\n" );
+ SetThreadMode( MATERIAL_QUEUED_THREADED, nServiceThread );
+ }
+ else if ( !bAllow && bQueued )
+ {
+ // disabling queued mode just needs to stop the queuing of drawing
+ // but still allow other threaded access to the Material System
+ // flush the queue
+ DevMsg( "Queued Material System: DISABLED!\n" );
+ ForceSingleThreaded();
+ MaterialLock_t hMaterialLock = Lock();
+ SetThreadMode( MATERIAL_SINGLE_THREADED, -1 );
+ Unlock( hMaterialLock );
+ }
+ }
+ else
+ {
+ m_bAllowQueuedRendering = false;
+ }
+ return bOldAllow;
+#endif // !DEDICATED
+}
+void CMaterialSystem::ExecuteQueued()
+{
+}
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+IMatRenderContext *CMaterialSystem::GetRenderContext()
+{
+ IMatRenderContext *pResult = m_pRenderContext.Get();
+ if ( !pResult )
+ {
+ pResult = &m_HardwareRenderContext;
+ m_pRenderContext.Set( &m_HardwareRenderContext );
+ }
+ return RetAddRef( pResult );
+}
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+IMatRenderContext *CMaterialSystem::CreateRenderContext( MaterialContextType_t type )
+{
+ switch ( type )
+ {
+ case MATERIAL_HARDWARE_CONTEXT:
+ return NULL;
+
+ case MATERIAL_QUEUED_CONTEXT:
+ {
+ CMatQueuedRenderContext *pQueuedContext = new CMatQueuedRenderContext;
+ pQueuedContext->Init( this, &m_HardwareRenderContext );
+ pQueuedContext->BeginQueue( &m_HardwareRenderContext );
+ return pQueuedContext;
+
+ }
+
+ case MATERIAL_NULL_CONTEXT:
+ {
+ CMatRenderContextBase *pNullContext = CreateNullRenderContext();
+ pNullContext->Init();
+ pNullContext->InitializeFrom( &m_HardwareRenderContext );
+ return pNullContext;
+ }
+ }
+ Assert(0);
+ return NULL;
+}
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+IMatRenderContext *CMaterialSystem::SetRenderContext( IMatRenderContext *pNewContext )
+{
+ IMatRenderContext *pOldContext = m_pRenderContext.Get();
+ if ( pNewContext )
+ {
+ pNewContext->AddRef();
+ m_pRenderContext.Set( assert_cast<IMatRenderContextInternal *>(pNewContext) );
+ }
+ else
+ {
+ m_pRenderContext.Set( NULL );
+ }
+ return pOldContext;
+}
+
+
+//-----------------------------------------------------------------------------
+// Get/Set Material proxy factory
+//-----------------------------------------------------------------------------
+IMaterialProxyFactory* CMaterialSystem::GetMaterialProxyFactory()
+{
+ return m_pMaterialProxyFactory;
+}
+
+void CMaterialSystem::SetMaterialProxyFactory( IMaterialProxyFactory* pFactory )
+{
+ // Changing the factory requires an uncaching of all materials
+ // since the factory may contain different proxies
+ UncacheAllMaterials();
+
+ m_pMaterialProxyFactory = pFactory;
+}
+
+
+//-----------------------------------------------------------------------------
+// Can we use editor materials?
+//-----------------------------------------------------------------------------
+bool CMaterialSystem::CanUseEditorMaterials() const
+{
+ return m_bCanUseEditorMaterials;
+}
+
+
+//-----------------------------------------------------------------------------
+// Methods related to mode setting...
+//-----------------------------------------------------------------------------
+
+// Gets the number of adapters...
+int CMaterialSystem::GetDisplayAdapterCount() const
+{
+ return g_pShaderDeviceMgr->GetAdapterCount( );
+}
+
+// Returns info about each adapter
+void CMaterialSystem::GetDisplayAdapterInfo( int adapter, MaterialAdapterInfo_t& info ) const
+{
+ g_pShaderDeviceMgr->GetAdapterInfo( adapter, info );
+}
+
+// Returns the number of modes
+int CMaterialSystem::GetModeCount( int adapter ) const
+{
+ return g_pShaderDeviceMgr->GetModeCount( adapter );
+}
+
+
+//-----------------------------------------------------------------------------
+// Compatability function, should go away eventually
+//-----------------------------------------------------------------------------
+static void ConvertModeStruct( ShaderDeviceInfo_t *pMode, const MaterialSystem_Config_t &config )
+{
+ pMode->m_DisplayMode.m_nWidth = config.m_VideoMode.m_Width;
+ pMode->m_DisplayMode.m_nHeight = config.m_VideoMode.m_Height;
+ pMode->m_DisplayMode.m_Format = config.m_VideoMode.m_Format;
+ pMode->m_DisplayMode.m_nRefreshRateNumerator = config.m_VideoMode.m_RefreshRate;
+ pMode->m_DisplayMode.m_nRefreshRateDenominator = config.m_VideoMode.m_RefreshRate ? 1 : 0;
+ pMode->m_nBackBufferCount = 1;
+ pMode->m_nAASamples = config.m_nAASamples;
+ pMode->m_nAAQuality = config.m_nAAQuality;
+ pMode->m_nDXLevel = MAX( ABSOLUTE_MINIMUM_DXLEVEL, config.dxSupportLevel );
+ pMode->m_nWindowedSizeLimitWidth = (int)config.m_WindowedSizeLimitWidth;
+ pMode->m_nWindowedSizeLimitHeight = (int)config.m_WindowedSizeLimitHeight;
+
+ pMode->m_bWindowed = config.Windowed();
+ pMode->m_bResizing = config.Resizing();
+ pMode->m_bUseStencil = config.Stencil();
+ pMode->m_bLimitWindowedSize = config.LimitWindowedSize();
+ pMode->m_bWaitForVSync = config.WaitForVSync();
+ pMode->m_bScaleToOutputResolution = config.ScaleToOutputResolution();
+ pMode->m_bUsingMultipleWindows = config.UsingMultipleWindows();
+}
+
+static void ConvertModeStruct( MaterialVideoMode_t *pMode, const ShaderDisplayMode_t &info )
+{
+ pMode->m_Width = info.m_nWidth;
+ pMode->m_Height = info.m_nHeight;
+ pMode->m_Format = info.m_Format;
+ pMode->m_RefreshRate = info.m_nRefreshRateDenominator ? ( info.m_nRefreshRateNumerator / info.m_nRefreshRateDenominator ) : 0;
+}
+
+
+//-----------------------------------------------------------------------------
+// Returns mode information..
+//-----------------------------------------------------------------------------
+void CMaterialSystem::GetModeInfo( int nAdapter, int nMode, MaterialVideoMode_t& info ) const
+{
+ ShaderDisplayMode_t shaderInfo;
+ g_pShaderDeviceMgr->GetModeInfo( &shaderInfo, nAdapter, nMode );
+ ConvertModeStruct( &info, shaderInfo );
+}
+
+
+//-----------------------------------------------------------------------------
+// Returns the mode info for the current display device
+//-----------------------------------------------------------------------------
+void CMaterialSystem::GetDisplayMode( MaterialVideoMode_t& info ) const
+{
+ ShaderDisplayMode_t shaderInfo;
+ g_pShaderDeviceMgr->GetCurrentModeInfo( &shaderInfo, m_nAdapter );
+ ConvertModeStruct( &info, shaderInfo );
+}
+
+void CMaterialSystem::ForceSingleThreaded()
+{
+ if ( !ThreadInMainThread() )
+ {
+ Error("Can't force single thread from within thread!\n");
+ }
+ if ( GetThreadMode() != MATERIAL_SINGLE_THREADED )
+ {
+ if ( m_pActiveAsyncJob && !m_pActiveAsyncJob->IsFinished() )
+ {
+ m_pActiveAsyncJob->WaitForFinish();
+ }
+ SafeRelease( m_pActiveAsyncJob );
+
+ ThreadRelease();
+
+ g_pShaderAPI->EnableShaderShaderMutex( false );
+ m_HardwareRenderContext.InitializeFrom(&m_QueuedRenderContexts[m_iCurQueuedContext]);
+ m_pRenderContext.Set( &m_HardwareRenderContext );
+ for ( int i = 0; i < ARRAYSIZE( m_QueuedRenderContexts ); i++ )
+ {
+ Assert( m_QueuedRenderContexts[i].IsInitialized() );
+ m_QueuedRenderContexts[i].EndQueue(true);
+ }
+ if( mat_debugalttab.GetBool() )
+ {
+ Warning("Forcing queued mode off!\n");
+ }
+
+ // NOTE: Must happen after EndQueue or proxies get bound again, which is bad.
+ m_ThreadMode = MATERIAL_SINGLE_THREADED;
+
+ m_bForcedSingleThreaded = true;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Sets the mode...
+//-----------------------------------------------------------------------------
+bool CMaterialSystem::SetMode( void* hwnd, const MaterialSystem_Config_t &config )
+{
+ Assert( m_bGeneratedConfig );
+
+ ForceSingleThreaded();
+
+ ShaderDeviceInfo_t info;
+ ConvertModeStruct( &info, config );
+
+ bool bPreviouslyUsingGraphics = g_pShaderDevice->IsUsingGraphics();
+ if( config.m_nVRModeAdapter != -1 && config.m_nVRModeAdapter < GetDisplayAdapterCount() && !bPreviouslyUsingGraphics )
+ {
+ // if this is init-time, we need to override the adapter with the
+ // VR mode adapter
+ m_nAdapter = config.m_nVRModeAdapter;
+ }
+
+ bool bOk = g_pShaderAPI->SetMode( hwnd, m_nAdapter, info );
+ if ( !bOk )
+ return false;
+
+#if defined( USE_SDL )
+ uint width = info.m_DisplayMode.m_nWidth;
+ uint height = info.m_DisplayMode.m_nHeight;
+ g_pLauncherMgr->RenderedSize( width, height, true ); // true = set
+#endif
+
+ TextureManager()->FreeStandardRenderTargets();
+ TextureManager()->AllocateStandardRenderTargets();
+
+ // FIXME: There's gotta be a better way of doing this?
+ // After the first mode set, make sure to download any textures created
+ // before the first mode set. After the first mode set, all textures
+ // will be reloaded via the reaquireresources call. Same goes for procedural materials
+ if ( !bPreviouslyUsingGraphics )
+ {
+ if ( IsPC() )
+ {
+ TextureManager()->RestoreRenderTargets();
+ TextureManager()->RestoreNonRenderTargetTextures();
+ if ( MaterialSystem()->CanUseEditorMaterials() )
+ {
+ // We are in Hammer. Allocate these here since we aren't going to allocate
+ // lightmaps.
+ // HACK!
+ // NOTE! : Overbright is 1.0 so that Hammer will work properly with the white bumped and unbumped lightmaps.
+ MathLib_Init( 2.2f, 2.2f, 0.0f, OVERBRIGHT );
+ }
+
+ AllocateStandardTextures();
+ TextureManager()->WarmTextureCache();
+ }
+
+ if ( IsX360() )
+ {
+ // shaderapi was not viable at init time, it is now
+ TextureManager()->ReloadTextures();
+ AllocateStandardTextures();
+ }
+ }
+
+ g_pShaderDevice->SetHardwareGammaRamp( config.m_fMonitorGamma, config.m_fGammaTVRangeMin, config.m_fGammaTVRangeMax,
+ config.m_fGammaTVExponent, config.m_bGammaTVEnabled );
+
+ // Copy over that state which isn't stored currently in convars
+ g_config.m_VideoMode = config.m_VideoMode;
+ g_config.SetFlag( MATSYS_VIDCFG_FLAGS_WINDOWED, config.Windowed() );
+ g_config.SetFlag( MATSYS_VIDCFG_FLAGS_STENCIL, config.Stencil() );
+ g_config.SetFlag( MATSYS_VIDCFG_FLAGS_VR_MODE, config.VRMode() );
+ WriteConfigIntoConVars( config );
+
+ extern void SetupDirtyDiskReportFunc();
+ SetupDirtyDiskReportFunc();
+
+ return true;
+}
+
+// Creates/ destroys a child window
+bool CMaterialSystem::AddView( void* hwnd )
+{
+ return g_pShaderDevice->AddView( hwnd );
+}
+
+void CMaterialSystem::RemoveView( void* hwnd )
+{
+ g_pShaderDevice->RemoveView( hwnd );
+}
+
+// Activates a view
+void CMaterialSystem::SetView( void* hwnd )
+{
+ g_pShaderDevice->SetView( hwnd );
+}
+
+
+//-----------------------------------------------------------------------------
+// Installs a function to be called when we need to release vertex buffers
+//-----------------------------------------------------------------------------
+void CMaterialSystem::AddReleaseFunc( MaterialBufferReleaseFunc_t func )
+{
+ // Shouldn't have two copies in our list
+ Assert( m_ReleaseFunc.Find( func ) == -1 );
+ m_ReleaseFunc.AddToTail( func );
+}
+
+void CMaterialSystem::RemoveReleaseFunc( MaterialBufferReleaseFunc_t func )
+{
+ int i = m_ReleaseFunc.Find( func );
+ if( i != -1 )
+ m_ReleaseFunc.Remove(i);
+}
+
+
+//-----------------------------------------------------------------------------
+// Installs a function to be called when we need to restore vertex buffers
+//-----------------------------------------------------------------------------
+void CMaterialSystem::AddRestoreFunc( MaterialBufferRestoreFunc_t func )
+{
+ // Shouldn't have two copies in our list
+ Assert( m_RestoreFunc.Find( func ) == -1 );
+ m_RestoreFunc.AddToTail( func );
+}
+
+void CMaterialSystem::RemoveRestoreFunc( MaterialBufferRestoreFunc_t func )
+{
+ int i = m_RestoreFunc.Find( func );
+ Assert( i != -1 );
+ m_RestoreFunc.Remove(i);
+}
+
+
+//-----------------------------------------------------------------------------
+// Called by the shader API when it's just about to lose video memory
+//-----------------------------------------------------------------------------
+void CMaterialSystem::ReleaseShaderObjects()
+{
+ if( mat_debugalttab.GetBool() )
+ {
+ Warning( "mat_debugalttab: CMaterialSystem::ReleaseShaderObjects\n" );
+ }
+
+ m_HardwareRenderContext.OnReleaseShaderObjects();
+
+ g_pOcclusionQueryMgr->FreeOcclusionQueryObjects();
+ TextureManager()->ReleaseTextures();
+ ReleaseStandardTextures();
+ GetLightmaps()->ReleaseLightmapPages();
+ for (int i = 0; i < m_ReleaseFunc.Count(); ++i)
+ {
+ m_ReleaseFunc[i]();
+ }
+}
+
+void CMaterialSystem::RestoreShaderObjects( CreateInterfaceFn shaderFactory, int nChangeFlags )
+{
+ if ( shaderFactory )
+ {
+ g_pShaderAPI = (IShaderAPI*)shaderFactory( SHADERAPI_INTERFACE_VERSION, NULL );
+ g_pShaderDevice = (IShaderDevice*)shaderFactory( SHADER_DEVICE_INTERFACE_VERSION, NULL );
+ g_pShaderShadow = (IShaderShadow*)shaderFactory( SHADERSHADOW_INTERFACE_VERSION, NULL );
+ }
+
+ for( MaterialHandle_t i = m_MaterialDict.FirstMaterial(); i != m_MaterialDict.InvalidMaterial(); i = m_MaterialDict.NextMaterial( i ) )
+ {
+ IMaterialInternal *pMat = m_MaterialDict.GetMaterialInternal( i );
+ if ( pMat )
+ pMat->ReportVarChanged( NULL );
+ }
+
+
+ if ( mat_debugalttab.GetBool() )
+ {
+ Warning( "mat_debugalttab: CMaterialSystem::RestoreShaderObjects\n" );
+ }
+ // Shader API sets this to the max value the card supports when it resets
+ // the state, so restore this value.
+ g_pShaderAPI->SetAnisotropicLevel( GetCurrentConfigForVideoCard().m_nForceAnisotropicLevel );
+
+ // NOTE: render targets must be restored first, then vb/ibs, then managed textures
+ // FIXME: Gotta restore lightmap pages + standard textures before restore funcs are called
+ // because they use them both.
+ TextureManager()->RestoreRenderTargets();
+ AllocateStandardTextures();
+ GetLightmaps()->RestoreLightmapPages();
+ g_pOcclusionQueryMgr->AllocOcclusionQueryObjects();
+ for (int i = 0; i < m_RestoreFunc.Count(); ++i)
+ {
+ m_RestoreFunc[i]( nChangeFlags );
+ }
+ TextureManager()->RestoreNonRenderTargetTextures( );
+}
+
+
+//-----------------------------------------------------------------------------
+// Use this to spew information about the 3D layer
+//-----------------------------------------------------------------------------
+void CMaterialSystem::SpewDriverInfo() const
+{
+ Warning( "ShaderAPI: %s\n", m_pShaderDLL );
+ g_pShaderDevice->SpewDriverInfo();
+}
+
+
+//-----------------------------------------------------------------------------
+// Color converting blitter
+//-----------------------------------------------------------------------------
+
+bool CMaterialSystem::ConvertImageFormat( unsigned char *src, enum ImageFormat srcImageFormat,
+ unsigned char *dst, enum ImageFormat dstImageFormat,
+ int width, int height, int srcStride, int dstStride )
+{
+ return ImageLoader::ConvertImageFormat( src, srcImageFormat,
+ dst, dstImageFormat, width, height, srcStride, dstStride );
+}
+
+//-----------------------------------------------------------------------------
+// Figures out the amount of memory needed by a bitmap
+//-----------------------------------------------------------------------------
+int CMaterialSystem::GetMemRequired( int width, int height, int depth, ImageFormat format, bool mipmap )
+{
+ return ImageLoader::GetMemRequired( width, height, depth, format, mipmap );
+}
+
+
+//-----------------------------------------------------------------------------
+// Method to allow clients access to the MaterialSystem_Config
+//-----------------------------------------------------------------------------
+MaterialSystem_Config_t& CMaterialSystem::GetConfig()
+{
+//hushed Assert( m_bGeneratedConfig );
+ return g_config;
+}
+
+
+//-----------------------------------------------------------------------------
+// Gets image format info
+//-----------------------------------------------------------------------------
+ImageFormatInfo_t const& CMaterialSystem::ImageFormatInfo( ImageFormat fmt) const
+{
+ return ImageLoader::ImageFormatInfo(fmt);
+}
+
+
+//-----------------------------------------------------------------------------
+// Reads keyvalues for information
+//-----------------------------------------------------------------------------
+static void ReadInt( KeyValues *pGroup, const char *pName, int nDefaultVal, int nUndefinedVal, int *pDest )
+{
+ *pDest = pGroup->GetInt( pName, nDefaultVal );
+// Warning( "\t%s = %d\n", pName, *pDest );
+ Assert( *pDest != nUndefinedVal );
+}
+
+static void ReadFlt( KeyValues *pGroup, const char *pName, float flDefaultVal, float flUndefinedVal, float *pDest )
+{
+ *pDest = pGroup->GetFloat( pName, flDefaultVal );
+// Warning( "\t%s = %f\n", pName, *pDest );
+ Assert( *pDest != flUndefinedVal );
+}
+
+static void LoadFlags( KeyValues *pGroup, const char *pName, bool bDefaultValue, unsigned int nFlag, unsigned int *pFlags )
+{
+ int nValue = pGroup->GetInt( pName, bDefaultValue ? 1 : 0 );
+// Warning( "\t%s = %s\n", pName, nValue ? "true" : "false" );
+ if ( nValue )
+ {
+ *pFlags |= nFlag;
+ }
+}
+
+#define ASPECT_4x3 0
+#define ASPECT_16x9 1
+#define ASPECT_16x10 2
+
+
+//-----------------------------------------------------------------------------
+// Purpose: aspect ratio mappings (for normal/widescreen combo)
+//-----------------------------------------------------------------------------
+struct RatioToAspectMode_t
+{
+ int nAspectCode;
+ float flAspectRatio;
+};
+
+RatioToAspectMode_t g_RatioToAspectModes[] =
+{
+ { ASPECT_4x3, 4.0f / 3.0f },
+ { ASPECT_16x9, 16.0f / 9.0f },
+ { ASPECT_16x10, 16.0f / 10.0f },
+ { ASPECT_16x10, 1.0f },
+};
+
+//-----------------------------------------------------------------------------
+// Purpose: returns the aspect ratio mode number for the given resolution
+//-----------------------------------------------------------------------------
+int GetScreenAspectMode( int width, int height )
+{
+ float flAspectRatio = (float)width / (float)height;
+
+ // Just find the closest ratio
+ float flClosestAspectRatioDist = 99999.0f;
+ int nClosestAspectCode = ASPECT_4x3;
+ for ( int i = 0; i < ARRAYSIZE(g_RatioToAspectModes); i++ )
+ {
+ float flDist = fabs( g_RatioToAspectModes[i].flAspectRatio - flAspectRatio );
+ if ( flDist < flClosestAspectRatioDist )
+ {
+ flClosestAspectRatioDist = flDist;
+ nClosestAspectCode = g_RatioToAspectModes[i].nAspectCode;
+ }
+ }
+
+ return nClosestAspectCode;
+}
+
+
+// Heuristic similar to one we put into L4D
+bool BetterResolution( int nRecommendedNumPixels, int nBestNumPixels, int nNewNumPixels )
+{
+ float flRecommendedNumPixels = (float) nRecommendedNumPixels;
+ float flBestNumPixels = (float) nBestNumPixels;
+ float flNewNumPixels = (float) nNewNumPixels;
+
+ // Give ourselves a little head room
+ float flTooBig = flRecommendedNumPixels * 1.1f;
+
+ // If our best is too big and the new resolution is no bigger, pick it
+ if ( ( flBestNumPixels > flTooBig ) && ( flNewNumPixels < flBestNumPixels ) )
+ return true;
+
+ // Don't allow resolutions which are too big
+ if ( flNewNumPixels > flTooBig )
+ return false;
+
+ // Finally, just check for nearness to desired number of pixels
+ float flDelta = fabs( flRecommendedNumPixels - flNewNumPixels );
+ float flBestDelta = fabs( flRecommendedNumPixels - flBestNumPixels );
+ if ( flDelta >= flBestDelta )
+ return false;
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// This is called when the config changes
+//-----------------------------------------------------------------------------
+void CMaterialSystem::GenerateConfigFromConfigKeyValues( MaterialSystem_Config_t *pConfig, bool bOverwriteCommandLineValues )
+{
+ if ( !g_pShaderDeviceMgr || !pConfig )
+ return;
+
+ // Look for the default recommended dx support level
+ MaterialAdapterInfo_t adapterInfo;
+ g_pShaderDeviceMgr->GetAdapterInfo( m_nAdapter, adapterInfo );
+
+ pConfig->dxSupportLevel = MAX( ABSOLUTE_MINIMUM_DXLEVEL, adapterInfo.m_nDXSupportLevel );
+ KeyValues *pKeyValues = new KeyValues( "config" );
+ if ( !GetRecommendedConfigurationInfo( pConfig->dxSupportLevel, pKeyValues ) )
+ {
+ pKeyValues->deleteThis();
+ return;
+ }
+
+ pConfig->m_Flags = 0;
+
+#ifdef LINUX
+
+ uint width = 0;
+ uint height = 0;
+ uint refreshHz = 0; // Not used
+
+#ifdef USE_SDL
+ // query backbuffer size (window size whether FS or windowed)
+ if( g_pLauncherMgr )
+ {
+ g_pLauncherMgr->GetNativeDisplayInfo( -1, width, height, refreshHz );
+ }
+#endif
+
+ pConfig->m_VideoMode.m_Width = width;
+ pConfig->m_VideoMode.m_Height = height;
+
+#else
+
+ // Get the recommended resolution from dxsupport.cfg, this assumes a 4:3 aspect ratio
+ int nRecommendedWidth, nRecommendedHeight;
+ ReadInt( pKeyValues, "DefaultRes", 640, -1, &nRecommendedWidth );
+ nRecommendedHeight = ( nRecommendedWidth * 3 ) / 4;
+ int nRecommendedPixels = nRecommendedHeight * nRecommendedWidth;
+
+ // Get the desktop resolution and aspect ratio
+ ShaderDisplayMode_t displayMode;
+ g_pShaderDeviceMgr->GetCurrentModeInfo( &displayMode, 0 );
+ int nCurrentScreenAspect = GetScreenAspectMode( displayMode.m_nWidth, displayMode.m_nHeight );
+
+ // Let's see what the device supports and pick the most appropriate mode
+ g_pShaderDeviceMgr->GetModeInfo( &displayMode, 0, 0 );
+ int nBestMode, nBestWidth, nBestHeight;
+ nBestMode = nBestWidth = nBestHeight = -1;
+ int nBestPixels = displayMode.m_nHeight * displayMode.m_nWidth;
+
+ int nNumVideoModes = g_pShaderDeviceMgr->GetModeCount( 0 );
+
+ // Pick the resolution with the right aspect ratio which matches the recommended resolution most closely
+ for ( int i=0; i<nNumVideoModes; i++ )
+ {
+ g_pShaderDeviceMgr->GetModeInfo( &displayMode, 0, i );
+
+ if ( nCurrentScreenAspect == GetScreenAspectMode( displayMode.m_nWidth, displayMode.m_nHeight ) )
+ {
+ int nNumPixels = displayMode.m_nWidth * displayMode.m_nHeight;
+
+ // Initially select the first mode we find of the correct aspect ratio for the display
+ if ( ( nBestMode == -1) || BetterResolution( nRecommendedPixels, nBestPixels, nNumPixels ) )
+ {
+ nBestMode = i;
+ nBestPixels = nNumPixels;
+ nBestWidth = displayMode.m_nWidth;
+ nBestHeight = displayMode.m_nHeight;
+ }
+ }
+ }
+
+ // We found a good mode
+ if ( nBestMode != -1 )
+ {
+ pConfig->m_VideoMode.m_Width = nBestWidth;
+ pConfig->m_VideoMode.m_Height = nBestHeight;
+ }
+ else // Fall back to 4:3 mode from the cfg file. This should never happen
+ {
+ pConfig->m_VideoMode.m_Width = nRecommendedWidth;
+ pConfig->m_VideoMode.m_Height = nRecommendedHeight;
+ }
+
+#if defined( _X360 )
+ pConfig->m_VideoMode.m_Width = GetSystemMetrics( SM_CXSCREEN );
+ pConfig->m_VideoMode.m_Height = GetSystemMetrics( SM_CYSCREEN );
+#endif
+
+ pKeyValues->deleteThis();
+
+#endif // LINUX
+
+ WriteConfigurationInfoToConVars( bOverwriteCommandLineValues );
+ m_bGeneratedConfig = true;
+}
+
+//-----------------------------------------------------------------------------
+// If mat_proxy goes to 0, we need to reload all materials, because their shader
+// params might have been messed with.
+//-----------------------------------------------------------------------------
+static void MatProxyCallback( IConVar *pConVar, const char *old, float flOldValue )
+{
+ ConVarRef var( pConVar );
+ int oldVal = (int)flOldValue;
+ if ( var.GetInt() == 0 && oldVal != 0 )
+ {
+ g_MaterialSystem.ReloadMaterials();
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Convars that control the config record
+//-----------------------------------------------------------------------------
+static ConVar mat_vsync( "mat_vsync", "0", FCVAR_ALLOWED_IN_COMPETITIVE, "Force sync to vertical retrace", true, 0.0, true, 1.0 );
+static ConVar mat_forcehardwaresync( "mat_forcehardwaresync", IsPC() ? "1" : "0", FCVAR_ALLOWED_IN_COMPETITIVE );
+
+// Texture-related
+static ConVar mat_trilinear( "mat_trilinear", "0", FCVAR_ALLOWED_IN_COMPETITIVE );
+#ifdef _X360 // The code that reads this out of moddefaults.txt is #if'd out for the 360, so force aniso to 2 here.
+ static ConVar mat_forceaniso( "mat_forceaniso", "2", FCVAR_ARCHIVE ); // 0 = Bilinear, 1 = Trilinear, 2+ = Aniso
+#elif defined ( OSX )
+ static ConVar mat_forceaniso( "mat_forceaniso", "1", FCVAR_ARCHIVE, "Filtering level", true, 0, true, 8 ); // 0 = Bilinear, 1 = Trilinear, 2+ = Aniso
+#else
+ static ConVar mat_forceaniso( "mat_forceaniso", "1", FCVAR_ARCHIVE ); // 0 = Bilinear, 1 = Trilinear, 2+ = Aniso
+#endif
+static ConVar mat_filterlightmaps( "mat_filterlightmaps", "1" );
+static ConVar mat_filtertextures( "mat_filtertextures", "1" );
+static ConVar mat_mipmaptextures( "mat_mipmaptextures", "1" );
+static ConVar mat_vrmode_adapter( "mat_vrmode_adapter", "-1" );
+
+static void mat_showmiplevels_Callback_f( IConVar *var, const char *pOldValue, float flOldValue )
+{
+ // turn off texture filtering if we are showing mip levels.
+ mat_filtertextures.SetValue( ( ( ConVar * )var )->GetInt() == 0 );
+}
+// Debugging textures
+static ConVar mat_showmiplevels( "mat_showmiplevels", "0", FCVAR_CHEAT, "color-code miplevels 2: normalmaps, 1: everything else", mat_showmiplevels_Callback_f );
+
+static ConVar mat_specular( "mat_specular", "1", FCVAR_ALLOWED_IN_COMPETITIVE, "Enable/Disable specularity for perf testing. Will cause a material reload upon change." );
+static ConVar mat_bumpmap( "mat_bumpmap", "1", FCVAR_ALLOWED_IN_COMPETITIVE );
+static ConVar mat_phong( "mat_phong", "1" );
+static ConVar mat_parallaxmap( "mat_parallaxmap", "1", FCVAR_HIDDEN | FCVAR_ALLOWED_IN_COMPETITIVE );
+static ConVar mat_reducefillrate( "mat_reducefillrate", "0", FCVAR_ALLOWED_IN_COMPETITIVE );
+
+#if defined( OSX ) && !defined( STAGING_ONLY ) && !defined( _DEBUG )
+// OSX users are currently running OOM. We limit them to texture quality high here, which avoids the problem while we come up with a real solution.
+static ConVar mat_picmip( "mat_picmip", "1", FCVAR_ARCHIVE, "", true, 0, true, 4 );
+#else
+static ConVar mat_picmip( "mat_picmip", "0", FCVAR_ARCHIVE, "", true, -1, true, 4 );
+#endif
+static ConVar mat_slopescaledepthbias_normal( "mat_slopescaledepthbias_normal", "0.0f", FCVAR_CHEAT );
+static ConVar mat_depthbias_normal( "mat_depthbias_normal", "0.0f", FCVAR_CHEAT | FCVAR_ALLOWED_IN_COMPETITIVE );
+static ConVar mat_slopescaledepthbias_decal( "mat_slopescaledepthbias_decal", "-0.5", FCVAR_CHEAT ); // Reciprocals of these biases sent to API
+static ConVar mat_depthbias_decal( "mat_depthbias_decal", "-262144", FCVAR_CHEAT | FCVAR_ALLOWED_IN_COMPETITIVE ); //
+
+static ConVar mat_slopescaledepthbias_shadowmap( "mat_slopescaledepthbias_shadowmap", "16", FCVAR_CHEAT );
+static ConVar mat_depthbias_shadowmap( "mat_depthbias_shadowmap", "0.0005", FCVAR_CHEAT );
+
+static ConVar mat_monitorgamma( "mat_monitorgamma", "2.2", FCVAR_ARCHIVE, "monitor gamma (typically 2.2 for CRT and 1.7 for LCD)", true, 1.6f, true, 2.6f );
+static ConVar mat_monitorgamma_tv_range_min( "mat_monitorgamma_tv_range_min", "16" );
+static ConVar mat_monitorgamma_tv_range_max( "mat_monitorgamma_tv_range_max", "255" );
+// TV's generally have a 2.5 gamma, so we need to convert our 2.2 frame buffer into a 2.5 frame buffer for display on a TV
+static ConVar mat_monitorgamma_tv_exp( "mat_monitorgamma_tv_exp", "2.5", 0, "", true, 1.0f, true, 4.0f );
+#ifdef _X360
+static ConVar mat_monitorgamma_tv_enabled( "mat_monitorgamma_tv_enabled", "1", FCVAR_ARCHIVE, "" );
+#else
+static ConVar mat_monitorgamma_tv_enabled( "mat_monitorgamma_tv_enabled", "0", FCVAR_ARCHIVE, "" );
+#endif
+
+static ConVar mat_antialias( "mat_antialias", "0", FCVAR_ARCHIVE );
+static ConVar mat_aaquality( "mat_aaquality", "0", FCVAR_ARCHIVE );
+static ConVar mat_diffuse( "mat_diffuse", "1", FCVAR_CHEAT );
+//=============================================================================
+// HPE_BEGIN:
+// [Forrest] Make this a cheat variable because low res textures makes enemy
+// players and bullet impacts stand out more.
+//=============================================================================
+static ConVar mat_showlowresimage( "mat_showlowresimage", "0", FCVAR_CHEAT );
+//=============================================================================
+// HPE_END
+//=============================================================================
+static ConVar mat_fullbright( "mat_fullbright","0", FCVAR_CHEAT );
+static ConVar mat_normalmaps( "mat_normalmaps", "0", FCVAR_CHEAT );
+static ConVar mat_measurefillrate( "mat_measurefillrate", "0", FCVAR_CHEAT );
+static ConVar mat_fillrate( "mat_fillrate", "0", FCVAR_CHEAT );
+static ConVar mat_reversedepth( "mat_reversedepth", "0", FCVAR_CHEAT );
+#ifdef DX_TO_GL_ABSTRACTION
+static ConVar mat_bufferprimitives( "mat_bufferprimitives", "0" ); // I'm not seeing any benefit speed wise for buffered primitives on GLM/POSIX (checked via TF2 timedemo) - default to zero
+#else
+static ConVar mat_bufferprimitives( "mat_bufferprimitives", "1" );
+#endif
+static ConVar mat_drawflat( "mat_drawflat","0", FCVAR_CHEAT );
+static ConVar mat_softwarelighting( "mat_softwarelighting", "0", FCVAR_ALLOWED_IN_COMPETITIVE );
+static ConVar mat_proxy( "mat_proxy", "0", FCVAR_CHEAT, "", MatProxyCallback );
+static ConVar mat_norendering( "mat_norendering", "0", FCVAR_CHEAT );
+static ConVar mat_compressedtextures( "mat_compressedtextures", "1" );
+static ConVar mat_fastspecular( "mat_fastspecular", "1", 0, "Enable/Disable specularity for visual testing. Will not reload materials and will not affect perf." );
+static ConVar mat_fastnobump( "mat_fastnobump", "0", FCVAR_CHEAT ); // Binds 1-texel normal map for quick internal testing
+
+// These are not controlled by the material system, but are limited by settings in the material system
+static ConVar r_shadowrendertotexture( "r_shadowrendertotexture", "0", FCVAR_ARCHIVE );
+static ConVar r_flashlightdepthtexture( "r_flashlightdepthtexture", "1" );
+#ifndef _X360
+static ConVar r_waterforceexpensive( "r_waterforceexpensive", "0", FCVAR_ARCHIVE );
+#endif
+static ConVar r_waterforcereflectentities( "r_waterforcereflectentities", "0", FCVAR_ALLOWED_IN_COMPETITIVE );
+static ConVar mat_motion_blur_enabled( "mat_motion_blur_enabled", "0", FCVAR_ARCHIVE );
+
+
+uint32 g_nDebugVarsSignature = 0;
+
+
+//-----------------------------------------------------------------------------
+// This is called when the config changes
+//-----------------------------------------------------------------------------
+void CMaterialSystem::ReadConfigFromConVars( MaterialSystem_Config_t *pConfig )
+{
+ if ( !g_pCVar )
+ return;
+
+ // video panel config items
+#ifndef CSS_PERF_TEST
+ pConfig->SetFlag( MATSYS_VIDCFG_FLAGS_NO_WAIT_FOR_VSYNC, !mat_vsync.GetBool() );
+#endif
+ pConfig->SetFlag( MATSYS_VIDCFG_FLAGS_FORCE_TRILINEAR, mat_trilinear.GetBool() );
+ pConfig->SetFlag( MATSYS_VIDCFG_FLAGS_DISABLE_SPECULAR, !mat_specular.GetBool() );
+ pConfig->SetFlag( MATSYS_VIDCFG_FLAGS_DISABLE_BUMPMAP, !mat_bumpmap.GetBool() );
+ pConfig->SetFlag( MATSYS_VIDCFG_FLAGS_DISABLE_PHONG, !mat_phong.GetBool() );
+ pConfig->SetFlag( MATSYS_VIDCFG_FLAGS_ENABLE_PARALLAX_MAPPING, mat_parallaxmap.GetBool() );
+ pConfig->SetFlag( MATSYS_VIDCFG_FLAGS_REDUCE_FILLRATE, mat_reducefillrate.GetBool() );
+ pConfig->m_nForceAnisotropicLevel = max( mat_forceaniso.GetInt(), 1 );
+ pConfig->dxSupportLevel = MAX( ABSOLUTE_MINIMUM_DXLEVEL, mat_dxlevel.GetInt() );
+ pConfig->skipMipLevels = mat_picmip.GetInt();
+ pConfig->SetFlag( MATSYS_VIDCFG_FLAGS_FORCE_HWSYNC, mat_forcehardwaresync.GetBool() );
+ pConfig->m_SlopeScaleDepthBias_Decal = mat_slopescaledepthbias_decal.GetFloat();
+ pConfig->m_DepthBias_Decal = mat_depthbias_decal.GetFloat();
+ pConfig->m_SlopeScaleDepthBias_Normal = mat_slopescaledepthbias_normal.GetFloat();
+ pConfig->m_DepthBias_Normal = mat_depthbias_normal.GetFloat();
+ pConfig->m_SlopeScaleDepthBias_ShadowMap = mat_slopescaledepthbias_shadowmap.GetFloat();
+ pConfig->m_DepthBias_ShadowMap = mat_depthbias_shadowmap.GetFloat();
+
+ pConfig->m_fMonitorGamma = mat_monitorgamma.GetFloat();
+ pConfig->m_fGammaTVRangeMin = mat_monitorgamma_tv_range_min.GetFloat();
+ pConfig->m_fGammaTVRangeMax = mat_monitorgamma_tv_range_max.GetFloat();
+ pConfig->m_fGammaTVExponent = mat_monitorgamma_tv_exp.GetFloat();
+ pConfig->m_bGammaTVEnabled = mat_monitorgamma_tv_enabled.GetBool();
+
+ pConfig->m_nAASamples = mat_antialias.GetInt();
+ pConfig->m_nAAQuality = mat_aaquality.GetInt();
+ pConfig->bShowDiffuse = mat_diffuse.GetInt() ? true : false;
+// pConfig->bAllowCheats = false; // hack
+ pConfig->bShowNormalMap = mat_normalmaps.GetInt() ? true : false;
+ pConfig->bShowLowResImage = mat_showlowresimage.GetInt() ? true : false;
+ pConfig->bMeasureFillRate = mat_measurefillrate.GetInt() ? true : false;
+ pConfig->bVisualizeFillRate = mat_fillrate.GetInt() ? true : false;
+ pConfig->bFilterLightmaps = mat_filterlightmaps.GetInt() ? true : false;
+ pConfig->bFilterTextures = mat_filtertextures.GetInt() ? true : false;
+ pConfig->bMipMapTextures = mat_mipmaptextures.GetInt() ? true : false;
+ pConfig->nShowMipLevels = mat_showmiplevels.GetInt();
+ pConfig->bReverseDepth = mat_reversedepth.GetInt() ? true : false;
+ pConfig->bBufferPrimitives = mat_bufferprimitives.GetInt() ? true : false;
+ pConfig->bDrawFlat = mat_drawflat.GetInt() ? true : false;
+ pConfig->bSoftwareLighting = mat_softwarelighting.GetInt() ? true : false;
+ pConfig->proxiesTestMode = mat_proxy.GetInt();
+ pConfig->m_bSuppressRendering = mat_norendering.GetInt() != 0;
+ pConfig->bCompressedTextures = mat_compressedtextures.GetBool();
+ pConfig->bShowSpecular = mat_fastspecular.GetInt() ? true : false;
+ pConfig->nFullbright = mat_fullbright.GetInt();
+ pConfig->m_bFastNoBump = mat_fastnobump.GetInt() != 0;
+ pConfig->m_bMotionBlur = mat_motion_blur_enabled.GetBool();
+ pConfig->m_bSupportFlashlight = mat_supportflashlight.GetInt() != 0;
+ pConfig->m_bShadowDepthTexture = r_flashlightdepthtexture.GetBool();
+
+ pConfig->SetFlag( MATSYS_VIDCFG_FLAGS_ENABLE_HDR, HardwareConfig() && HardwareConfig()->GetHDREnabled() );
+
+ // Render-to-texture shadows are disabled for dxlevel 70 because of material issues
+ if ( pConfig->dxSupportLevel < 80 )
+ {
+ r_shadowrendertotexture.SetValue( 0 );
+#ifndef _X360
+ r_waterforceexpensive.SetValue( 0 );
+#endif
+ r_waterforcereflectentities.SetValue( 0 );
+ }
+ if ( pConfig->dxSupportLevel < 90 )
+ {
+ mat_requires_rt_alloc_first.SetValue( 1 );
+ r_flashlightdepthtexture.SetValue( 0 );
+ mat_motion_blur_enabled.SetValue( 0 );
+ pConfig->m_bShadowDepthTexture = false;
+ pConfig->m_bMotionBlur = false;
+ pConfig->SetFlag( MATSYS_VIDCFG_FLAGS_ENABLE_HDR, false );
+ }
+
+ // VR mode adapter will generally be -1 if VR mode is not disabled
+ pConfig->m_nVRModeAdapter = mat_vrmode_adapter.GetInt();
+ if( pConfig->m_nVRModeAdapter != -1 )
+ {
+ // we must always be windowed in the config in VR mode
+ // so that we will start up on the main display. Once
+ // VR overrides the adapter the only place we can go
+ // full screen is on the HMD.
+ pConfig->SetFlag( MATSYS_VIDCFG_FLAGS_WINDOWED, true );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Was the convar specified on the command-line?
+//-----------------------------------------------------------------------------
+static bool WasConVarSpecifiedOnCommandLine( const char *pConfigName )
+{
+ // mat_dxlevel cannot be used on the command-line. Use -dxlevel instead.
+ if ( !Q_stricmp( pConfigName, "mat_dxlevel" ) )
+ return false;
+
+ return ( g_pCVar->GetCommandLineValue( pConfigName ) != NULL);
+}
+
+
+static const char *pConvarsAllowedInDXSupport[]={
+ "cl_detaildist",
+ "cl_detailfade",
+ "cl_ejectbrass",
+ "dsp_off",
+ "dsp_slow_cpu",
+ "mat_antialias",
+ "mat_aaquality",
+ "mat_bumpmap",
+ "mat_colorcorrection",
+ "mat_depthbias_decal",
+ "mat_depthbias_normal",
+ "mat_disable_ps_patch",
+ "mat_forceaniso",
+ "mat_forcehardwaresync",
+ "mat_forcemanagedtextureintohardware",
+ "mat_hdr_level",
+ "mat_parallaxmap",
+ "mat_picmip",
+ "mat_reducefillrate",
+ "mat_reduceparticles",
+ "mat_slopescaledepthbias_decal",
+ "mat_slopescaledepthbias_normal",
+ "mat_softwarelighting",
+ "mat_specular",
+ "mat_trilinear",
+ "mat_vsync",
+ "props_break_max_pieces",
+ "r_VehicleViewDampen",
+ "r_decal_cullsize",
+ "r_dopixelvisibility",
+ "r_drawdetailprops",
+ "r_drawflecks",
+ "r_drawmodeldecals",
+ "r_dynamic",
+ "r_lightcache_zbuffercache",
+ "r_fastzreject",
+ "r_overlayfademax",
+ "r_overlayfademin",
+ "r_rootlod",
+ "r_screenfademaxsize",
+ "r_screenfademinsize",
+ "r_shadowrendertotexture",
+ "r_shadows",
+ "r_waterforceexpensive",
+ "r_waterforcereflectentities",
+ "sv_alternateticks",
+ "mat_dxlevel",
+ "mat_fallbackEyeRefract20",
+ "r_shader_srgb",
+ "mat_motion_blur_enabled",
+ "r_flashlightdepthtexture",
+ "mat_disablehwmorph",
+ "r_portal_stencil_depth",
+ "cl_blobbyshadows",
+ "r_flex",
+ "r_drawropes",
+ "props_break_max_pieces",
+ "cl_ragdoll_fade_time",
+ "cl_ragdoll_forcefade",
+ "tf_impactwatertimeenable",
+ "fx_drawimpactdebris",
+ "fx_drawimpactdust",
+ "fx_drawmetalspark",
+ "mem_min_heapsize",
+ "mem_max_heapsize",
+ "mem_max_heapsize_dedicated",
+ "snd_spatialize_roundrobin",
+ "snd_cull_duplicates",
+ "cl_particle_retire_cost",
+ "mat_phong"
+};
+
+//-----------------------------------------------------------------------------
+// Write dxsupport info to configvars
+//-----------------------------------------------------------------------------
+void CMaterialSystem::WriteConfigurationInfoToConVars( bool bOverwriteCommandLineValues )
+{
+ if ( !g_pCVar )
+ return;
+
+ KeyValues *pKeyValues = new KeyValues( "config" );
+ if ( !GetRecommendedConfigurationInfo( g_config.dxSupportLevel, pKeyValues ) )
+ {
+ pKeyValues->deleteThis();
+ return;
+ }
+
+ for( KeyValues *pKey = pKeyValues->GetFirstSubKey(); pKey; pKey = pKey->GetNextKey() )
+ {
+ const char *pConfigName = pKey->GetName();
+ if ( Q_strnicmp( pConfigName, "convar.", 7 ))
+ continue;
+
+ pConfigName += 7;
+ // check if legal
+ bool bLegalVar = false;
+ for(int i=0; i< NELEMS( pConvarsAllowedInDXSupport ) ; i++)
+ {
+ if (! stricmp( pConvarsAllowedInDXSupport[i], pConfigName ) )
+ {
+ bLegalVar = true;
+ break;
+ }
+ }
+ if (! bLegalVar )
+ {
+ Msg(" Bad convar found in dxsupport - %s\n", pConfigName );
+ continue;
+ }
+
+ if ( bOverwriteCommandLineValues || !WasConVarSpecifiedOnCommandLine( pConfigName ) )
+ {
+ ConVar *pConVar = g_pCVar->FindVar( pConfigName );
+ if ( !pConVar )
+ {
+ // NOTE: This is essential for dealing with convars that
+ // are not specified in either the app that uses the materialsystem
+ // or the materialsystem itself
+
+ // Yes, this causes a memory leak. Too bad!
+ int nLen = Q_strlen( pConfigName ) + 1;
+ char *pString = new char[nLen];
+ Q_strncpy( pString, pConfigName, nLen );
+
+ // Actually, we need two memory leaks, or we lose the default string.
+ int nDefaultLen = Q_strlen( pKey->GetString() ) + 1;
+ char *pDefaultString = new char[nDefaultLen];
+ Q_strncpy( pDefaultString, pKey->GetString(), nDefaultLen );
+
+ pConVar = new ConVar( pString, pDefaultString );
+ }
+ pConVar->SetValue( pKey->GetString() );
+ }
+ }
+
+ pKeyValues->deleteThis();
+}
+
+
+//-----------------------------------------------------------------------------
+// This is called when the config changes
+//-----------------------------------------------------------------------------
+void CMaterialSystem::WriteConfigIntoConVars( const MaterialSystem_Config_t &config )
+{
+ if ( !g_pCVar )
+ return;
+
+ mat_vsync.SetValue( config.WaitForVSync() );
+ mat_trilinear.SetValue( config.ForceTrilinear() );
+ mat_specular.SetValue( config.UseSpecular() );
+ mat_bumpmap.SetValue( config.UseBumpmapping() );
+ mat_phong.SetValue( config.UsePhong() );
+ mat_parallaxmap.SetValue( config.UseParallaxMapping() );
+ mat_reducefillrate.SetValue( config.ReduceFillrate() );
+ mat_forceaniso.SetValue( config.m_nForceAnisotropicLevel );
+ mat_dxlevel.SetValue( MAX( ABSOLUTE_MINIMUM_DXLEVEL, config.dxSupportLevel ) );
+ mat_picmip.SetValue( config.skipMipLevels );
+ mat_forcehardwaresync.SetValue( config.ForceHWSync() );
+ mat_slopescaledepthbias_normal.SetValue( config.m_SlopeScaleDepthBias_Normal );
+ mat_depthbias_normal.SetValue( config.m_DepthBias_Normal );
+ mat_slopescaledepthbias_decal.SetValue( config.m_SlopeScaleDepthBias_Decal );
+ mat_depthbias_decal.SetValue( config.m_DepthBias_Decal );
+ mat_slopescaledepthbias_shadowmap.SetValue( config.m_SlopeScaleDepthBias_ShadowMap );
+ mat_depthbias_shadowmap.SetValue( config.m_DepthBias_ShadowMap );
+
+ mat_monitorgamma.SetValue( config.m_fMonitorGamma );
+ mat_monitorgamma_tv_range_min.SetValue( config.m_fGammaTVRangeMin );
+ mat_monitorgamma_tv_range_max.SetValue( config.m_fGammaTVRangeMax );
+ mat_monitorgamma_tv_exp.SetValue( config.m_fGammaTVExponent );
+ mat_monitorgamma_tv_enabled.SetValue( config.m_bGammaTVEnabled );
+
+ mat_antialias.SetValue( config.m_nAASamples );
+ mat_aaquality.SetValue( config.m_nAAQuality );
+ mat_diffuse.SetValue( config.bShowDiffuse ? 1 : 0 );
+// config.bAllowCheats = false; // hack
+ mat_normalmaps.SetValue( config.bShowNormalMap ? 1 : 0 );
+ mat_showlowresimage.SetValue( config.bShowLowResImage ? 1 : 0 );
+ mat_measurefillrate.SetValue( config.bMeasureFillRate ? 1 : 0 );
+ mat_fillrate.SetValue( config.bVisualizeFillRate ? 1 : 0 );
+ mat_filterlightmaps.SetValue( config.bFilterLightmaps ? 1 : 0 );
+ mat_filtertextures.SetValue( config.bFilterTextures ? 1 : 0 );
+ mat_mipmaptextures.SetValue( config.bMipMapTextures ? 1 : 0 );
+ mat_showmiplevels.SetValue( config.nShowMipLevels );
+ mat_reversedepth.SetValue( config.bReverseDepth ? 1 : 0 );
+ mat_bufferprimitives.SetValue( config.bBufferPrimitives ? 1 : 0 );
+ mat_drawflat.SetValue( config.bDrawFlat ? 1 : 0 );
+ mat_softwarelighting.SetValue( config.bSoftwareLighting ? 1 : 0 );
+ mat_proxy.SetValue( config.proxiesTestMode );
+ mat_norendering.SetValue( config.m_bSuppressRendering ? 1 : 0 );
+ mat_compressedtextures.SetValue( config.bCompressedTextures ? 1 : 0 );
+ mat_fastspecular.SetValue( config.bShowSpecular ? 1 : 0 );
+ mat_fullbright.SetValue( config.nFullbright );
+ mat_fastnobump.SetValue( config.m_bFastNoBump ? 1 : 0 );
+ bool hdre = config.HDREnabled();
+ HardwareConfig()->SetHDREnabled( hdre );
+ r_flashlightdepthtexture.SetValue( config.m_bShadowDepthTexture ? 1 : 0 );
+ mat_motion_blur_enabled.SetValue( config.m_bMotionBlur ? 1 : 0 );
+ mat_supportflashlight.SetValue( config.m_bSupportFlashlight ? 1 : 0 );
+}
+
+
+//-----------------------------------------------------------------------------
+// This is called constantly to catch for config changes
+//-----------------------------------------------------------------------------
+bool CMaterialSystem::OverrideConfig( const MaterialSystem_Config_t &_config, bool forceUpdate )
+{
+ Assert( m_bGeneratedConfig );
+ if ( memcmp( &_config, &g_config, sizeof(_config) ) == 0 )
+ {
+ return false;
+ }
+
+ MaterialLock_t hLock = Lock();
+ MaterialSystem_Config_t config = _config;
+
+ bool bRedownloadLightmaps = false;
+ bool bRedownloadTextures = false;
+ bool recomputeSnapshots = false;
+ bool dxSupportLevelChanged = false;
+ bool bReloadMaterials = false;
+ bool bResetAnisotropy = false;
+ bool bSetStandardVertexShaderConstants = false;
+ bool bMonitorGammaChanged = false;
+ bool bVideoModeChange = false;
+ bool bResetTextureFilter = false;
+ bool bForceAltTab = false;
+
+ // internal config settings
+#ifndef _X360
+ MaterialSystem_Config_Internal_t config_internal;
+ config_internal.r_waterforceexpensive = r_waterforceexpensive.GetInt();
+#endif
+
+ if ( !g_pShaderDevice->IsUsingGraphics() )
+ {
+ g_config = config;
+#ifndef _X360
+ g_config_internal = config_internal;
+#endif
+
+ // Shouldn't call this more than once.
+ ColorSpace::SetGamma( 2.2f, 2.2f, OVERBRIGHT, g_config.bAllowCheats, false );
+ Unlock( hLock );
+ return bRedownloadLightmaps;
+ }
+
+ // set the default state since we might be changing the number of
+ // texture units, etc. (i.e. we don't want to leave unit 2 in overbright mode
+ // if it isn't going to be reset upon each SetDefaultState because there is
+ // effectively only one texture unit.)
+ g_pShaderAPI->SetDefaultState();
+
+ // toggle dx emulation level
+ if ( config.dxSupportLevel != g_config.dxSupportLevel )
+ {
+ if ( mat_debugalttab.GetBool() )
+ {
+ Warning( "mat_debugalttab: Setting dxSupportLevelChanged, bResetAnisotropy, and bReloadMaterials because new dxlevel = %d and old dxlevel = %d\n",
+ ( int )config.dxSupportLevel, g_config.dxSupportLevel );
+ }
+ dxSupportLevelChanged = true;
+ bResetAnisotropy = true;
+
+ // Necessary for DXSupportLevelChanged to work
+ g_config.dxSupportLevel = config.dxSupportLevel;
+
+ // This will reset config to match whatever the dxlevel wants
+ // and slam to convars to match
+ g_pShaderAPI->DXSupportLevelChanged( );
+ WriteConfigurationInfoToConVars();
+ ReadConfigFromConVars( &config );
+ bReloadMaterials = true;
+ }
+
+ if ( config.HDREnabled() != g_config.HDREnabled() )
+ {
+ if ( mat_debugalttab.GetBool() )
+ {
+ Warning( "mat_debugalttab: Setting forceUpdate, bReloadMaterials, and bForceAltTab because new hdr level = %d and old hdr level = %d\n",
+ ( int )config.HDREnabled(), g_config.HDREnabled() );
+ }
+
+ forceUpdate = true;
+ bReloadMaterials = true;
+ bForceAltTab = true;
+ }
+
+ if ( config.ShadowDepthTexture() != g_config.ShadowDepthTexture() )
+ {
+ if ( mat_debugalttab.GetBool() )
+ {
+ Warning( "mat_debugalttab: Setting forceUpdate, bReloadMaterials and recomputeSnapshots (ShadowDepthTexture changed: %d -> %d)\n",
+ g_config.ShadowDepthTexture() ? 1 : 0, config.ShadowDepthTexture() ? 1 : 0 );
+ }
+
+ forceUpdate = true;
+ bReloadMaterials = true;
+ recomputeSnapshots = true;
+ }
+
+ if ( config.VRMode() != g_config.VRMode() || config.m_nVRModeAdapter != g_config.m_nVRModeAdapter )
+ {
+ bVideoModeChange = true;
+ }
+
+ // Don't use compressed textures for the moment if we don't support them
+ if ( HardwareConfig() && !HardwareConfig()->SupportsCompressedTextures() )
+ {
+ config.bCompressedTextures = false;
+ }
+
+ if ( forceUpdate )
+ {
+ if ( mat_debugalttab.GetBool() )
+ {
+ Warning( "mat_debugalttab: forceUpdate is true, therefore setting recomputeSnapshots, bRedownloadLightmaps, bRedownloadTextures, bResetAnisotropy, and bSetStandardVertexShaderConstants\n" );
+ }
+ GetLightmaps()->EnableLightmapFiltering( config.bFilterLightmaps );
+ recomputeSnapshots = true;
+ bRedownloadLightmaps = true;
+ bRedownloadTextures = true;
+ bResetAnisotropy = true;
+ bSetStandardVertexShaderConstants = true;
+ }
+
+ // toggle bump mapping
+ if ( config.UseBumpmapping() != g_config.UseBumpmapping() || config.UsePhong() != g_config.UsePhong() )
+ {
+ if( mat_debugalttab.GetBool() )
+ {
+ Warning( "mat_debugalttab: forceUpdate is true, therefore setting recomputeSnapshots, bRedownloadLightmaps, bRedownloadTextures, bResetAnisotropy, and bSetStandardVertexShaderConstants\n" );
+ }
+ recomputeSnapshots = true;
+ bReloadMaterials = true;
+ bResetAnisotropy = true;
+ }
+
+ // toggle specularity
+ if ( config.UseSpecular() != g_config.UseSpecular() )
+ {
+ if( mat_debugalttab.GetBool() )
+ {
+ Warning( "mat_debugalttab: new usespecular=%d, old usespecular=%d, setting recomputeSnapshots, bReloadMaterials, and bResetAnisotropy\n",
+ ( int )config.UseSpecular(), ( int )g_config.UseSpecular() );
+ }
+ recomputeSnapshots = true;
+ bReloadMaterials = true;
+ bResetAnisotropy = true;
+ }
+
+ // toggle parallax mapping
+ if ( config.UseParallaxMapping() != g_config.UseParallaxMapping() )
+ {
+ if ( mat_debugalttab.GetBool() )
+ {
+ Warning( "mat_debugalttab: new UseParallaxMapping=%d, old UseParallaxMapping=%d, setting bReloadMaterials\n",
+ ( int )config.UseParallaxMapping(), ( int )g_config.UseParallaxMapping() );
+ }
+ bReloadMaterials = true;
+ }
+
+ // Reload materials if we want reduced fillrate
+ if ( config.ReduceFillrate() != g_config.ReduceFillrate() )
+ {
+ if ( mat_debugalttab.GetBool() )
+ {
+ Warning( "mat_debugalttab: new ReduceFillrate=%d, old ReduceFillrate=%d, setting bReloadMaterials\n",
+ ( int )config.ReduceFillrate(), ( int )g_config.ReduceFillrate() );
+ }
+ bReloadMaterials = true;
+ }
+
+ // toggle reverse depth
+ if ( config.bReverseDepth != g_config.bReverseDepth )
+ {
+ if( mat_debugalttab.GetBool() )
+ {
+ Warning( "mat_debugalttab: new ReduceFillrate=%d, old ReduceFillrate=%d, setting bReloadMaterials\n",
+ ( int )config.ReduceFillrate(), ( int )g_config.ReduceFillrate() );
+ }
+ recomputeSnapshots = true;
+ bResetAnisotropy = true;
+ }
+
+ // toggle no transparency
+ if ( config.bNoTransparency != g_config.bNoTransparency )
+ {
+ if ( mat_debugalttab.GetBool() )
+ {
+ Warning( "mat_debugalttab: new bNoTransparency=%d, old bNoTransparency=%d, setting recomputeSnapshots and bResetAnisotropy\n",
+ ( int )config.bNoTransparency, ( int )g_config.bNoTransparency );
+ }
+ recomputeSnapshots = true;
+ bResetAnisotropy = true;
+ }
+
+ // toggle lightmap filtering
+ if ( config.bFilterLightmaps != g_config.bFilterLightmaps )
+ {
+ if ( mat_debugalttab.GetBool() )
+ {
+ Warning( "mat_debugalttab: new bFilterLightmaps=%d, old bFilterLightmaps=%d, setting EnableLightmapFiltering\n",
+ ( int )config.bFilterLightmaps, ( int )g_config.bFilterLightmaps );
+ }
+ GetLightmaps()->EnableLightmapFiltering( config.bFilterLightmaps );
+ }
+
+ // toggle software lighting
+ if ( config.bSoftwareLighting != g_config.bSoftwareLighting )
+ {
+ if( mat_debugalttab.GetBool() )
+ {
+ Warning( "mat_debugalttab: new bSoftwareLighting=%d, old bSoftwareLighting=%d, setting bReloadMaterials\n",
+ ( int )config.bFilterLightmaps, ( int )g_config.bFilterLightmaps );
+ }
+ bReloadMaterials = true;
+ }
+
+#ifndef _X360
+ if ( config_internal.r_waterforceexpensive != g_config_internal.r_waterforceexpensive )
+ {
+ if ( mat_debugalttab.GetBool() )
+ {
+ Warning( "mat_debugalttab: new r_waterforceexpensive=%d, old r_waterforceexpensive=%d, setting bReloadMaterials\n",
+ ( int )config_internal.r_waterforceexpensive, ( int )g_config_internal.r_waterforceexpensive );
+ }
+ bReloadMaterials = true;
+ }
+#endif
+
+ // generic things that cause us to redownload lightmaps
+ if ( config.bAllowCheats != g_config.bAllowCheats )
+ {
+ if ( mat_debugalttab.GetBool() )
+ {
+ Warning( "mat_debugalttab: new bAllowCheats=%d, old bAllowCheats=%d, setting bRedownloadLightmaps\n",
+ ( int )config.bAllowCheats, ( int )g_config.bAllowCheats );
+ }
+ bRedownloadLightmaps = true;
+ }
+
+ // generic things that cause us to redownload textures
+ if ( config.bAllowCheats != g_config.bAllowCheats ||
+ config.skipMipLevels != g_config.skipMipLevels ||
+ config.nShowMipLevels != g_config.nShowMipLevels ||
+ ((config.bCompressedTextures != g_config.bCompressedTextures) && HardwareConfig()->SupportsCompressedTextures())||
+ config.bShowLowResImage != g_config.bShowLowResImage
+ )
+ {
+ if ( mat_debugalttab.GetBool() )
+ {
+ Warning( "mat_debugalttab: setting bRedownloadTextures, recomputeSnapshots, and bResetAnisotropy\n" );
+ }
+ bRedownloadTextures = true;
+ recomputeSnapshots = true;
+ bResetAnisotropy = true;
+ }
+
+ if ( config.ForceTrilinear() != g_config.ForceTrilinear() )
+ {
+ if ( mat_debugalttab.GetBool() )
+ {
+ Warning( "mat_debugalttab: new forcetrilinear: %d, old forcetrilinear: %d, setting bResetTextureFilter\n",
+ ( int )config.ForceTrilinear(), ( int )g_config.ForceTrilinear() );
+ }
+ bResetTextureFilter = true;
+ }
+
+ if ( config.m_nForceAnisotropicLevel != g_config.m_nForceAnisotropicLevel )
+ {
+ if( mat_debugalttab.GetBool() )
+ {
+ Warning( "mat_debugalttab: new m_nForceAnisotropicLevel: %d, old m_nForceAnisotropicLevel: %d, setting bResetAnisotropy and bResetTextureFilter\n",
+ ( int )config.ForceTrilinear(), ( int )g_config.ForceTrilinear() );
+ }
+ bResetAnisotropy = true;
+ bResetTextureFilter = true;
+ }
+
+ if ( config.m_fMonitorGamma != g_config.m_fMonitorGamma || config.m_fGammaTVRangeMin != g_config.m_fGammaTVRangeMin ||
+ config.m_fGammaTVRangeMax != g_config.m_fGammaTVRangeMax || config.m_fGammaTVExponent != g_config.m_fGammaTVExponent ||
+ config.m_bGammaTVEnabled != g_config.m_bGammaTVEnabled )
+ {
+ if( mat_debugalttab.GetBool() )
+ {
+ Warning( "mat_debugalttab: new monitorgamma: %f, old monitorgamma: %f, setting bMonitorGammaChanged\n",
+ config.m_fMonitorGamma, g_config.m_fMonitorGamma );
+ }
+ bMonitorGammaChanged = true;
+ }
+
+ if ( config.m_VideoMode.m_Width != g_config.m_VideoMode.m_Width ||
+ config.m_VideoMode.m_Height != g_config.m_VideoMode.m_Height ||
+ config.m_VideoMode.m_RefreshRate != g_config.m_VideoMode.m_RefreshRate ||
+ config.m_nAASamples != g_config.m_nAASamples ||
+ config.m_nAAQuality != g_config.m_nAAQuality ||
+ config.Windowed() != g_config.Windowed() ||
+ config.Stencil() != g_config.Stencil() )
+ {
+ if( mat_debugalttab.GetBool() )
+ {
+ Warning( "mat_debugalttab: video mode changed for one of various reasons\n" );
+ }
+ bVideoModeChange = true;
+ }
+
+ // toggle wait for vsync
+ // In GL, we just check this and it's just a function call--no need for device shenanigans.
+#if !defined( DX_TO_GL_ABSTRACTION )
+ if ( (IsX360() || !config.Windowed()) && (config.WaitForVSync() != g_config.WaitForVSync()) )
+ {
+# if ( !defined( _X360 ) )
+ {
+ if ( mat_debugalttab.GetBool() )
+ {
+ Warning( "mat_debugalttab: video mode changed due to toggle of wait for vsync\n" );
+ }
+ bVideoModeChange = true;
+ }
+# else
+ {
+ g_pShaderAPI->EnableVSync_360( config.WaitForVSync() );
+ }
+# endif
+ }
+#endif
+
+ g_config = config;
+#ifndef _X360
+ g_config_internal = config_internal;
+#endif
+
+ if ( dxSupportLevelChanged )
+ {
+ if ( mat_debugalttab.GetBool() )
+ {
+ Warning( "mat_debugalttab: dx support level changed, clearing snapshots\n" );
+ }
+ // All snapshots have basically become invalid;
+ g_pShaderAPI->ClearSnapshots();
+ }
+
+ if ( bRedownloadTextures || bRedownloadLightmaps )
+ {
+ // Get rid of this?
+ ColorSpace::SetGamma( 2.2f, 2.2f, OVERBRIGHT, g_config.bAllowCheats, false );
+ }
+
+ // 360 does not support various configuration changes and cannot reload materials
+ if ( !IsX360() )
+ {
+ if ( bResetAnisotropy || recomputeSnapshots || bRedownloadLightmaps ||
+ bRedownloadTextures || bResetAnisotropy || bVideoModeChange ||
+ bSetStandardVertexShaderConstants || bResetTextureFilter )
+ {
+ Unlock( hLock );
+ ForceSingleThreaded();
+ hLock = Lock();
+ }
+ }
+ if ( bReloadMaterials && !IsX360() )
+ {
+ if ( mat_debugalttab.GetBool() )
+ {
+ Warning( "mat_debugalttab: ReloadMaterials\n" );
+ }
+ ReloadMaterials();
+ }
+
+ // 360 does not support various configuration changes and cannot reload textures
+ // 360 has no reason to reload textures, it's unnecessary and massively expensive
+ // 360 does not use this path as an init affect to get its textures into memory
+ if ( bRedownloadTextures && !IsX360() )
+ {
+ if ( mat_debugalttab.GetBool() )
+ {
+ Warning( "mat_debugalttab: redownloading textures\n" );
+ }
+
+ if ( g_pShaderAPI->CanDownloadTextures() )
+ {
+ TextureManager()->RestoreRenderTargets();
+ TextureManager()->RestoreNonRenderTargetTextures();
+ }
+ }
+ else if ( bResetTextureFilter )
+ {
+ if( mat_debugalttab.GetBool() )
+ {
+ Warning( "mat_debugalttab: ResetTextureFilteringState\n" );
+ }
+ TextureManager()->ResetTextureFilteringState();
+ }
+
+ // Recompute all state snapshots
+ if ( recomputeSnapshots )
+ {
+ if( mat_debugalttab.GetBool() )
+ {
+ Warning( "mat_debugalttab: RecomputeAllStateSnapshots\n" );
+ }
+ RecomputeAllStateSnapshots();
+ }
+
+ if ( bResetAnisotropy )
+ {
+ if( mat_debugalttab.GetBool() )
+ {
+ Warning( "mat_debugalttab: SetAnisotropicLevel\n" );
+ }
+ g_pShaderAPI->SetAnisotropicLevel( config.m_nForceAnisotropicLevel );
+ }
+
+ if ( bSetStandardVertexShaderConstants )
+ {
+ if ( mat_debugalttab.GetBool() )
+ {
+ Warning( "mat_debugalttab: SetStandardVertexShaderConstants\n" );
+ }
+ g_pShaderAPI->SetStandardVertexShaderConstants( OVERBRIGHT );
+ }
+
+ if ( bMonitorGammaChanged )
+ {
+ if( mat_debugalttab.GetBool() )
+ {
+ Warning( "mat_debugalttab: SetHardwareGammaRamp\n" );
+ }
+ g_pShaderDevice->SetHardwareGammaRamp( config.m_fMonitorGamma, config.m_fGammaTVRangeMin, config.m_fGammaTVRangeMax,
+ config.m_fGammaTVExponent, config.m_bGammaTVEnabled );
+ }
+
+ if ( bVideoModeChange )
+ {
+ if ( mat_debugalttab.GetBool() )
+ {
+ Warning( "mat_debugalttab: ChangeVideoMode\n" );
+ }
+ ShaderDeviceInfo_t info;
+ ConvertModeStruct( &info, config );
+ g_pShaderAPI->ChangeVideoMode( info );
+
+#if defined( USE_SDL )
+ uint width = info.m_DisplayMode.m_nWidth;
+ uint height = info.m_DisplayMode.m_nHeight;
+ g_pLauncherMgr->RenderedSize( width, height, true ); // true = set
+#endif
+ }
+
+ if ( bForceAltTab )
+ {
+ // Simulate an Alt-Tab
+// g_pShaderAPI->ReleaseResources();
+// g_pShaderAPI->ReacquireResources();
+ }
+
+ Unlock( hLock );
+ if ( bVideoModeChange )
+ {
+ ForceSingleThreaded();
+ }
+ return bRedownloadLightmaps;
+}
+
+
+//-----------------------------------------------------------------------------
+// This is called when the config changes
+//-----------------------------------------------------------------------------
+bool CMaterialSystem::UpdateConfig( bool forceUpdate )
+{
+ int nUpdateFlags = 0;
+ if ( g_pCVar && g_pCVar->HasQueuedMaterialThreadConVarSets() )
+ {
+ ForceSingleThreaded();
+ nUpdateFlags = g_pCVar->ProcessQueuedMaterialThreadConVarSets();
+ }
+
+ MaterialSystem_Config_t config = g_config;
+ ReadConfigFromConVars( &config );
+ return OverrideConfig( config, forceUpdate );
+}
+
+
+
+void CMaterialSystem::ReleaseResources()
+{
+ if( mat_debugalttab.GetBool() )
+ {
+ Warning( "mat_debugalttab: CMaterialSystem::ReleaseResources\n" );
+ }
+ g_pShaderAPI->FlushBufferedPrimitives();
+ g_pShaderDevice->ReleaseResources();
+}
+
+void CMaterialSystem::ReacquireResources()
+{
+ if( mat_debugalttab.GetBool() )
+ {
+ Warning( "mat_debugalttab: CMaterialSystem::ReacquireResources\n" );
+ }
+ g_pShaderDevice->ReacquireResources();
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+bool CMaterialSystem::OnDrawMesh( IMesh *pMesh, int firstIndex, int numIndices )
+{
+ if ( IsInStubMode() )
+ {
+ return false;
+ }
+
+ return GetRenderContextInternal()->OnDrawMesh( pMesh, firstIndex, numIndices );
+}
+
+bool CMaterialSystem::OnDrawMesh( IMesh *pMesh, CPrimList *pLists, int nLists )
+{
+ if ( IsInStubMode() )
+ {
+ return false;
+ }
+
+ return GetRenderContextInternal()->OnDrawMesh( pMesh, pLists, nLists );
+}
+
+void CMaterialSystem::OnThreadEvent( uint32 threadEvent )
+{
+ m_threadEvents.AddToTail( threadEvent );
+}
+
+ShaderAPITextureHandle_t CMaterialSystem::GetShaderAPITextureBindHandle( ITexture *pTexture, int nFrame, int nTextureChannel )
+{
+ return ShaderSystem()->GetShaderAPITextureBindHandle( pTexture, nFrame, nTextureChannel );
+}
+
+//-----------------------------------------------------------------------------
+// Creates a procedural texture
+//-----------------------------------------------------------------------------
+ITexture *CMaterialSystem::CreateProceduralTexture(
+ const char *pTextureName,
+ const char *pTextureGroupName,
+ int w,
+ int h,
+ ImageFormat fmt,
+ int nFlags )
+{
+ ITextureInternal* pTex = TextureManager()->CreateProceduralTexture( pTextureName, pTextureGroupName, w, h, 1, fmt, nFlags );
+ return pTex;
+}
+
+
+//-----------------------------------------------------------------------------
+// Create new materials (currently only used by the editor!)
+//-----------------------------------------------------------------------------
+IMaterial *CMaterialSystem::CreateMaterial( const char *pMaterialName, KeyValues *pVMTKeyValues )
+{
+ // For not, just create a material with no default settings
+ IMaterialInternal* pMaterial = IMaterialInternal::CreateMaterial( pMaterialName, TEXTURE_GROUP_OTHER, pVMTKeyValues );
+ pMaterial->IncrementReferenceCount();
+ AddMaterialToMaterialList( pMaterial );
+ return pMaterial->GetQueueFriendlyVersion();
+}
+
+
+//-----------------------------------------------------------------------------
+// Finds or creates a procedural material
+//-----------------------------------------------------------------------------
+IMaterial *CMaterialSystem::FindProceduralMaterial( const char *pMaterialName, const char *pTextureGroupName, KeyValues *pVMTKeyValues )
+{
+ // We need lower-case symbols for this to work
+ int nLen = Q_strlen( pMaterialName ) + 1;
+ char *pTemp = (char*)stackalloc( nLen );
+ Q_strncpy( pTemp, pMaterialName, nLen );
+ Q_strlower( pTemp );
+ Q_FixSlashes( pTemp, '/' );
+
+ // 'true' causes the search to find procedural materials
+ IMaterialInternal *pMaterial = m_MaterialDict.FindMaterial( pTemp, true );
+ if ( pMaterial )
+ {
+ pVMTKeyValues->deleteThis();
+ }
+ else
+ {
+ pMaterial = IMaterialInternal::CreateMaterial( pMaterialName, pTextureGroupName, pVMTKeyValues );
+ AddMaterialToMaterialList( static_cast<IMaterialInternal*>( pMaterial ) );
+ }
+
+ return pMaterial->GetQueueFriendlyVersion();
+}
+
+
+//-----------------------------------------------------------------------------
+// Search by name
+//-----------------------------------------------------------------------------
+bool CMaterialSystem::IsMaterialLoaded( char const *pMaterialName )
+{
+ // We need lower-case symbols for this to work
+ int nLen = Q_strlen( pMaterialName ) + 1;
+ char *pFixedNameTemp = (char*)stackalloc( nLen );
+ char *pTemp = (char*)stackalloc( nLen );
+ Q_strncpy( pFixedNameTemp, pMaterialName, nLen );
+ Q_strlower( pFixedNameTemp );
+#ifdef POSIX
+ // strip extensions needs correct slashing for the OS, so fix it up early for Posix
+ Q_FixSlashes( pFixedNameTemp, '/' );
+#endif
+ Q_StripExtension( pFixedNameTemp, pTemp, nLen );
+#ifndef POSIX
+ Q_FixSlashes( pTemp, '/' );
+#endif
+
+ Assert( nLen >= Q_strlen( pTemp ) + 1 );
+
+ return m_MaterialDict.FindMaterial( pTemp, false ) != NULL; // 'false' causes the search to find only file-created materials
+}
+
+//-----------------------------------------------------------------------------
+// Search by name
+//-----------------------------------------------------------------------------
+IMaterial* CMaterialSystem::FindMaterial( char const *pMaterialName, const char *pTextureGroupName, bool bComplain, const char *pComplainPrefix )
+{
+ return FindMaterialEx( pMaterialName, pTextureGroupName, MATERIAL_FINDCONTEXT_NONE, bComplain, pComplainPrefix );
+}
+
+//-----------------------------------------------------------------------------
+// Search by name
+//-----------------------------------------------------------------------------
+IMaterial* CMaterialSystem::FindMaterialEx( char const* pMaterialName, const char *pTextureGroupName, int nContext, bool bComplain, const char *pComplainPrefix )
+{
+ // We need lower-case symbols for this to work
+ int nLen = Q_strlen( pMaterialName ) + 1;
+ char *pFixedNameTemp = (char*)stackalloc( nLen );
+ char *pTemp = (char*)stackalloc( nLen );
+ Q_strncpy( pFixedNameTemp, pMaterialName, nLen );
+ Q_strlower( pFixedNameTemp );
+#ifdef POSIX
+ // strip extensions needs correct slashing for the OS, so fix it up early for Posix
+ Q_FixSlashes( pFixedNameTemp, '/' );
+#endif
+ Q_StripExtension( pFixedNameTemp, pTemp, nLen );
+#ifndef POSIX
+ Q_FixSlashes( pTemp, '/' );
+#endif
+
+ Assert( nLen >= Q_strlen( pTemp ) + 1 );
+
+ IMaterialInternal *pExistingMaterial = m_MaterialDict.FindMaterial( pTemp, false ); // 'false' causes the search to find only file-created materials
+
+ if ( pExistingMaterial )
+ return pExistingMaterial->GetQueueFriendlyVersion();
+
+ // It hasn't been seen yet, so let's check to see if it's in the filesystem.
+ nLen = Q_strlen( "materials/" ) + Q_strlen( pTemp ) + Q_strlen( ".vmt" ) + 1;
+ char *vmtName = (char *)stackalloc( nLen );
+
+ // Check to see if this is a UNC-specified material name
+ bool bIsUNC = pTemp[0] == '/' && pTemp[1] == '/' && pTemp[2] != '/';
+ if ( !bIsUNC )
+ {
+ Q_strncpy( vmtName, "materials/", nLen );
+ Q_strncat( vmtName, pTemp, nLen, COPY_ALL_CHARACTERS );
+
+ V_FixDoubleSlashes( vmtName );
+ }
+ else
+ {
+ Q_strncpy( vmtName, pTemp, nLen );
+ }
+
+ //Q_strncat( vmtName, ".vmt", nLen, COPY_ALL_CHARACTERS );
+ Assert( nLen >= (int)Q_strlen( vmtName ) + 1 );
+
+ CUtlVector<FileNameHandle_t> includes;
+ KeyValues *pKeyValues = new KeyValues("vmt");
+ KeyValues *pPatchKeyValues = new KeyValues( "vmt_patches" );
+ if ( !LoadVMTFile( *pKeyValues, *pPatchKeyValues, vmtName, true, &includes ) )
+ {
+ pKeyValues->deleteThis();
+ pKeyValues = NULL;
+ pPatchKeyValues->deleteThis();
+ pPatchKeyValues = NULL;
+ }
+ else
+ {
+ char *matNameWithExtension;
+ nLen = Q_strlen( pTemp ) + Q_strlen( ".vmt" ) + 1;
+ matNameWithExtension = (char *)stackalloc( nLen );
+ Q_strncpy( matNameWithExtension, pTemp, nLen );
+ Q_strncat( matNameWithExtension, ".vmt", nLen, COPY_ALL_CHARACTERS );
+
+ IMaterialInternal *pMat = NULL;
+ if ( !Q_stricmp( pKeyValues->GetName(), "subrect" ) )
+ {
+ pMat = m_MaterialDict.AddMaterialSubRect( matNameWithExtension, pTextureGroupName, pKeyValues, pPatchKeyValues );
+ }
+ else
+ {
+ pMat = m_MaterialDict.AddMaterial( matNameWithExtension, pTextureGroupName );
+ if ( g_pShaderDevice->IsUsingGraphics() )
+ {
+ if ( !bIsUNC )
+ {
+ m_pForcedTextureLoadPathID = "GAME";
+ }
+ pMat->PrecacheVars( pKeyValues, pPatchKeyValues, &includes, nContext );
+ m_pForcedTextureLoadPathID = NULL;
+ }
+ }
+ pKeyValues->deleteThis();
+ pPatchKeyValues->deleteThis();
+
+ return pMat->GetQueueFriendlyVersion();
+ }
+
+ if ( bComplain )
+ {
+ Assert( pTemp );
+
+ // convert to lowercase
+ nLen = Q_strlen(pTemp) + 1 ;
+ char *name = (char*)stackalloc( nLen );
+ Q_strncpy( name, pTemp, nLen );
+ Q_strlower( name );
+
+ if ( m_MaterialDict.NoteMissing( name ) )
+ {
+ if ( pComplainPrefix )
+ {
+ DevWarning( "%s", pComplainPrefix );
+ }
+ DevWarning( "material \"%s\" not found.\n", name );
+ }
+ }
+
+ return g_pErrorMaterial->GetRealTimeVersion();
+}
+
+void CMaterialSystem::SetAsyncTextureLoadCache( void* h )
+{
+ Assert( !h || !m_hAsyncLoadFileCache );
+ m_hAsyncLoadFileCache = ( FileCacheHandle_t ) h;
+}
+
+static char const *TextureAliases[] =
+{
+ // this table is only here for backwards compatibility where a render target change was made,
+ // and we wish to redirect an existing old client.dll for hl2 to reference this texture. It's
+ // not meant as a general texture aliasing system.
+ "_rt_FullFrameFB1", "_rt_FullScreen"
+};
+
+ITexture *CMaterialSystem::FindTexture( char const *pTextureName, const char *pTextureGroupName, bool bComplain /* = false */, int nAdditionalCreationFlags /* = 0 */ )
+{
+ if ( m_hAsyncLoadFileCache && !TextureManager()->IsTextureLoaded( pTextureName ) )
+ {
+ bool bIsUNCName = ( pTextureName[0] == '/' && pTextureName[1] == '/' && pTextureName[2] != '/' );
+ if ( !bIsUNCName )
+ {
+ const char* pPathID = "GAME";
+ char buf[MAX_PATH];
+ V_snprintf( buf, MAX_PATH, "materials/%s", pTextureName );
+ V_SetExtension( buf, ".vtf", sizeof( buf ) );
+
+ const char *pbuf = buf;
+ g_pFullFileSystem->AddFilesToFileCache( m_hAsyncLoadFileCache, &pbuf, 1, pPathID );
+ return TextureManager()->ErrorTexture();
+ }
+ }
+
+ ITextureInternal *pTexture = TextureManager()->FindOrLoadTexture( pTextureName, pTextureGroupName, nAdditionalCreationFlags );
+ Assert( pTexture );
+ if ( pTexture->IsError() )
+ {
+ if ( IsPC() )
+ {
+ for ( int i=0; i<NELEMS( TextureAliases ); i+=2 )
+ {
+ if ( !Q_stricmp( pTextureName, TextureAliases[i] ) )
+ {
+ return FindTexture( TextureAliases[i+1], pTextureGroupName, bComplain, nAdditionalCreationFlags );
+ }
+ }
+ }
+ if ( bComplain )
+ {
+ DevWarning( "Texture '%s' not found.\n", pTextureName );
+ }
+ }
+
+ return pTexture;
+}
+
+bool CMaterialSystem::IsTextureLoaded( char const* pTextureName ) const
+{
+ return TextureManager()->IsTextureLoaded( pTextureName );
+}
+
+void CMaterialSystem::AddTextureAlias( const char *pAlias, const char *pRealName )
+{
+ TextureManager()->AddTextureAlias( pAlias, pRealName );
+}
+
+void CMaterialSystem::RemoveTextureAlias( const char *pAlias )
+{
+ TextureManager()->RemoveTextureAlias( pAlias );
+}
+
+void CMaterialSystem::SetExcludedTextures( const char *pScriptName )
+{
+ TextureManager()->SetExcludedTextures( pScriptName );
+}
+
+void CMaterialSystem::UpdateExcludedTextures( void )
+{
+ TextureManager()->UpdateExcludedTextures();
+ // Have to re-setup the representative textures since they may have been removed out from under us by the queued loader.
+ for (MaterialHandle_t i = FirstMaterial(); i != InvalidMaterial(); i = NextMaterial(i) )
+ {
+ GetMaterialInternal(i)->FindRepresentativeTexture();
+ GetMaterialInternal(i)->PrecacheMappingDimensions();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Recomputes state snapshots for all materials
+//-----------------------------------------------------------------------------
+void CMaterialSystem::RecomputeAllStateSnapshots()
+{
+ g_pShaderAPI->ClearSnapshots();
+ for (MaterialHandle_t i = FirstMaterial(); i != InvalidMaterial(); i = NextMaterial(i) )
+ {
+ GetMaterialInternal(i)->RecomputeStateSnapshots();
+ }
+ g_pShaderAPI->ResetRenderState();
+}
+
+//-----------------------------------------------------------------------------
+// Suspend texture streaming operations, for abormal periods such as loading
+//-----------------------------------------------------------------------------
+void CMaterialSystem::SuspendTextureStreaming()
+{
+ TextureManager()->SuspendTextureStreaming();
+}
+
+//-----------------------------------------------------------------------------
+// Inverse of SuspendTextureStreaming
+//-----------------------------------------------------------------------------
+void CMaterialSystem::ResumeTextureStreaming()
+{
+ TextureManager()->ResumeTextureStreaming();
+}
+
+//-----------------------------------------------------------------------------
+// Uncache all materials
+//-----------------------------------------------------------------------------
+void CMaterialSystem::UncacheAllMaterials()
+{
+ MaterialLock_t hLock = Lock();
+ Flush( true );
+
+ m_bReplacementFilesValid = false;
+
+ for ( MaterialHandle_t i = FirstMaterial(); i != InvalidMaterial(); i = NextMaterial( i ) )
+ {
+ Assert( GetMaterialInternal(i)->GetReferenceCount() >= 0 );
+ GetMaterialInternal(i)->Uncache();
+ }
+ TextureManager()->RemoveUnusedTextures();
+ Unlock( hLock );
+}
+
+
+//-----------------------------------------------------------------------------
+// Uncache unused materials
+//-----------------------------------------------------------------------------
+void CMaterialSystem::UncacheUnusedMaterials( bool bRecomputeStateSnapshots )
+{
+ tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s", __FUNCTION__ );
+
+ MaterialLock_t hLock = Lock();
+ Flush( true );
+
+ // We need two loops to make sure we don't reset the snapshots if nothing got removed,
+ // otherwise the snapshot recomputation is expensive and avoided at load time
+ bool bDidUncacheMaterial = false;
+ for ( MaterialHandle_t i = FirstMaterial(); i != InvalidMaterial(); i = NextMaterial(i) )
+ {
+ IMaterialInternal *pMatInternal = GetMaterialInternal( i );
+ Assert( pMatInternal->GetReferenceCount() >= 0 );
+ if ( pMatInternal->GetReferenceCount() <= 0 )
+ {
+ bDidUncacheMaterial = true;
+ pMatInternal->Uncache();
+ }
+ }
+
+ if ( IsX360() && bRecomputeStateSnapshots )
+ {
+ // Always recompute snapshots because the queued loading process skips it during pre-purge,
+ // allowing it to happen just once, here.
+ bDidUncacheMaterial = true;
+ }
+
+ if ( bDidUncacheMaterial && bRecomputeStateSnapshots )
+ {
+ // Clear the state snapshots since we are going to rebuild all of them.
+ g_pShaderAPI->ClearSnapshots();
+ g_pShaderAPI->ClearVertexAndPixelShaderRefCounts();
+
+ for ( MaterialHandle_t i = FirstMaterial(); i != InvalidMaterial(); i = NextMaterial(i) )
+ {
+ IMaterialInternal *pMatInternal = GetMaterialInternal(i);
+ if ( pMatInternal->GetReferenceCount() > 0 )
+ {
+ // Recompute the state snapshots for the materials that we are keeping
+ // since we blew all of them away above.
+ pMatInternal->RecomputeStateSnapshots();
+ }
+ }
+ g_pShaderAPI->PurgeUnusedVertexAndPixelShaders();
+ }
+
+ if ( bRecomputeStateSnapshots )
+ {
+ // kick out all per material context datas
+ for( MaterialHandle_t i = m_MaterialDict.FirstMaterial(); i != m_MaterialDict.InvalidMaterial(); i = m_MaterialDict.NextMaterial( i ) )
+ {
+ GetMaterialInternal(i)->ClearContextData();
+ }
+ }
+
+ TextureManager()->RemoveUnusedTextures();
+ Unlock( hLock );
+}
+
+
+//-----------------------------------------------------------------------------
+// Release temporary HW memory...
+//-----------------------------------------------------------------------------
+void CMaterialSystem::ResetTempHWMemory( bool bExitingLevel )
+{
+ g_pShaderAPI->DestroyVertexBuffers( bExitingLevel );
+ TextureManager()->ReleaseTempRenderTargetBits();
+}
+
+
+//-----------------------------------------------------------------------------
+// Cache used materials
+//-----------------------------------------------------------------------------
+void CMaterialSystem::CacheUsedMaterials( )
+{
+ g_pShaderAPI->EvictManagedResources();
+ size_t count = 0;
+ for (MaterialHandle_t i = FirstMaterial(); i != InvalidMaterial(); i = NextMaterial(i) )
+ {
+ // Some (mac) drivers (amd) seem to keep extra resources around on uploads until the next frame swap. This
+ // injects pointless synthetic swaps (between already-static load frames)
+ if ( mat_texture_reload_frame_swap_workaround.GetBool() )
+ {
+ if ( count++ % 20 == 0 )
+ {
+ Flush(true);
+ SwapBuffers(); // Not the right thing to call
+ }
+ }
+ IMaterialInternal* pMat = GetMaterialInternal(i);
+ Assert( pMat->GetReferenceCount() >= 0 );
+ if( pMat->GetReferenceCount() > 0 )
+ {
+ pMat->Precache();
+ }
+ }
+ if ( mat_forcemanagedtextureintohardware.GetBool() )
+ {
+ TextureManager()->ForceAllTexturesIntoHardware();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Reloads textures + materials
+//-----------------------------------------------------------------------------
+void CMaterialSystem::ReloadTextures( void )
+{
+ // Add by jay in changelist 621420.
+ ForceSingleThreaded();
+
+ // 360 should not have gotten here
+ Assert( !IsX360() );
+
+ KeyValuesSystem()->InvalidateCache();
+
+ TextureManager()->RestoreRenderTargets();
+ TextureManager()->RestoreNonRenderTargetTextures();
+}
+
+void CMaterialSystem::ReloadMaterials( const char *pSubString )
+{
+ bool bDeviceReady = g_pShaderAPI->CanDownloadTextures();
+
+ if ( !bDeviceReady )
+ {
+ //$ TODO: Merge m_bDeferredMaterialReload from cs:go?
+ Msg( "%s bDeviceReady false\n", __FUNCTION__ );
+ }
+
+ // Add by jay in changelist 621420.
+ ForceSingleThreaded();
+
+ KeyValuesSystem()->InvalidateCache();
+
+ bool bVertexFormatChanged = false;
+ if( pSubString == NULL )
+ {
+ bVertexFormatChanged = true;
+ UncacheAllMaterials();
+ CacheUsedMaterials();
+ }
+ else
+ {
+ Flush( false );
+
+ char const chMultiDelim = '*';
+ CUtlVector< char > arrSearchSubString;
+ CUtlVector< char const * > arrSearchItems;
+
+ if ( strchr( pSubString, chMultiDelim ) )
+ {
+ arrSearchSubString.SetCount( strlen( pSubString ) + 1 );
+ strcpy( arrSearchSubString.Base(), pSubString );
+ for ( char * pch = arrSearchSubString.Base(); pch; )
+ {
+ char *pchEnd = strchr( pch, chMultiDelim );
+ pchEnd ? *( pchEnd ++ ) = 0 : 0;
+ arrSearchItems.AddToTail( pch );
+ pch = pchEnd;
+ }
+ }
+
+ for (MaterialHandle_t i = FirstMaterial(); i != InvalidMaterial(); i = NextMaterial(i) )
+ {
+ if( GetMaterialInternal(i)->GetReferenceCount() <= 0 )
+ continue;
+
+ char const *szMatName = GetMaterialInternal(i)->GetName();
+
+ if ( arrSearchItems.Count() > 1 )
+ {
+ bool bMatched = false;
+
+ for ( int k = 0; !bMatched && ( k < arrSearchItems.Count() ); ++ k )
+ if( Q_stristr( szMatName, arrSearchItems[k] ) )
+ bMatched = true;
+
+ if ( !bMatched )
+ continue;
+ }
+ else
+ {
+ if( !Q_stristr( szMatName, pSubString ) )
+ continue;
+ }
+
+ if ( !GetMaterialInternal(i)->IsPrecached() )
+ {
+ if ( GetMaterialInternal(i)->IsPrecachedVars() )
+ {
+ GetMaterialInternal(i)->Uncache( );
+ }
+ }
+ else
+ {
+ VertexFormat_t oldVertexFormat = GetMaterialInternal(i)->GetVertexFormat();
+ GetMaterialInternal(i)->Uncache();
+ GetMaterialInternal(i)->Precache();
+ GetMaterialInternal(i)->ReloadTextures();
+ if( GetMaterialInternal(i)->GetVertexFormat() != oldVertexFormat )
+ {
+ bVertexFormatChanged = true;
+ }
+ }
+ }
+ }
+
+ if( bVertexFormatChanged && bDeviceReady )
+ {
+ // Reloading materials could cause a vertex format change, so
+ // we need to release and restore
+ ReleaseShaderObjects();
+ RestoreShaderObjects( NULL, MATERIAL_RESTORE_VERTEX_FORMAT_CHANGED );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Allocates the standard textures used by the material system
+//-----------------------------------------------------------------------------
+void CMaterialSystem::AllocateStandardTextures()
+{
+ if ( m_StandardTexturesAllocated )
+ return;
+
+ m_StandardTexturesAllocated = true;
+
+ float nominal_lightmap_value = 1.0;
+ if ( HardwareConfig()->GetHDRType() == HDR_TYPE_INTEGER )
+ nominal_lightmap_value = 1.0/16.0;
+
+ unsigned char texel[4];
+ texel[3] = 255;
+
+ int tcFlags = TEXTURE_CREATE_MANAGED;
+ int tcFlagsSRGB = TEXTURE_CREATE_MANAGED | TEXTURE_CREATE_SRGB;
+
+ if ( IsX360() )
+ {
+ tcFlags |= TEXTURE_CREATE_CANCONVERTFORMAT;
+ tcFlagsSRGB |= TEXTURE_CREATE_CANCONVERTFORMAT;
+ }
+
+ // allocate a white, single texel texture for the fullbright lightmap
+ // note: make sure and redo this when changing gamma, etc.
+ // don't mipmap lightmaps
+ m_FullbrightLightmapTextureHandle = g_pShaderAPI->CreateTexture( 1, 1, 1, IMAGE_FORMAT_BGRX8888, 1, 1, tcFlags, "[FULLBRIGHT_LIGHTMAP_TEXID]", TEXTURE_GROUP_LIGHTMAP );
+ g_pShaderAPI->ModifyTexture( m_FullbrightLightmapTextureHandle );
+ g_pShaderAPI->TexMinFilter( SHADER_TEXFILTERMODE_LINEAR );
+ g_pShaderAPI->TexMagFilter( SHADER_TEXFILTERMODE_LINEAR );
+ float tmpVect[3] = { nominal_lightmap_value, nominal_lightmap_value, nominal_lightmap_value };
+ ColorSpace::LinearToLightmap( texel, tmpVect );
+ g_pShaderAPI->TexImage2D( 0, 0, IMAGE_FORMAT_BGRX8888, 0, 1, 1, IMAGE_FORMAT_BGRX8888, false, texel );
+
+ // allocate a black single texel texture
+#if !defined( _X360 )
+ m_BlackTextureHandle = g_pShaderAPI->CreateTexture( 1, 1, 1, IMAGE_FORMAT_BGRX8888, 1, 1, tcFlagsSRGB, "[BLACK_TEXID]", TEXTURE_GROUP_OTHER );
+ g_pShaderAPI->ModifyTexture( m_BlackTextureHandle );
+ g_pShaderAPI->TexMinFilter( SHADER_TEXFILTERMODE_LINEAR );
+ g_pShaderAPI->TexMagFilter( SHADER_TEXFILTERMODE_LINEAR );
+ texel[0] = texel[1] = texel[2] = 0;
+ g_pShaderAPI->TexImage2D( 0, 0, IMAGE_FORMAT_BGRX8888, 0, 1, 1, IMAGE_FORMAT_BGRX8888, false, texel );
+#else
+ m_BlackTextureHandle = ((ITextureInternal*)FindTexture( "black", TEXTURE_GROUP_OTHER, true ))->GetTextureHandle( 0 );
+#endif
+ g_pShaderAPI->SetStandardTextureHandle( TEXTURE_BLACK, m_BlackTextureHandle );
+
+ // allocate a fully white single texel texture
+#if !defined( _X360 )
+ m_WhiteTextureHandle = g_pShaderAPI->CreateTexture( 1, 1, 1, IMAGE_FORMAT_BGRX8888, 1, 1, tcFlagsSRGB, "[WHITE_TEXID]", TEXTURE_GROUP_OTHER );
+ g_pShaderAPI->ModifyTexture( m_WhiteTextureHandle );
+ g_pShaderAPI->TexMinFilter( SHADER_TEXFILTERMODE_LINEAR );
+ g_pShaderAPI->TexMagFilter( SHADER_TEXFILTERMODE_LINEAR );
+ texel[0] = texel[1] = texel[2] = 255;
+ g_pShaderAPI->TexImage2D( 0, 0, IMAGE_FORMAT_BGRX8888, 0, 1, 1, IMAGE_FORMAT_BGRX8888, false, texel );
+#else
+ m_WhiteTextureHandle = ((ITextureInternal*)FindTexture( "white", TEXTURE_GROUP_OTHER, true ))->GetTextureHandle( 0 );
+#endif
+ g_pShaderAPI->SetStandardTextureHandle( TEXTURE_WHITE, m_WhiteTextureHandle );
+
+ // allocate a grey single texel texture with an alpha of zero (for mat_fullbright 2)
+#if !defined( _X360 )
+ m_GreyTextureHandle = g_pShaderAPI->CreateTexture( 1, 1, 1, IMAGE_FORMAT_BGRX8888, 1, 1, tcFlagsSRGB, "[GREY_TEXID]", TEXTURE_GROUP_OTHER );
+ g_pShaderAPI->ModifyTexture( m_GreyTextureHandle );
+ g_pShaderAPI->TexMinFilter( SHADER_TEXFILTERMODE_LINEAR );
+ g_pShaderAPI->TexMagFilter( SHADER_TEXFILTERMODE_LINEAR );
+ texel[0] = texel[1] = texel[2] = 128;
+ texel[3] = 255; // needs to be 255 so that mat_fullbright 2 stuff isn't translucent.
+ g_pShaderAPI->TexImage2D( 0, 0, IMAGE_FORMAT_BGRX8888, 0, 1, 1, IMAGE_FORMAT_BGRX8888, false, texel );
+#else
+ m_GreyTextureHandle = ((ITextureInternal*)FindTexture( "grey", TEXTURE_GROUP_OTHER, true ))->GetTextureHandle( 0 );
+#endif
+ g_pShaderAPI->SetStandardTextureHandle( TEXTURE_GREY, m_GreyTextureHandle );
+
+ // allocate a grey single texel texture with an alpha of zero (for mat_fullbright 2)
+#if !defined( _X360 )
+ m_GreyAlphaZeroTextureHandle = g_pShaderAPI->CreateTexture( 1, 1, 1, IMAGE_FORMAT_RGBA8888, 1, 1, tcFlagsSRGB, "[GREYALPHAZERO_TEXID]", TEXTURE_GROUP_OTHER );
+ g_pShaderAPI->ModifyTexture( m_GreyAlphaZeroTextureHandle );
+ g_pShaderAPI->TexMinFilter( SHADER_TEXFILTERMODE_LINEAR );
+ g_pShaderAPI->TexMagFilter( SHADER_TEXFILTERMODE_LINEAR );
+ texel[0] = texel[1] = texel[2] = 128;
+ texel[3] = 0; // needs to be 0 so that self-illum doens't affect mat_fullbright 2
+ g_pShaderAPI->TexImage2D( 0, 0, IMAGE_FORMAT_RGBA8888, 0, 1, 1, IMAGE_FORMAT_RGBA8888, false, texel );
+ texel[3] = 255; // set back to default value so we don't affect the rest of this code.'
+#else
+ m_GreyAlphaZeroTextureHandle = ((ITextureInternal*)FindTexture( "greyalphazero", TEXTURE_GROUP_OTHER, true ))->GetTextureHandle( 0 );
+#endif
+ g_pShaderAPI->SetStandardTextureHandle( TEXTURE_GREY_ALPHA_ZERO, m_GreyAlphaZeroTextureHandle );
+
+ // allocate a single texel flat normal texture lightmap
+ m_FlatNormalTextureHandle = g_pShaderAPI->CreateTexture( 1, 1, 1, IMAGE_FORMAT_BGRX8888, 1, 1, tcFlags, "[FLAT_NORMAL_TEXTURE]", TEXTURE_GROUP_OTHER );
+ g_pShaderAPI->ModifyTexture( m_FlatNormalTextureHandle );
+ g_pShaderAPI->TexMinFilter( SHADER_TEXFILTERMODE_LINEAR );
+ g_pShaderAPI->TexMagFilter( SHADER_TEXFILTERMODE_LINEAR );
+ texel[0] = 255; // B
+ texel[1] = 127; // G
+ texel[2] = 127; // R
+ g_pShaderAPI->TexImage2D( 0, 0, IMAGE_FORMAT_BGRX8888, 0, 1, 1, IMAGE_FORMAT_BGRX8888, false, texel );
+ g_pShaderAPI->SetStandardTextureHandle( TEXTURE_NORMALMAP_FLAT, m_FlatNormalTextureHandle );
+
+ // allocate a single texel fullbright 1 lightmap for use with bump textures
+ m_FullbrightBumpedLightmapTextureHandle = g_pShaderAPI->CreateTexture( 1, 1, 1, IMAGE_FORMAT_BGRX8888, 1, 1, tcFlags, "[FULLBRIGHT_BUMPED_LIGHTMAP_TEXID]", TEXTURE_GROUP_LIGHTMAP );
+ g_pShaderAPI->ModifyTexture( m_FullbrightBumpedLightmapTextureHandle );
+ g_pShaderAPI->TexMinFilter( SHADER_TEXFILTERMODE_LINEAR );
+ g_pShaderAPI->TexMagFilter( SHADER_TEXFILTERMODE_LINEAR );
+ float linearColor[3] = { nominal_lightmap_value, nominal_lightmap_value, nominal_lightmap_value };
+ unsigned char dummy[3];
+ ColorSpace::LinearToBumpedLightmap( linearColor, linearColor, linearColor, linearColor,
+ dummy, texel, dummy, dummy );
+ g_pShaderAPI->TexImage2D( 0, 0, IMAGE_FORMAT_BGRX8888, 0, 1, 1, IMAGE_FORMAT_BGRX8888, false, texel );
+ g_pShaderAPI->SetStandardTextureHandle( TEXTURE_LIGHTMAP_BUMPED_FULLBRIGHT, m_FullbrightBumpedLightmapTextureHandle );
+
+ {
+ int iGammaLookupFlags = tcFlags;
+ ImageFormat gammalookupfmt;
+ gammalookupfmt = IMAGE_FORMAT_I8;
+
+ // generate the linear->gamma conversion table texture.
+ {
+ const int LINEAR_TO_GAMMA_TABLE_WIDTH = 512;
+ m_LinearToGammaTableTextureHandle = g_pShaderAPI->CreateTexture( LINEAR_TO_GAMMA_TABLE_WIDTH, 1, 1, gammalookupfmt, 1, 1, iGammaLookupFlags, "[LINEAR_TO_GAMMA_LOOKUP_SRGBON_TEXID]", TEXTURE_GROUP_PIXEL_SHADERS );
+ g_pShaderAPI->ModifyTexture( m_LinearToGammaTableTextureHandle );
+ g_pShaderAPI->TexMinFilter( SHADER_TEXFILTERMODE_LINEAR );
+ g_pShaderAPI->TexMagFilter( SHADER_TEXFILTERMODE_LINEAR );
+ g_pShaderAPI->TexWrap( SHADER_TEXCOORD_S, SHADER_TEXWRAPMODE_CLAMP );
+ g_pShaderAPI->TexWrap( SHADER_TEXCOORD_T, SHADER_TEXWRAPMODE_CLAMP );
+ g_pShaderAPI->TexWrap( SHADER_TEXCOORD_U, SHADER_TEXWRAPMODE_CLAMP );
+
+ float pixelData[LINEAR_TO_GAMMA_TABLE_WIDTH]; //sometimes used as float, sometimes as uint8, sizeof(float) > sizeof(uint8)
+ for( int i = 0; i != LINEAR_TO_GAMMA_TABLE_WIDTH; ++i )
+ {
+ float fLookupResult = ((float)i) / ((float)(LINEAR_TO_GAMMA_TABLE_WIDTH - 1));
+ fLookupResult = g_pShaderAPI->LinearToGamma_HardwareSpecific( fLookupResult );
+
+ //do an extra srgb conversion because we'll be converting back on texture read
+ fLookupResult = g_pShaderAPI->LinearToGamma_HardwareSpecific( fLookupResult ); //that's right, linear->gamma->gamma2x so that that gamma->linear srgb read still ends up in gamma
+
+ int iColor = RoundFloatToInt( fLookupResult * 255.0f );
+ if( iColor > 255 )
+ iColor = 255;
+
+ ((uint8 *)pixelData)[i] = (uint8)iColor;
+ }
+
+ g_pShaderAPI->TexImage2D( 0, 0, gammalookupfmt, 0, LINEAR_TO_GAMMA_TABLE_WIDTH, 1, gammalookupfmt, false, (void *)pixelData );
+ }
+
+ // generate the identity conversion table texture.
+ {
+ const int LINEAR_TO_GAMMA_IDENTITY_TABLE_WIDTH = 256;
+ m_LinearToGammaIdentityTableTextureHandle = g_pShaderAPI->CreateTexture( LINEAR_TO_GAMMA_IDENTITY_TABLE_WIDTH, 1, 1, gammalookupfmt, 1, 1, tcFlags, "[LINEAR_TO_GAMMA_LOOKUP_SRGBOFF_TEXID]", TEXTURE_GROUP_PIXEL_SHADERS );
+ g_pShaderAPI->ModifyTexture( m_LinearToGammaIdentityTableTextureHandle );
+ g_pShaderAPI->TexMinFilter( SHADER_TEXFILTERMODE_LINEAR );
+ g_pShaderAPI->TexMagFilter( SHADER_TEXFILTERMODE_LINEAR );
+ g_pShaderAPI->TexWrap( SHADER_TEXCOORD_S, SHADER_TEXWRAPMODE_CLAMP );
+ g_pShaderAPI->TexWrap( SHADER_TEXCOORD_T, SHADER_TEXWRAPMODE_CLAMP );
+ g_pShaderAPI->TexWrap( SHADER_TEXCOORD_U, SHADER_TEXWRAPMODE_CLAMP );
+
+ float pixelData[LINEAR_TO_GAMMA_IDENTITY_TABLE_WIDTH]; //sometimes used as float, sometimes as uint8, sizeof(float) > sizeof(uint8)
+ for( int i = 0; i != LINEAR_TO_GAMMA_IDENTITY_TABLE_WIDTH; ++i )
+ {
+ float fLookupResult = ((float)i) / ((float)(LINEAR_TO_GAMMA_IDENTITY_TABLE_WIDTH - 1));
+
+ //do an extra srgb conversion because we'll be converting back on texture read
+ fLookupResult = g_pShaderAPI->LinearToGamma_HardwareSpecific( fLookupResult );
+
+ int iColor = RoundFloatToInt( fLookupResult * 255.0f );
+ if ( iColor > 255 )
+ iColor = 255;
+
+ ((uint8 *)pixelData)[i] = (uint8)iColor;
+ }
+
+ g_pShaderAPI->TexImage2D( 0, 0, gammalookupfmt, 0, LINEAR_TO_GAMMA_IDENTITY_TABLE_WIDTH, 1, gammalookupfmt, false, (void *)pixelData );
+ }
+ }
+
+ //create the maximum depth texture
+ {
+ m_MaxDepthTextureHandle = g_pShaderAPI->CreateTexture( 1, 1, 1, IMAGE_FORMAT_RGBA8888, 1, 1, tcFlags, "[MAXDEPTH_TEXID]", TEXTURE_GROUP_OTHER );
+ g_pShaderAPI->ModifyTexture( m_MaxDepthTextureHandle );
+ g_pShaderAPI->TexMinFilter( SHADER_TEXFILTERMODE_LINEAR );
+ g_pShaderAPI->TexMagFilter( SHADER_TEXFILTERMODE_LINEAR );
+
+ //360 gets depth out of the red channel (which doubles as depth in D24S8) and may be 0/1 depending on REVERSE_DEPTH_ON_X360
+ //PC gets depth out of the alpha channel
+ texel[0] = texel[1] = texel[2] = ReverseDepthOnX360() ? 0 : 255;
+ texel[3] = 255;
+
+ g_pShaderAPI->TexImage2D( 0, 0, IMAGE_FORMAT_RGBA8888, 0, 1, 1, IMAGE_FORMAT_RGBA8888, false, texel );
+ }
+
+ //only the shaderapi can handle switching between textures correctly, so pass off the textures to it.
+ g_pShaderAPI->SetLinearToGammaConversionTextures( m_LinearToGammaTableTextureHandle, m_LinearToGammaIdentityTableTextureHandle );
+}
+
+void CMaterialSystem::ReleaseStandardTextures()
+{
+ if ( m_StandardTexturesAllocated )
+ {
+ if ( IsPC() )
+ {
+ g_pShaderAPI->DeleteTexture( m_BlackTextureHandle );
+ g_pShaderAPI->DeleteTexture( m_WhiteTextureHandle );
+ g_pShaderAPI->DeleteTexture( m_GreyTextureHandle );
+ g_pShaderAPI->DeleteTexture( m_GreyAlphaZeroTextureHandle );
+ }
+ g_pShaderAPI->DeleteTexture( m_FullbrightLightmapTextureHandle );
+ g_pShaderAPI->DeleteTexture( m_FlatNormalTextureHandle );
+ g_pShaderAPI->DeleteTexture( m_FullbrightBumpedLightmapTextureHandle );
+
+ g_pShaderAPI->DeleteTexture( m_LinearToGammaTableTextureHandle );
+ g_pShaderAPI->DeleteTexture( m_LinearToGammaIdentityTableTextureHandle );
+ g_pShaderAPI->SetLinearToGammaConversionTextures( INVALID_SHADERAPI_TEXTURE_HANDLE, INVALID_SHADERAPI_TEXTURE_HANDLE );
+
+ g_pShaderAPI->DeleteTexture( m_MaxDepthTextureHandle );
+
+ m_StandardTexturesAllocated = false;
+ }
+}
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+void CMaterialSystem::BeginFrame( float frameTime )
+{
+ // Safety measure (calls should only come from the main thread, also check correct pairing)
+ if ( !ThreadInMainThread() || IsInFrame() )
+ return;
+
+ // check debug vars. we will use these to setup g_nDebugVarsSignature so that materials will
+ // rebuild their draw lists when debug modes changed.
+ g_nDebugVarsSignature = (
+ (mat_specular.GetInt() != 0 ) + ( mat_normalmaps.GetInt() << 1 ) +
+ ( mat_fullbright.GetInt() << 2 ) + (mat_fastnobump.GetInt() << 4 ) ) << 24;
+
+
+ Assert( m_bGeneratedConfig );
+
+ VPROF_BUDGET( "CMaterialSystem::BeginFrame", VPROF_BUDGETGROUP_SWAP_BUFFERS );
+ tmZoneFiltered( TELEMETRY_LEVEL0, 50, TMZF_NONE, "%s", __FUNCTION__ );
+
+ IMatRenderContextInternal *pRenderContext = GetRenderContextInternal();
+ if ( g_config.ForceHWSync() && (IsPC() || m_ThreadMode != MATERIAL_QUEUED_THREADED) )
+ {
+ tmZoneFiltered( TELEMETRY_LEVEL0, 50, TMZF_NONE, "ForceHardwareSync" );
+ pRenderContext->ForceHardwareSync();
+ }
+
+ pRenderContext->MarkRenderDataUnused( true );
+ pRenderContext->BeginFrame();
+ pRenderContext->SetFrameTime( frameTime );
+ pRenderContext->SetToneMappingScaleLinear( Vector( 1,1,1) );
+
+ Assert( !m_bInFrame );
+ m_bInFrame = true;
+}
+
+bool CMaterialSystem::IsInFrame( ) const
+{
+ return m_bInFrame;
+}
+
+#ifdef RAD_TELEMETRY_ENABLED
+static const char *GetMatString( enum MaterialThreadMode_t ThreadMode )
+{
+ switch( ThreadMode )
+ {
+ case MATERIAL_SINGLE_THREADED: return "single";
+ case MATERIAL_QUEUED_SINGLE_THREADED: return "queued_single";
+ case MATERIAL_QUEUED_THREADED: return "queued_threaded";
+ default: return "???";
+ }
+}
+#endif
+
+ConVar mat_queue_mode( "mat_queue_mode", "-1", FCVAR_ARCHIVE, "The queue/thread mode the material system should use: -1=default, 0=synchronous single thread"
+#ifdef MAT_QUEUE_MODE_PROFILE
+ ", 1=queued single thread"
+#endif
+ ", 2=queued multithreaded" );
+
+ConVar mat_queue_report( "mat_queue_report", "0", FCVAR_ARCHIVE, "Report thread stalls. Positive number will filter by stalls >= time in ms. -1 reports all locks." );
+
+void CMaterialSystem::ThreadExecuteQueuedContext( CMatQueuedRenderContext *pContext )
+{
+#ifdef RAD_TELEMETRY_ENABLED
+ tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s-%s", __FUNCTION__, GetMatString( m_ThreadMode ) );
+ CTelemetrySpikeDetector Spike( "ThreadExecuteQueuedContext", 1 );
+#endif
+
+ Assert( m_bThreadHasOwnership );
+
+ m_nRenderThreadID = ThreadGetCurrentId();
+ IMatRenderContextInternal* pSavedRenderContext = m_pRenderContext.Get();
+ m_pRenderContext.Set( &m_HardwareRenderContext );
+ pContext->EndQueue( true );
+ m_pRenderContext.Set( pSavedRenderContext );
+ m_nRenderThreadID = 0xFFFFFFFF;
+}
+
+IThreadPool *CMaterialSystem::CreateMatQueueThreadPool()
+{
+ if( IsX360() )
+ {
+ return g_pThreadPool;
+ }
+ else if( !m_pMatQueueThreadPool )
+ {
+ ThreadPoolStartParams_t startParams;
+
+ startParams.nThreads = 1;
+ startParams.nStackSize = 256*1024;
+ startParams.fDistribute = TRS_TRUE;
+
+ // The rendering thread has the GL context and the main thread is coming in and
+ // "helping" finish jobs - that breaks OpenGL, which requires TLS. This flag states
+ // that only the threadpool threads should execute these jobs.
+ startParams.bExecOnThreadPoolThreadsOnly = true;
+
+ m_pMatQueueThreadPool = CreateThreadPool();
+ m_pMatQueueThreadPool->Start( startParams, "MatQueue" );
+ }
+
+ return m_pMatQueueThreadPool;
+}
+
+void CMaterialSystem::DestroyMatQueueThreadPool()
+{
+ if( m_pMatQueueThreadPool )
+ {
+ m_pMatQueueThreadPool->Stop();
+ delete m_pMatQueueThreadPool;
+ m_pMatQueueThreadPool = NULL;
+ }
+}
+
+
+//-----------------------------------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------------------------------
+class CThreadAcquire : public CJob
+{
+ virtual JobStatus_t DoExecute()
+ {
+ g_pShaderAPI->AcquireThreadOwnership();
+
+ return JOB_OK;
+ }
+};
+
+void CMaterialSystem::EndFrame( void )
+{
+ // Safety measure (calls should only come from the main thread, also check correct pairing)
+ if ( !ThreadInMainThread() || !IsInFrame() )
+ return;
+
+ Assert( m_bGeneratedConfig );
+ VPROF_BUDGET( "CMaterialSystem::EndFrame", VPROF_BUDGETGROUP_SWAP_BUFFERS );
+
+ GetRenderContextInternal()->EndFrame();
+
+ TextureManager()->Update();
+
+ while ( !m_scheduledComposites.IsEmpty() )
+ {
+ // We hold a ref, so if there's only one count left, it's us. Let it go and move on.
+ if ( m_scheduledComposites[ 0 ]->GetRefCount() == 1 )
+ {
+ m_scheduledComposites[ 0 ]->Release();
+ m_scheduledComposites.Remove( 0 );
+ continue;
+ }
+
+ m_scheduledComposites[ 0 ]->Resolve();
+ m_pendingComposites.AddToTail( m_scheduledComposites[ 0 ] );
+
+ m_scheduledComposites.Remove( 0 );
+
+ // Only do one per frame, because these can actually be fairly expensive.
+ break;
+ }
+
+ FOR_EACH_VEC( m_pendingComposites, i )
+ {
+ CTextureCompositor* comp = m_pendingComposites[ i ];
+
+ // We hold a ref, so if there's only one count left, it's us. Let it go and move on.
+ if ( comp->GetRefCount() == 1 )
+ {
+ comp->Release();
+ m_pendingComposites.Remove( i );
+ // Back up one
+ --i;
+ continue;
+ }
+
+ comp->Update();
+
+ if ( comp->GetResolveStatus() == ECRS_Complete || comp->GetResolveStatus() == ECRS_Error )
+ {
+ comp->Release();
+ m_pendingComposites.Remove( i );
+
+ // Stop after the first one reports that it was completed, these can take awhile and
+ // we don't want to hammer anyone's framerate.
+ break;
+ }
+ }
+
+ //-------------------------------------------------------------
+
+ int iConVarThreadMode = mat_queue_mode.GetInt();
+
+ // For this testing release, -2 is equivalent to 0 (off). When we release, we'll make -2 equivalent to -1 (on)
+ if ( iConVarThreadMode == -2 )
+ {
+ iConVarThreadMode = MATERIAL_QUEUED_THREADED;
+ }
+
+#ifndef MAT_QUEUE_MODE_PROFILE
+ if ( iConVarThreadMode == MATERIAL_QUEUED_SINGLE_THREADED )
+ {
+ iConVarThreadMode = MATERIAL_SINGLE_THREADED;
+ }
+#endif
+
+ MaterialThreadMode_t nextThreadMode = ( iConVarThreadMode >= 0 ) ? (MaterialThreadMode_t)iConVarThreadMode : m_IdealThreadMode;
+ // note: This is a hack because there is no explicit query for the device being deactivated due to device lost.
+ // however, that is all the current implementation of CanDownloadTextures actually does.
+ bool bDeviceReady = g_pShaderAPI->CanDownloadTextures();
+ if ( !bDeviceReady || !m_bAllowQueuedRendering )
+ {
+ nextThreadMode = MATERIAL_SINGLE_THREADED;
+ }
+
+ if ( m_bForcedSingleThreaded || m_bThreadingNotAvailable )
+ {
+ nextThreadMode = MATERIAL_SINGLE_THREADED;
+ m_bForcedSingleThreaded = false;
+ }
+
+ switch ( m_ThreadMode )
+ {
+ case MATERIAL_SINGLE_THREADED:
+ OnRenderingAsyncComplete();
+ break;
+
+ case MATERIAL_QUEUED_THREADED:
+ {
+ VPROF_BUDGET( "Mat_ThreadedEndframe", "Mat_ThreadedEndframe" );
+ if ( !m_bThreadHasOwnership )
+ {
+ ThreadAcquire( true );
+ }
+
+ if ( m_pActiveAsyncJob && !m_pActiveAsyncJob->IsFinished() )
+ {
+ m_pActiveAsyncJob->WaitForFinish();
+ if ( !IsPC() && g_config.ForceHWSync() )
+ {
+ g_pShaderAPI->ForceHardwareSync();
+ }
+ }
+ SafeRelease( m_pActiveAsyncJob );
+
+ OnRenderingAsyncComplete();
+
+ CMatQueuedRenderContext *pPrevContext = &m_QueuedRenderContexts[m_iCurQueuedContext];
+
+ m_iCurQueuedContext = ( ( m_iCurQueuedContext + 1 ) % ARRAYSIZE( m_QueuedRenderContexts) );
+ m_QueuedRenderContexts[m_iCurQueuedContext].BeginQueue( pPrevContext );
+ m_pRenderContext.Set( &m_QueuedRenderContexts[m_iCurQueuedContext] );
+
+ m_pActiveAsyncJob = new CFunctorJob( CreateFunctor( this, &CMaterialSystem::ThreadExecuteQueuedContext, pPrevContext ), "ThreadExecuteQueuedContext" );
+ if ( IsX360() )
+ {
+ if ( m_nServiceThread >= 0 )
+ {
+ m_pActiveAsyncJob->SetServiceThread( m_nServiceThread );
+ }
+ }
+
+ IThreadPool *pThreadPool = CreateMatQueueThreadPool();
+ pThreadPool->AddJob( m_pActiveAsyncJob );
+ break;
+ }
+
+ case MATERIAL_QUEUED_SINGLE_THREADED:
+ OnRenderingAsyncComplete();
+ break;
+
+#ifdef MAT_QUEUE_MODE_PROFILE
+ {
+ VPROF_BUDGET( "Mat_ThreadedEndframe", "Mat_QueuedEndframe" );
+
+ g_pShaderAPI->SetDisallowAccess( false );
+ m_pRenderContext.Set( &m_HardwareRenderContext );
+ m_QueuedRenderContexts[m_iCurQueuedContext].CallQueued();
+ m_pRenderContext.Set( &m_QueuedRenderContexts[m_iCurQueuedContext] );
+ g_pShaderAPI->SetDisallowAccess( true );
+ break;
+ }
+#endif
+ }
+
+ bool bRelease = false;
+ if ( !bDeviceReady )
+ {
+ if ( nextThreadMode != MATERIAL_SINGLE_THREADED )
+ {
+ Assert( nextThreadMode == MATERIAL_SINGLE_THREADED );
+ bRelease = true;
+ nextThreadMode = MATERIAL_SINGLE_THREADED;
+ if( mat_debugalttab.GetBool() )
+ {
+ Warning("Handling alt-tab in queued mode!\n");
+ }
+ }
+ }
+
+ if ( m_threadEvents.Count() )
+ {
+ nextThreadMode = MATERIAL_SINGLE_THREADED;
+ }
+
+ if ( m_ThreadMode != nextThreadMode )
+ {
+ // Shut down the current mode & set new mode
+ switch ( m_ThreadMode )
+ {
+ case MATERIAL_SINGLE_THREADED:
+ break;
+
+ case MATERIAL_QUEUED_THREADED:
+ {
+ if ( m_pActiveAsyncJob )
+ {
+ m_pActiveAsyncJob->WaitForFinish();
+ SafeRelease( m_pActiveAsyncJob );
+ }
+ // probably have a queued context set here, need hardware to flush the queue if the job isn't active
+ m_HardwareRenderContext.InitializeFrom(&m_QueuedRenderContexts[m_iCurQueuedContext]);
+ m_pRenderContext.Set( &m_HardwareRenderContext );
+
+ m_QueuedRenderContexts[m_iCurQueuedContext].EndQueue( true );
+ ThreadRelease();
+ }
+ break;
+
+#ifdef MAT_QUEUE_MODE_PROFILE
+ case MATERIAL_QUEUED_SINGLE_THREADED:
+ {
+ g_pShaderAPI->SetDisallowAccess( false );
+ // We have a queued context set here, need hardware to flush the queue if the job isn't active
+ m_pRenderContext.Set( &m_HardwareRenderContext );
+ m_QueuedRenderContexts[m_iCurQueuedContext].EndQueue( true );
+ break;
+ }
+#endif
+ }
+
+ m_ThreadMode = nextThreadMode;
+ Assert( g_MatSysMutex.GetOwnerId() == 0 );
+
+ g_pShaderAPI->EnableShaderShaderMutex( m_ThreadMode != MATERIAL_SINGLE_THREADED ); // use mutex even for queued to allow "disalow access" to function properly
+ g_pShaderAPI->EnableBuffer2FramesAhead( true );
+
+ switch ( m_ThreadMode )
+ {
+ case MATERIAL_SINGLE_THREADED:
+ m_pRenderContext.Set( &m_HardwareRenderContext );
+ for ( int i = 0; i < ARRAYSIZE( m_QueuedRenderContexts ); i++ )
+ {
+ Assert( m_QueuedRenderContexts[i].IsInitialized() );
+ m_QueuedRenderContexts[i].EndQueue( true );
+ }
+ break;
+
+#ifdef MAT_QUEUE_MODE_PROFILE
+ case MATERIAL_QUEUED_SINGLE_THREADED:
+#endif
+ case MATERIAL_QUEUED_THREADED:
+ {
+ m_iCurQueuedContext = 0;
+ m_QueuedRenderContexts[m_iCurQueuedContext].BeginQueue( &m_HardwareRenderContext );
+ m_pRenderContext.Set( &m_QueuedRenderContexts[m_iCurQueuedContext] );
+#ifdef MAT_QUEUE_MODE_PROFILE
+ if ( m_ThreadMode == MATERIAL_QUEUED_SINGLE_THREADED )
+ {
+ g_pShaderAPI->SetDisallowAccess( true );
+ }
+ else
+#endif
+ {
+ g_pShaderAPI->ReleaseThreadOwnership();
+
+ CJob *pActiveAsyncJob = new CThreadAcquire();
+ IThreadPool *pThreadPool = CreateMatQueueThreadPool();
+ pThreadPool->AddJob( pActiveAsyncJob );
+ SafeRelease( pActiveAsyncJob );
+
+ m_bThreadHasOwnership = true;
+ m_ThreadOwnershipID = ThreadGetCurrentId();
+ }
+ }
+ break;
+ }
+ }
+
+ if ( m_ThreadMode == MATERIAL_SINGLE_THREADED )
+ {
+ for ( int i = 0; i < m_threadEvents.Count(); i++ )
+ {
+ g_pShaderDevice->HandleThreadEvent(m_threadEvents[i]);
+ }
+ m_threadEvents.RemoveAll();
+ }
+ Assert( m_bInFrame );
+ m_bInFrame = false;
+}
+
+void CMaterialSystem::SetInStubMode( bool bInStubMode )
+{
+ m_bInStubMode = bInStubMode;
+}
+
+bool CMaterialSystem::IsInStubMode()
+{
+ return m_bInStubMode;
+}
+
+void CMaterialSystem::Flush( bool flushHardware )
+{
+ GetRenderContextInternal()->Flush( flushHardware );
+}
+
+
+//-----------------------------------------------------------------------------
+// Flushes managed textures from the texture cacher
+//-----------------------------------------------------------------------------
+void CMaterialSystem::EvictManagedResources()
+{
+ g_pShaderAPI->EvictManagedResources();
+}
+
+int __cdecl MaterialNameCompareFunc( const void *elem1, const void *elem2 )
+{
+ IMaterialInternal *pMaterialA = g_MaterialSystem.GetMaterialInternal( *(MaterialHandle_t *)elem1 );
+ IMaterialInternal *pMaterialB = g_MaterialSystem.GetMaterialInternal( *(MaterialHandle_t *)elem2 );
+
+ // case insensitive to group similar named materials
+ return stricmp( pMaterialA->GetName(), pMaterialB->GetName() );
+}
+
+void CMaterialSystem::DebugPrintUsedMaterials( const char *pSearchSubString, bool bVerbose )
+{
+ MaterialHandle_t h;
+ int i;
+ int nNumCached;
+ int nRefCount;
+ int nSortedMaterials;
+ int nNumErrors;
+
+ // build a mapping to sort the material names
+ MaterialHandle_t *pSorted = (MaterialHandle_t*)stackalloc( GetNumMaterials() * sizeof(MaterialHandle_t) );
+ nSortedMaterials = 0;
+ for (h = FirstMaterial(); h != InvalidMaterial(); h = NextMaterial(h) )
+ {
+ pSorted[nSortedMaterials++] = h;
+ }
+ qsort( pSorted, nSortedMaterials, sizeof(MaterialHandle_t), MaterialNameCompareFunc );
+
+ nNumCached = 0;
+ nNumErrors = 0;
+ for (i = 0; i < nSortedMaterials; i++)
+ {
+ // iterate using sort mapping
+ IMaterialInternal *pMaterial = GetMaterialInternal(pSorted[i]);
+
+ nRefCount = pMaterial->GetReferenceCount();
+
+ if ( nRefCount < 0 )
+ {
+ nNumErrors++;
+ }
+ else if (!nRefCount)
+ {
+ if (pMaterial->IsPrecached() || pMaterial->IsPrecachedVars())
+ {
+ nNumErrors++;
+ }
+ }
+ else
+ {
+ // nonzero reference count
+ // tally the valid ones
+ nNumCached++;
+
+ if( pSearchSubString )
+ {
+ if( !Q_stristr( pMaterial->GetName(), pSearchSubString ) &&
+ (!pMaterial->GetShader() || !Q_stristr( pMaterial->GetShader()->GetName(), pSearchSubString )) )
+ {
+ continue;
+ }
+ }
+
+ DevMsg( "%s (shader: %s) refCount: %d.\n", pMaterial->GetName(),
+ pMaterial->GetShader() ? pMaterial->GetShader()->GetName() : "unknown\n", nRefCount );
+
+ if( !bVerbose )
+ {
+ continue;
+ }
+
+ if( pMaterial->IsPrecached() )
+ {
+ if( pMaterial->GetShader() )
+ {
+ for( int j = 0; j < pMaterial->GetShader()->GetNumParams(); j++ )
+ {
+ IMaterialVar *var;
+ var = pMaterial->GetShaderParams()[j];
+
+ if( var )
+ {
+ switch( var->GetType() )
+ {
+ case MATERIAL_VAR_TYPE_TEXTURE:
+ {
+ ITextureInternal *texture = static_cast<ITextureInternal *>( var->GetTextureValue() );
+ if( !texture )
+ {
+ DevWarning( "Programming error: CMaterialSystem::DebugPrintUsedMaterialsCallback: NULL texture\n" );
+ continue;
+ }
+
+ if( IsTextureInternalEnvCubemap( texture ) )
+ {
+ DevMsg( " \"%s\" \"env_cubemap\"\n", var->GetName() );
+ }
+ else
+ {
+ DevMsg( " \"%s\" \"%s\"\n",
+ var->GetName(),
+ texture->GetName() );
+ DevMsg( " %dx%d refCount: %d numframes: %d\n", texture->GetActualWidth(), texture->GetActualHeight(),
+ texture->GetReferenceCount(), texture->GetNumAnimationFrames() );
+ }
+ }
+ break;
+ case MATERIAL_VAR_TYPE_UNDEFINED:
+ break;
+ default:
+ DevMsg( " \"%s\" \"%s\"\n", var->GetName(), var->GetStringValue() );
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // list the critical errors after, otherwise the console log scrolls them away
+ if (nNumErrors)
+ {
+ for (i = 0; i < nSortedMaterials; i++)
+ {
+ // iterate using sort mapping
+ IMaterialInternal *pMaterial = GetMaterialInternal(pSorted[i]);
+
+ nRefCount = pMaterial->GetReferenceCount();
+
+ if ( nRefCount < 0 )
+ {
+ // reference counts should not be negative
+ DevWarning( "DebugPrintUsedMaterials: refCount (%d) < 0 for material: \"%s\"\n",
+ nRefCount, pMaterial->GetName() );
+ }
+ else if (!nRefCount)
+ {
+ // ensure that it stayed uncached after the post loading uncache
+ // this is effectively a coding bug thats needs to be fixed
+ // a material is being precached without incrementing its reference
+ if (pMaterial->IsPrecached() || pMaterial->IsPrecachedVars())
+ {
+ DevWarning( "DebugPrintUsedMaterials: material: \"%s\" didn't unache\n",
+ pMaterial->GetName() );
+ }
+ }
+ }
+ DevWarning( "%d Errors\n", nNumErrors );
+ }
+
+ if (!pSearchSubString)
+ {
+ DevMsg( "%d Cached, %d Total Materials\n", nNumCached, GetNumMaterials() );
+ }
+}
+
+void CMaterialSystem::DebugPrintUsedTextures( void )
+{
+ TextureManager()->DebugPrintUsedTextures();
+}
+
+#if defined( _X360 )
+void CMaterialSystem::ListUsedMaterials( void )
+{
+ int numMaterials = GetNumMaterials();
+ xMaterialList_t* pMaterialList = (xMaterialList_t *)stackalloc( numMaterials * sizeof( xMaterialList_t ) );
+
+ numMaterials = 0;
+ for ( MaterialHandle_t hMaterial = FirstMaterial(); hMaterial != InvalidMaterial(); hMaterial = NextMaterial( hMaterial ) )
+ {
+ IMaterialInternal *pMaterial = GetMaterialInternal( hMaterial );
+ pMaterialList[numMaterials].pName = pMaterial->GetName();
+ pMaterialList[numMaterials].pShaderName = pMaterial->GetShader() ? pMaterial->GetShader()->GetName() : "???";
+ pMaterialList[numMaterials].refCount = pMaterial->GetReferenceCount();
+ numMaterials++;
+ }
+
+ XBX_rMaterialList( numMaterials, pMaterialList );
+}
+#endif
+
+void CMaterialSystem::ToggleSuppressMaterial( char const* pMaterialName )
+{
+ /*
+ // This version suppresses all but the material
+ IMaterial *pMaterial = GetFirstMaterial();
+ while (pMaterial)
+ {
+ if (stricmp(pMaterial->GetName(), pMaterialName))
+ {
+ IMaterialInternal* pMatInt = static_cast<IMaterialInternal*>(pMaterial);
+ pMatInt->ToggleSuppression();
+ }
+ pMaterial = GetNextMaterial();
+ }
+ */
+
+ // Note: if we use this function a lot, we'll want to do something else, like have them
+ // pass in a texture group or reuse whatever texture group the material already had.
+ // As it is, this is rarely used, so if it's not in TEXTURE_GROUP_OTHER, it'll go in
+ // TEXTURE_GROUP_SHARED.
+ IMaterial* pMaterial = FindMaterial( pMaterialName, TEXTURE_GROUP_OTHER, true, NULL );
+ if ( !IsErrorMaterial( pMaterial ) )
+ {
+ IMaterialInternal* pMatInt = static_cast<IMaterialInternal*>(pMaterial);
+ pMatInt = pMatInt->GetRealTimeVersion(); //always work with the realtime material internally
+ pMatInt->ToggleSuppression();
+ }
+}
+
+void CMaterialSystem::ToggleDebugMaterial( char const* pMaterialName )
+{
+ // Note: if we use this function a lot, we'll want to do something else, like have them
+ // pass in a texture group or reuse whatever texture group the material already had.
+ // As it is, this is rarely used, so if it's not in TEXTURE_GROUP_OTHER, it'll go in
+ // TEXTURE_GROUP_SHARED.
+ IMaterial* pMaterial = FindMaterial( pMaterialName, TEXTURE_GROUP_OTHER, false, NULL );
+ if ( !IsErrorMaterial( pMaterial ) )
+ {
+ IMaterialInternal* pMatInt = static_cast<IMaterialInternal*>(pMaterial);
+ pMatInt = pMatInt->GetRealTimeVersion(); //always work with the realtime material internally
+ pMatInt->ToggleDebugTrace();
+ }
+ else
+ {
+ Warning("Unknown material %s\n", pMaterialName );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Used to iterate over all shaders for editing purposes
+//-----------------------------------------------------------------------------
+int CMaterialSystem::ShaderCount() const
+{
+ return ShaderSystem()->ShaderCount();
+}
+
+int CMaterialSystem::GetShaders( int nFirstShader, int nMaxCount, IShader **ppShaderList ) const
+{
+ return ShaderSystem()->GetShaders( nFirstShader, nMaxCount, ppShaderList );
+}
+
+
+//-----------------------------------------------------------------------------
+// FIXME: Is there a better way of doing this?
+// Returns shader flag names for editors to be able to edit them
+//-----------------------------------------------------------------------------
+int CMaterialSystem::ShaderFlagCount() const
+{
+ return ShaderSystem()->ShaderStateCount( );
+}
+
+const char *CMaterialSystem::ShaderFlagName( int nIndex ) const
+{
+ return ShaderSystem()->ShaderStateString( nIndex );
+}
+
+
+//-----------------------------------------------------------------------------
+// Returns the currently active shader fallback for a particular shader
+//-----------------------------------------------------------------------------
+void CMaterialSystem::GetShaderFallback( const char *pShaderName, char *pFallbackShader, int nFallbackLength )
+{
+ // FIXME: This is pretty much a hack. We need a better way for the
+ // editor to get ahold of shader fallbacks
+ int nCount = ShaderCount();
+ IShader** ppShaderList = (IShader**)_alloca( nCount * sizeof(IShader) );
+ GetShaders( 0, nCount, ppShaderList );
+
+ do
+ {
+ int i;
+ for ( i = 0; i < nCount; ++i )
+ {
+ if ( !Q_stricmp( pShaderName, ppShaderList[i]->GetName() ) )
+ break;
+ }
+
+ // Didn't find a match!
+ if ( i == nCount )
+ {
+ Q_strncpy( pFallbackShader, "wireframe", nFallbackLength );
+ return;
+ }
+
+ // Found a match
+ // FIXME: Theoretically, getting fallbacks should require a param list
+ // In practice, it looks rare or maybe even neved done
+ const char *pFallback = ppShaderList[i]->GetFallbackShader( NULL );
+ if ( !pFallback )
+ {
+ Q_strncpy( pFallbackShader, pShaderName, nFallbackLength );
+ return;
+ }
+ else
+ {
+ pShaderName = pFallback;
+ }
+ } while (true);
+}
+
+//-----------------------------------------------------------------------------
+// Triggers OpenGL shader preloading at game startup
+//-----------------------------------------------------------------------------
+#ifdef DX_TO_GL_ABSTRACTION
+void CMaterialSystem::DoStartupShaderPreloading( void )
+{
+ GetRenderContextInternal()->DoStartupShaderPreloading();
+}
+#endif
+
+
+void CMaterialSystem::SwapBuffers( void )
+{
+ VPROF_BUDGET( "CMaterialSystem::SwapBuffers", VPROF_BUDGETGROUP_SWAP_BUFFERS );
+ GetRenderContextInternal()->SwapBuffers();
+ g_FrameNum++;
+}
+
+bool CMaterialSystem::InEditorMode() const
+{
+ Assert( m_bGeneratedConfig );
+ return g_config.bEditMode && CanUseEditorMaterials();
+}
+
+void CMaterialSystem::NoteAnisotropicLevel( int currentLevel )
+{
+ Assert( m_bGeneratedConfig );
+ g_config.m_nForceAnisotropicLevel = currentLevel;
+}
+
+// Get the current config for this video card (as last set by control panel or the default if not)
+const MaterialSystem_Config_t &CMaterialSystem::GetCurrentConfigForVideoCard() const
+{
+ Assert( m_bGeneratedConfig );
+ return g_config;
+}
+
+// Does the device support the given MSAA level?
+bool CMaterialSystem::SupportsMSAAMode( int nNumSamples )
+{
+ return g_pShaderAPI->SupportsMSAAMode( nNumSamples );
+}
+
+void CMaterialSystem::ReloadFilesInList( IFileList *pFilesToReload )
+{
+ if ( !IsPC() )
+ return;
+
+ // We have to flush the materials in 2 steps because they have recursive dependencies. The problem case
+ // is if you have two materials, A and B, that depend on C. You tell A to reload and it also reloads C. Then
+ // the filesystem thinks C doesn't need to be reloaded anymore. So when you get to B, it decides not to reload
+ // either since C doesn't need to be reloaded. To fix this, we ask all materials if they want to reload in
+ // one stage, then in the next stage we actually reload the appropriate ones.
+ MaterialHandle_t hNext;
+ for ( MaterialHandle_t h=m_MaterialDict.FirstMaterial(); h != m_MaterialDict.InvalidMaterial(); h=hNext )
+ {
+ hNext = m_MaterialDict.NextMaterial( h );
+ IMaterialInternal *pMat = m_MaterialDict.GetMaterialInternal( h );
+
+ pMat->DecideShouldReloadFromWhitelist( pFilesToReload );
+ }
+
+ // Now reload the materials that wanted to be reloaded.
+ for ( MaterialHandle_t h=m_MaterialDict.FirstMaterial(); h != m_MaterialDict.InvalidMaterial(); h=hNext )
+ {
+ hNext = m_MaterialDict.NextMaterial( h );
+ IMaterialInternal *pMat = m_MaterialDict.GetMaterialInternal( h );
+
+ pMat->ReloadFromWhitelistIfMarked();
+ }
+
+ // Flush out necessary textures.
+ TextureManager()->ReloadFilesInList( pFilesToReload );
+}
+
+// Does the device support the given CSAA level?
+bool CMaterialSystem::SupportsCSAAMode( int nNumSamples, int nQualityLevel )
+{
+ return g_pShaderAPI->SupportsCSAAMode( nNumSamples, nQualityLevel );
+}
+
+// Does the device support shadow depth texturing?
+bool CMaterialSystem::SupportsShadowDepthTextures( void )
+{
+ return g_pShaderAPI->SupportsShadowDepthTextures();
+}
+
+// Does the device support Fetch4
+bool CMaterialSystem::SupportsFetch4( void )
+{
+ return g_pShaderAPI->SupportsFetch4();
+}
+
+// Vendor-dependent shadow depth texture format
+ImageFormat CMaterialSystem::GetShadowDepthTextureFormat( void )
+{
+ return g_pShaderAPI->GetShadowDepthTextureFormat();
+}
+
+// Vendor-dependent slim texture format
+ImageFormat CMaterialSystem::GetNullTextureFormat( void )
+{
+ return g_pShaderAPI->GetNullTextureFormat();
+}
+
+void CMaterialSystem::SetShadowDepthBiasFactors( float fShadowSlopeScaleDepthBias, float fShadowDepthBias )
+{
+ g_pShaderAPI->SetShadowDepthBiasFactors( fShadowSlopeScaleDepthBias, fShadowDepthBias );
+}
+
+bool CMaterialSystem::SupportsHDRMode( HDRType_t nHDRMode )
+{
+ return HardwareConfig()->SupportsHDRMode( nHDRMode );
+}
+
+bool CMaterialSystem::UsesSRGBCorrectBlending( void ) const
+{
+ return HardwareConfig()->UsesSRGBCorrectBlending();
+}
+
+// Get video card identitier
+const MaterialSystemHardwareIdentifier_t &CMaterialSystem::GetVideoCardIdentifier( void ) const
+{
+ static MaterialSystemHardwareIdentifier_t foo;
+ Assert( 0 );
+ return foo;
+}
+
+void CMaterialSystem::AddModeChangeCallBack( ModeChangeCallbackFunc_t func )
+{
+ g_pShaderDeviceMgr->AddModeChangeCallback( func );
+}
+
+void CMaterialSystem::RemoveModeChangeCallBack( ModeChangeCallbackFunc_t func )
+{
+ g_pShaderDeviceMgr->RemoveModeChangeCallback( func );
+}
+
+
+//-----------------------------------------------------------------------------
+// Gets configuration information associated with the display card, and optionally for a particular DX level.
+// It will return a list of ConVars and values to set.
+//-----------------------------------------------------------------------------
+bool CMaterialSystem::GetRecommendedConfigurationInfo( int nDXLevel, KeyValues *pKeyValues )
+{
+ MaterialLock_t hLock = Lock();
+ bool bResult = g_pShaderDeviceMgr->GetRecommendedConfigurationInfo( m_nAdapter, nDXLevel, pKeyValues );
+ Unlock( hLock );
+ return bResult;
+}
+
+
+//-----------------------------------------------------------------------------
+// For dealing with device lost in cases where SwapBuffers isn't called all the time (Hammer)
+//-----------------------------------------------------------------------------
+void CMaterialSystem::HandleDeviceLost()
+{
+ if ( IsX360() )
+ return;
+
+ g_pShaderAPI->HandleDeviceLost();
+}
+
+bool CMaterialSystem::UsingFastClipping( void )
+{
+ return (HardwareConfig()->UseFastClipping() || (HardwareConfig()->MaxUserClipPlanes() < 1));
+};
+
+int CMaterialSystem::StencilBufferBits( void )
+{
+ return HardwareConfig()->StencilBufferBits();
+}
+
+ITexture* CMaterialSystem::CreateRenderTargetTexture(
+ int w,
+ int h,
+ RenderTargetSizeMode_t sizeMode, // Controls how size is generated (and regenerated on video mode change).
+ ImageFormat format,
+ MaterialRenderTargetDepth_t depth )
+{
+ return CreateNamedRenderTargetTextureEx( NULL, w, h, sizeMode, format, depth, TEXTUREFLAGS_CLAMPS|TEXTUREFLAGS_CLAMPT, 0 );
+}
+
+ITexture* CMaterialSystem::CreateNamedRenderTargetTexture(
+ const char *pRTName,
+ int w,
+ int h,
+ RenderTargetSizeMode_t sizeMode, // Controls how size is generated (and regenerated on video mode change).
+ ImageFormat format,
+ MaterialRenderTargetDepth_t depth,
+ bool bClampTexCoords,
+ bool bAutoMipMap )
+{
+ unsigned int textureFlags = 0;
+ if ( bClampTexCoords )
+ {
+ textureFlags |= TEXTUREFLAGS_CLAMPS | TEXTUREFLAGS_CLAMPT;
+ }
+
+ unsigned int renderTargetFlags = 0;
+ if ( bAutoMipMap )
+ {
+ renderTargetFlags |= CREATERENDERTARGETFLAGS_AUTOMIPMAP;
+ }
+
+ return CreateNamedRenderTargetTextureEx( pRTName, w, h, sizeMode, format, depth, textureFlags, renderTargetFlags );
+}
+
+ITexture* CMaterialSystem::CreateNamedRenderTargetTextureEx(
+ const char *pRTName,
+ int w,
+ int h,
+ RenderTargetSizeMode_t sizeMode, // Controls how size is generated (and regenerated on video mode change).
+ ImageFormat format,
+ MaterialRenderTargetDepth_t depth,
+ unsigned int textureFlags,
+ unsigned int renderTargetFlags )
+{
+ RenderTargetType_t rtType;
+
+ bool gl_canMixTargetSizes = (HardwareConfig() && HardwareConfig()->SupportsGLMixedSizeTargets());
+
+ // On GL, the depth buffer for a render target must be the same size (until we pick up mixed-sized attachments in 10.6.3)
+ if ( (!gl_canMixTargetSizes && IsPosix()) || IsEmulatingGL() )
+ {
+ if ( depth != MATERIAL_RT_DEPTH_SEPARATE && depth != MATERIAL_RT_DEPTH_NONE )
+ {
+ int fbWidth, fbHeight;
+ g_pShaderAPI->GetBackBufferDimensions( fbWidth, fbHeight );
+
+ if ( sizeMode != RT_SIZE_FULL_FRAME_BUFFER )
+ {
+ if ( w != fbWidth || h != fbHeight )
+ {
+ depth = MATERIAL_RT_DEPTH_SEPARATE;
+ }
+ }
+ }
+ }
+
+ // Determine RT type based on depth buffer requirements
+ switch ( depth )
+ {
+ case MATERIAL_RT_DEPTH_SEPARATE:
+ // using own depth buffer
+ rtType = RENDER_TARGET_WITH_DEPTH;
+ break;
+ case MATERIAL_RT_DEPTH_NONE:
+ // no depth buffer
+ rtType = RENDER_TARGET_NO_DEPTH;
+ break;
+ case MATERIAL_RT_DEPTH_ONLY:
+ // only depth buffer
+ rtType = RENDER_TARGET_ONLY_DEPTH;
+ break;
+ case MATERIAL_RT_DEPTH_SHARED:
+ default:
+ // using shared depth buffer
+ rtType = RENDER_TARGET;
+ break;
+ }
+
+ ITextureInternal* pTex = TextureManager()->CreateRenderTargetTexture( pRTName, w, h, sizeMode, format, rtType, textureFlags, renderTargetFlags );
+ pTex->IncrementReferenceCount();
+
+#if defined( _X360 )
+ if ( !( renderTargetFlags & CREATERENDERTARGETFLAGS_NOEDRAM ) )
+ {
+ // create the EDRAM surface that is bound to the RT Texture
+ pTex->CreateRenderTargetSurface( 0, 0, IMAGE_FORMAT_UNKNOWN, true );
+ }
+#endif
+
+ // If we're not in a BeginRenderTargetAllocation-EndRenderTargetAllocation block
+ // because we're being called by a legacy path (i.e. a mod), force an Alt-Tab after every
+ // RT allocation to ensure that all RTs get priority during allocation
+ if ( !m_bAllocatingRenderTargets )
+ {
+ EndRenderTargetAllocation();
+ }
+
+ return pTex;
+}
+
+//-----------------------------------------------------------------------------------------------------
+// New version which must be called inside BeginRenderTargetAllocation-EndRenderTargetAllocation block
+//-----------------------------------------------------------------------------------------------------
+ITexture *CMaterialSystem::CreateNamedRenderTargetTextureEx2(
+ const char *pRTName,
+ int w,
+ int h,
+ RenderTargetSizeMode_t sizeMode, // Controls how size is generated (and regenerated on video mode change).
+ ImageFormat format,
+ MaterialRenderTargetDepth_t depth,
+ unsigned int textureFlags,
+ unsigned int renderTargetFlags )
+{
+ // Only proceed if we are between BeginRenderTargetAllocation and EndRenderTargetAllocation
+ if ( !m_bAllocatingRenderTargets )
+ {
+ Warning( "Tried to create render target outside of CMaterialSystem::BeginRenderTargetAllocation/EndRenderTargetAllocation block\n" );
+ return NULL;
+ }
+
+ ITexture* pTexture = CreateNamedRenderTargetTextureEx( pRTName, w, h, sizeMode, format, depth, textureFlags, renderTargetFlags );
+
+ pTexture->DecrementReferenceCount(); // Follow the same convention as CTextureManager::LoadTexture (return refcount of 0).
+ return pTexture;
+}
+
+class CTextureBitsRegenerator : public ITextureRegenerator
+{
+public:
+ CTextureBitsRegenerator( int w, int h, int mips, ImageFormat fmt, int srcBufferSize, byte* srcBits )
+ : m_nWidth( w )
+ , m_nHeight( h )
+ , m_nMipmaps( mips )
+ , m_ImageFormat( fmt )
+ {
+ Assert( srcBits );
+ Assert( srcBufferSize > 0 );
+ Assert( m_nMipmaps != 0 );
+
+ // If these fail, we'll crash later, so look to before here for the problem.
+ Assert( ImageLoader::GetMemRequired( w, h, 1, fmt, m_nMipmaps > 1 ? true : false ) <= srcBufferSize );
+ Assert( m_nMipmaps == 1 || m_nMipmaps == ImageLoader::GetNumMipMapLevels( m_nWidth, m_nHeight, 1 ) );
+
+
+ m_ImageData.EnsureCapacity( srcBufferSize );
+ Q_memcpy( m_ImageData.Base(), srcBits, srcBufferSize );
+ }
+
+ virtual void RegenerateTextureBits( ITexture *pTexture, IVTFTexture *pVTFTexture, Rect_t *pRect )
+ {
+ Assert( pVTFTexture->FrameCount() == 1 );
+ Assert( pVTFTexture->FaceCount() == 1 );
+
+ int destWidth, destHeight, destDepth;
+ pVTFTexture->ComputeMipLevelDimensions( 0, &destWidth, &destHeight, &destDepth );
+ Assert( destDepth == 1 );
+ Assert( destWidth <= m_nWidth && destHeight <= m_nHeight );
+
+ unsigned char* pDest = pVTFTexture->ImageData();
+ ImageFormat destFmt = pVTFTexture->Format();
+
+ if ( destFmt == m_ImageFormat && destWidth == m_nWidth && destHeight == m_nHeight )
+ {
+ Q_memcpy( pDest, m_ImageData.Base(), m_ImageData.NumAllocated() );
+ }
+ else
+ {
+ int srcResX = m_nWidth;
+ int srcResY = m_nHeight;
+ int srcOffset = 0;
+ int dstOffset = 0;
+ int mip = 0;
+
+ // Skip the mips we're not including.
+ while ( mip < m_nMipmaps && ( srcResX > destWidth || srcResY > destHeight ) )
+ {
+ srcOffset += ImageLoader::GetMemRequired( srcResX, srcResY, 1, m_ImageFormat, false );
+
+ srcResX = Max( 1, ( srcResX >> 1 ) );
+ srcResY = Max( 1, ( srcResY >> 1 ) );
+
+ mip++;
+ }
+ // Assert we're where we expect to be now.
+ Assert( srcResX == destWidth && srcResY == destHeight );
+
+ for ( ; mip < m_nMipmaps; ++mip )
+ {
+ // Convert this mipmap level.
+ ImageLoader::ConvertImageFormat( m_ImageData.Base() + srcOffset, m_ImageFormat, pDest + dstOffset, destFmt, srcResX, srcResY );
+
+ // Then update offsets for the next mipmap level.
+ srcOffset += ImageLoader::GetMemRequired( srcResX, srcResY, 1, m_ImageFormat, false );
+ dstOffset += ImageLoader::GetMemRequired( srcResX, srcResY, 1, destFmt, false );
+
+ srcResX = Max( 1, ( srcResX >> 1 ) );
+ srcResY = Max( 1, ( srcResY >> 1 ) );
+ }
+ }
+ }
+
+ virtual void Release()
+ {
+ delete this;
+ }
+
+private:
+ int m_nWidth;
+ int m_nHeight;
+ int m_nMipmaps;
+ ImageFormat m_ImageFormat;
+ CUtlMemory<byte> m_ImageData;
+};
+
+ITexture* CMaterialSystem::CreateTextureFromBits(int w, int h, int mips, ImageFormat fmt, int srcBufferSize, byte* srcBits)
+{
+ int flags = TEXTUREFLAGS_SINGLECOPY
+ | ( mips > 1
+ ? TEXTUREFLAGS_ALL_MIPS
+ : TEXTUREFLAGS_NOMIP )
+ ;
+
+ return CreateNamedTextureFromBitsEx( "frombits", TEXTURE_GROUP_OTHER, w, h, mips, fmt, srcBufferSize, srcBits, flags );
+}
+
+void CMaterialSystem::OverrideRenderTargetAllocation( bool rtAlloc )
+{
+ m_bAllocatingRenderTargets = rtAlloc;
+}
+
+ITextureCompositor* CMaterialSystem::NewTextureCompositor( int w, int h, const char* pCompositeName, int nTeamNum, uint64 randomSeed, KeyValues* stageDesc, uint32 texCompositeCreateFlags )
+{
+ return CreateTextureCompositor( w, h, pCompositeName, nTeamNum, randomSeed, stageDesc, texCompositeCreateFlags );
+}
+
+void CMaterialSystem::ScheduleTextureComposite( CTextureCompositor* _texCompositor )
+{
+ Assert( _texCompositor != NULL );
+ _texCompositor->AddRef();
+ m_scheduledComposites.AddToTail( _texCompositor );
+}
+
+void CMaterialSystem::AsyncFindTexture( const char* pFilename, const char *pTextureGroupName, IAsyncTextureOperationReceiver* pRecipient, void* pExtraArgs, bool bComplain, int nAdditionalCreationFlags )
+{
+ Assert( pFilename != NULL );
+ Assert( pTextureGroupName != NULL );
+ Assert( pRecipient != NULL );
+
+ // Bump the ref count on the recipient before handing it off. This ensures the receiver won't go away before we have completed our work.
+ pRecipient->AddRef();
+
+ TextureManager()->AsyncFindOrLoadTexture( pFilename, pTextureGroupName, pRecipient, pExtraArgs, bComplain, nAdditionalCreationFlags );
+}
+
+// creates a texture suitable for use with materials from a raw stream of bits.
+// The bits will be retained by the material system and can be freed upon return.
+ITexture *CMaterialSystem::CreateNamedTextureFromBitsEx( const char* pName, const char *pTextureGroupName, int w, int h, int mips, ImageFormat fmt, int srcBufferSize, byte* srcBits, int nFlags )
+{
+ Assert( srcBits );
+
+ CTextureBitsRegenerator* regen = new CTextureBitsRegenerator( w, h, mips, fmt, srcBufferSize, srcBits );
+ ITextureInternal* tex = TextureManager()->CreateProceduralTexture( pName, pTextureGroupName, w, h, 1, fmt, nFlags, regen );
+ return tex;
+}
+
+bool CMaterialSystem::AddTextureCompositorTemplate( const char* pName, KeyValues* pTmplDesc, int /* nTexCompositeTemplateFlags */ )
+{
+ // Flags are currently unused, but added for futureproofing.
+ return TextureManager()->AddTextureCompositorTemplate( pName, pTmplDesc );
+}
+
+bool CMaterialSystem::VerifyTextureCompositorTemplates()
+{
+ return TextureManager()->VerifyTextureCompositorTemplates();
+}
+
+
+void CMaterialSystem::BeginRenderTargetAllocation( void )
+{
+ g_pShaderAPI->FlushBufferedPrimitives();
+ m_bAllocatingRenderTargets = true;
+}
+
+void CMaterialSystem::EndRenderTargetAllocation( void )
+{
+ // Any GPU newer than 2005 doesn't need to do this, and it eats up ~40% of our level load time!
+ const bool cbRequiresRenderTargetAllocationFirst = mat_requires_rt_alloc_first.GetBool();
+
+ g_pShaderAPI->FlushBufferedPrimitives();
+ m_bAllocatingRenderTargets = false;
+
+ if ( IsPC() && cbRequiresRenderTargetAllocationFirst && g_pShaderAPI->CanDownloadTextures() )
+ {
+ // Simulate an Alt-Tab...will cause RTs to be allocated first
+
+ g_pShaderDevice->ReleaseResources();
+ g_pShaderDevice->ReacquireResources();
+ }
+
+ TextureManager()->CacheExternalStandardRenderTargets();
+}
+
+void CMaterialSystem::SetRenderTargetFrameBufferSizeOverrides( int nWidth, int nHeight )
+{
+ m_nRenderTargetFrameBufferWidthOverride = nWidth;
+ m_nRenderTargetFrameBufferHeightOverride = nHeight;
+}
+
+
+void CMaterialSystem::GetRenderTargetFrameBufferDimensions( int & nWidth, int & nHeight )
+{
+ if( m_nRenderTargetFrameBufferHeightOverride && m_nRenderTargetFrameBufferWidthOverride )
+ {
+ nWidth = m_nRenderTargetFrameBufferWidthOverride;
+ nHeight = m_nRenderTargetFrameBufferHeightOverride;
+ }
+ else
+ {
+ GetBackBufferDimensions( nWidth, nHeight );
+ }
+}
+
+
+//-----------------------------------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------------------------------
+void CMaterialSystem::UpdateLightmap( int lightmapPageID, int lightmapSize[2],
+ int offsetIntoLightmapPage[2],
+ float *pFloatImage, float *pFloatImageBump1,
+ float *pFloatImageBump2, float *pFloatImageBump3 )
+{
+ CMatCallQueue *pCallQueue = GetRenderCallQueue();
+ if ( !pCallQueue )
+ {
+ m_Lightmaps.UpdateLightmap( lightmapPageID, lightmapSize, offsetIntoLightmapPage, pFloatImage, pFloatImageBump1, pFloatImageBump2, pFloatImageBump3 );
+ }
+ else
+ {
+ ExecuteOnce( DebuggerBreakIfDebugging() );
+ }
+}
+
+//-----------------------------------------------------------------------------------------------------
+// 360 TTF Font Support
+//-----------------------------------------------------------------------------------------------------
+#if defined( _X360 )
+HXUIFONT CMaterialSystem::OpenTrueTypeFont( const char *pFontname, int tall, int style )
+{
+ MaterialLock_t hLock = Lock();
+ HXUIFONT result = g_pShaderAPI->OpenTrueTypeFont( pFontname, tall, style );
+ Unlock( hLock );
+ return result;
+}
+void CMaterialSystem::CloseTrueTypeFont( HXUIFONT hFont )
+{
+ MaterialLock_t hLock = Lock();
+ g_pShaderAPI->CloseTrueTypeFont( hFont );
+ Unlock( hLock );
+}
+bool CMaterialSystem::GetTrueTypeFontMetrics( HXUIFONT hFont, XUIFontMetrics *pFontMetrics, XUICharMetrics charMetrics[256] )
+{
+ MaterialLock_t hLock = Lock();
+ bool result = g_pShaderAPI->GetTrueTypeFontMetrics( hFont, pFontMetrics, charMetrics );
+ Unlock( hLock );
+ return result;
+}
+bool CMaterialSystem::GetTrueTypeGlyphs( HXUIFONT hFont, int numChars, wchar_t *pWch, int *pOffsetX, int *pOffsetY, int *pWidth, int *pHeight, unsigned char *pRGBA, int *pRGBAOffset )
+{
+ MaterialLock_t hLock = Lock();
+ bool result = g_pShaderAPI->GetTrueTypeGlyphs( hFont, numChars, pWch, pOffsetX, pOffsetY, pWidth, pHeight, pRGBA, pRGBAOffset );
+ Unlock( hLock );
+ return result;
+}
+#endif
+
+//-----------------------------------------------------------------------------------------------------
+// 360 Back Buffer access. Due to hardware, RT data must be blitted from EDRAM
+// and converted.
+//-----------------------------------------------------------------------------------------------------
+#if defined( _X360 )
+void CMaterialSystem::ReadBackBuffer( Rect_t *pSrcRect, Rect_t *pDstRect, unsigned char *pDstData, ImageFormat dstFormat, int dstStride )
+{
+ Assert( pSrcRect && pDstRect && pDstData );
+
+ int fbWidth, fbHeight;
+ g_pShaderAPI->GetBackBufferDimensions( fbWidth, fbHeight );
+
+ if ( pDstRect->width > fbWidth || pDstRect->height > fbHeight )
+ {
+ Assert( 0 );
+ return;
+ }
+
+ // intermediate results will be placed at (0,0)
+ Rect_t rect;
+ rect.x = 0;
+ rect.y = 0;
+ rect.width = pDstRect->width;
+ rect.height = pDstRect->height;
+
+ ITexture *pTempRT;
+ bool bStretch = ( pSrcRect->width != pDstRect->width || pSrcRect->height != pDstRect->height );
+ if ( !bStretch )
+ {
+ // hijack an unused RT (no surface required) for 1:1 resolve work, fastest path
+ pTempRT = FindTexture( "_rt_FullFrameFB", TEXTURE_GROUP_RENDER_TARGET );
+ }
+ else
+ {
+ // hijack an unused RT (with surface abilities) for stretch work, slower path
+ pTempRT = FindTexture( "_rt_WaterReflection", TEXTURE_GROUP_RENDER_TARGET );
+ }
+
+ Assert( !pTempRT->IsError() && pDstRect->width <= pTempRT->GetActualWidth() && pDstRect->height <= pTempRT->GetActualHeight() );
+ GetRenderContextInternal()->CopyRenderTargetToTextureEx( pTempRT, 0, pSrcRect, &rect );
+
+ // access the RT bits
+ CPixelWriter writer;
+ g_pShaderAPI->ModifyTexture( ((ITextureInternal*)pTempRT)->GetTextureHandle( 0 ) );
+ if ( !g_pShaderAPI->TexLock( 0, 0, 0, 0, pTempRT->GetActualWidth(), pTempRT->GetActualHeight(), writer ) )
+ return;
+
+ // this will be adequate for non-block formats
+ int srcStride = pTempRT->GetActualWidth() * ImageLoader::SizeInBytes( pTempRT->GetImageFormat() );
+
+ // untile intermediate RT in place to achieve linear access
+ XGUntileTextureLevel(
+ pTempRT->GetActualWidth(),
+ pTempRT->GetActualHeight(),
+ 0,
+ XGGetGpuFormat( ImageLoader::ImageFormatToD3DFormat( pTempRT->GetImageFormat() ) ),
+ 0,
+ (char*)writer.GetPixelMemory(),
+ srcStride,
+ NULL,
+ writer.GetPixelMemory(),
+ NULL );
+
+ // swap back to x86 order as expected by image conversion
+ ImageLoader::ByteSwapImageData( (unsigned char*)writer.GetPixelMemory(), srcStride*pTempRT->GetActualHeight(), pTempRT->GetImageFormat() );
+
+ // convert to callers format
+ Assert( dstFormat == IMAGE_FORMAT_RGB888 );
+ ImageLoader::ConvertImageFormat( (unsigned char*)writer.GetPixelMemory(), pTempRT->GetImageFormat(), pDstData, dstFormat, pDstRect->width, pDstRect->height, srcStride, dstStride );
+
+ g_pShaderAPI->TexUnlock();
+}
+#endif
+
+#if defined( _X360 )
+void CMaterialSystem::PersistDisplay()
+{
+ g_pShaderAPI->PersistDisplay();
+}
+#endif
+
+#if defined( _X360 )
+void *CMaterialSystem::GetD3DDevice()
+{
+ return g_pShaderAPI->GetD3DDevice();
+}
+#endif
+
+#if defined( _X360 )
+bool CMaterialSystem::OwnGPUResources( bool bEnable )
+{
+ return g_pShaderAPI->OwnGPUResources( bEnable );
+}
+#endif
+
+//-----------------------------------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------------------------------
+class CThreadRelease : public CJob
+{
+ virtual JobStatus_t DoExecute()
+ {
+ g_pShaderAPI->ReleaseThreadOwnership();
+
+ return JOB_OK;
+ }
+};
+
+
+void CMaterialSystem::ThreadRelease( )
+{
+ if ( !m_bThreadHasOwnership )
+ {
+ return;
+ }
+
+ double flStartTime, flEndThreadRelease, flEndTime;
+ int do_report = mat_queue_report.GetInt();
+
+ if ( do_report )
+ {
+ flStartTime = Plat_FloatTime();
+ }
+
+ CJob *pActiveAsyncJob = new CThreadRelease();
+ IThreadPool *pThreadPool = CreateMatQueueThreadPool();
+ pThreadPool->AddJob( pActiveAsyncJob );
+ pActiveAsyncJob->WaitForFinish();
+
+ SafeRelease( pActiveAsyncJob );
+
+ if ( do_report )
+ {
+ flEndThreadRelease = Plat_FloatTime();
+ }
+
+ g_pShaderAPI->AcquireThreadOwnership();
+
+ m_bThreadHasOwnership = false;
+ m_ThreadOwnershipID = 0;
+
+ if ( do_report )
+ {
+ flEndTime = Plat_FloatTime();
+ double flResult = ( flEndTime - flStartTime ) * 1000.0;
+
+ if ( do_report == -1 || flResult > mat_queue_report.GetFloat() )
+ {
+ Color red( 200, 20, 20, 255 );
+ ConColorMsg( red, "CMaterialSystem::ThreadRelease: %0.2fms = Release:%0.2fms + Acquire:%0.2fms\n", flResult, ( flEndThreadRelease - flStartTime ) * 1000.0, ( flEndTime - flEndThreadRelease ) * 1000.0 );
+ }
+ }
+}
+
+
+void CMaterialSystem::ThreadAcquire( bool bForce )
+{
+ if ( !bForce )
+ {
+ return;
+ }
+
+ double flStartTime, flEndTime;
+ int do_report = mat_queue_report.GetInt();
+
+ if ( do_report )
+ {
+ flStartTime = Plat_FloatTime();
+ }
+
+ g_pShaderAPI->ReleaseThreadOwnership();
+
+ CJob *pActiveAsyncJob = new CThreadAcquire();
+ IThreadPool *pThreadPool = CreateMatQueueThreadPool();
+ pThreadPool->AddJob( pActiveAsyncJob );
+// while we could wait for this job to finish, there's no reason too
+// pActiveAsyncJob->WaitForFinish();
+
+ SafeRelease( pActiveAsyncJob );
+
+ m_bThreadHasOwnership = true;
+ m_ThreadOwnershipID = ThreadGetCurrentId();
+
+ if ( do_report )
+ {
+ flEndTime = Plat_FloatTime();
+ double flResult = ( flEndTime - flStartTime ) * 1000.0;
+
+ if ( do_report == -1 || flResult > mat_queue_report.GetFloat() )
+ {
+ Color red( 200, 20, 20, 255 );
+ ConColorMsg( red, "CMaterialSystem::ThreadAcquire: %0.2fms\n", flResult );
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------------------------------
+MaterialLock_t CMaterialSystem::Lock()
+{
+ double flStartTime;
+ int do_report = mat_queue_report.GetInt();
+
+ if ( do_report )
+ {
+ flStartTime = Plat_FloatTime();
+ }
+
+ IMatRenderContextInternal *pCurContext = GetRenderContextInternal();
+#if 1 // Rick's optimization: not sure this is needed anymore
+ if ( pCurContext != &m_HardwareRenderContext && m_pActiveAsyncJob )
+ {
+ m_pActiveAsyncJob->WaitForFinish();
+ // threadsafety note: not releasing or nulling pointer.
+ }
+
+ if ( m_ThreadMode != MATERIAL_SINGLE_THREADED )
+ {
+ TelemetrySetLockName( TELEMETRY_LEVEL0, (char const *)&g_MatSysMutex, "MatSysMutex" );
+
+ tmTryLock( TELEMETRY_LEVEL0, (char const *)&g_MatSysMutex, "CMaterialSystem" );
+ g_MatSysMutex.Lock();
+ tmEndTryLock( TELEMETRY_LEVEL0, (char const *)&g_MatSysMutex, TMLR_SUCCESS );
+ tmSetLockState( TELEMETRY_LEVEL0, (char const *)&g_MatSysMutex, TMLS_LOCKED, "CMaterialSystem" );
+ }
+#endif
+
+ MaterialLock_t hMaterialLock = (MaterialLock_t)pCurContext;
+ m_pRenderContext.Set( &m_HardwareRenderContext );
+
+ if ( m_ThreadMode != MATERIAL_SINGLE_THREADED )
+ {
+ g_pShaderAPI->SetDisallowAccess( false );
+ if ( pCurContext->GetCallQueueInternal() )
+ {
+ ThreadRelease();
+ }
+ }
+
+ g_pShaderAPI->ShaderLock();
+
+ if ( do_report )
+ {
+ double flEndTime = Plat_FloatTime();
+ double flResult = ( flEndTime - flStartTime ) * 1000.0;
+
+ if ( do_report == -1 || flResult > mat_queue_report.GetFloat() )
+ {
+ Color red( 200, 20, 20, 255 );
+ ConColorMsg( red, "*CMaterialSystem::Lock: %0.2fms\n", flResult );
+ }
+ }
+
+ return hMaterialLock;
+}
+
+//-----------------------------------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------------------------------
+void CMaterialSystem::Unlock( MaterialLock_t hMaterialLock )
+{
+ double flStartTime;
+ int do_report = mat_queue_report.GetInt();
+
+ if ( do_report )
+ {
+ flStartTime = Plat_FloatTime();
+ }
+
+ IMatRenderContextInternal *pRenderContext = (IMatRenderContextInternal *)hMaterialLock;
+ m_pRenderContext.Set( pRenderContext );
+ g_pShaderAPI->ShaderUnlock();
+
+#ifdef MAT_QUEUE_MODE_PROFILE
+ if ( m_ThreadMode == MATERIAL_QUEUED_SINGLE_THREADED )
+ {
+ g_pShaderAPI->SetDisallowAccess( true );
+ }
+ else
+#endif
+ if ( m_ThreadMode == MATERIAL_QUEUED_THREADED )
+ {
+ if ( pRenderContext->GetCallQueueInternal() )
+ {
+ ThreadAcquire();
+ }
+ }
+
+#if 1 // Rick's optimization: not sure this is needed anymore
+ if ( m_ThreadMode != MATERIAL_SINGLE_THREADED )
+ {
+ g_MatSysMutex.Unlock();
+ tmSetLockState( TELEMETRY_LEVEL0, (char const *)&g_MatSysMutex, TMLS_RELEASED, "CMaterialSystem" );
+ }
+#endif
+
+ if ( do_report )
+ {
+ double flEndTime = Plat_FloatTime();
+ double flResult = ( flEndTime - flStartTime ) * 1000.0;
+
+ if ( do_report || flResult > mat_queue_report.GetFloat() )
+ {
+ Color red( 200, 20, 20, 255 );
+ ConColorMsg( red, "*CMaterialSystem::Unlock: %0.2fms\n", flResult );
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------------------------------
+CMatCallQueue *CMaterialSystem::GetRenderCallQueue()
+{
+ IMatRenderContextInternal *pRenderContext = m_pRenderContext.Get();
+ return pRenderContext ? pRenderContext->GetCallQueueInternal() : NULL;
+}
+
+void CMaterialSystem::UnbindMaterial( IMaterial *pMaterial )
+{
+ Assert( (pMaterial == NULL) || ((IMaterialInternal *)pMaterial)->IsRealTimeVersion() );
+ if ( m_HardwareRenderContext.GetCurrentMaterial() == pMaterial )
+ {
+ m_HardwareRenderContext.Bind( g_pErrorMaterial, NULL );
+ }
+}
+
+
+
+class CReplacementProxy : public IMaterialProxy
+{
+public:
+ CReplacementProxy( void );
+ virtual ~CReplacementProxy( void );
+ virtual bool Init( IMaterial *pMaterial, KeyValues *pKeyValues );
+ virtual void OnBind( void * );
+ virtual void Release( );
+ virtual IMaterial * GetMaterial( );
+
+private:
+ IMaterial *m_pReplaceMaterial;
+};
+
+
+#define REPLACEMENT_NAME "_replacement"
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CReplacementProxy::CReplacementProxy( void ) : m_pReplaceMaterial ( NULL )
+{
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CReplacementProxy::~CReplacementProxy( void )
+{
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Get pointer to the color value
+// Input : *pMaterial -
+//-----------------------------------------------------------------------------
+bool CReplacementProxy::Init( IMaterial *pMaterial, KeyValues *pKeyValues )
+{
+ const char *pszFileName = pMaterial->GetName();
+ char szNewName[ MAX_PATH ];
+
+ V_sprintf_safe( szNewName, "%s" REPLACEMENT_NAME, pszFileName );
+ m_pReplaceMaterial = materials->CreateMaterial( szNewName, pKeyValues );
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input :
+//-----------------------------------------------------------------------------
+void CReplacementProxy::OnBind( void * )
+{
+}
+
+
+void CReplacementProxy::Release( )
+{
+ m_pReplaceMaterial->DecrementReferenceCount();
+ // Since we have a material-holding-a-material situation here, we need to nuke these if unreferenced to prevent the
+ // engine needing to double-call UncacheUnusedMaterials to actually get rid of all materials.
+ m_pReplaceMaterial->DeleteIfUnreferenced();
+ m_pReplaceMaterial = NULL;
+}
+
+
+
+IMaterial *CReplacementProxy::GetMaterial()
+{
+ static ConVarRef localplayer_visionflags( "localplayer_visionflags" );
+ bool bVisionOverride = ( localplayer_visionflags.IsValid() && ( localplayer_visionflags.GetInt() & ( 0x01 ) ) ); // Pyro-vision Goggles
+
+ if ( bVisionOverride )
+ {
+ return m_pReplaceMaterial;
+ }
+
+ return NULL;
+}
+
+
+EXPOSE_INTERFACE( CReplacementProxy, IMaterialProxy, "replace_proxy" IMATERIAL_PROXY_INTERFACE_VERSION );
+
+
+static const char *pszReplacementForceCopy[] =
+{
+ "$nocull",
+
+ NULL
+};
+
+void CMaterialSystem::LoadReplacementMaterials()
+{
+ const char* cLocation = "materials";
+ if ( CommandLine()->FindParm( "-matscan") ) {
+ ScanDirForReplacements( cLocation );
+ } else {
+ InitReplacementsFromFile( cLocation );
+ }
+}
+
+void CMaterialSystem::ScanDirForReplacements( const char *pszPathName )
+{
+ char szBaseName[ MAX_PATH ];
+
+ V_sprintf_safe( szBaseName, "%s/replacements.vmt", pszPathName );
+ if ( g_pFullFileSystem->FileExists( szBaseName ) )
+ {
+ KeyValues *pKV = g_pFullFileSystem->LoadKeyValues( IFileSystem::TYPE_VMT, szBaseName );
+ if ( pKV )
+ {
+ V_sprintf_safe( szBaseName, "%s/", pszPathName );
+ m_Replacements.Insert( szBaseName, pKV );
+ }
+ }
+
+ V_sprintf_safe( szBaseName, "%s/*", pszPathName );
+
+ FileFindHandle_t FindHandle;
+ const char *pFindFileName = g_pFullFileSystem->FindFirst( szBaseName, &FindHandle );
+
+ while ( pFindFileName && pFindFileName[ 0 ] != '\0' )
+ {
+ if ( g_pFullFileSystem->FindIsDirectory( FindHandle ) )
+ {
+ if ( strcmp( pFindFileName, "." ) != 0 && strcmp( pFindFileName, ".." ) != 0 )
+ {
+ char szNextBaseName[ MAX_PATH ];
+
+ V_sprintf_safe( szNextBaseName, "%s/%s", pszPathName, pFindFileName );
+ ScanDirForReplacements( szNextBaseName );
+ }
+ }
+
+ pFindFileName = g_pFullFileSystem->FindNext( FindHandle );
+ }
+
+ g_pFullFileSystem->FindClose( FindHandle );
+
+}
+
+void CMaterialSystem::InitReplacementsFromFile( const char *pszPathName )
+{
+ CUtlVector<char*> replacementFiles;
+ char szBaseName[MAX_PATH];
+ V_sprintf_safe( szBaseName, "%s/replacements.txt", pszPathName );
+
+ int replacementCount = ReadListFromFile( &replacementFiles, szBaseName );
+
+ for ( int i = 0; i < replacementCount; ++i )
+ {
+ V_snprintf( szBaseName, sizeof(szBaseName), "%s/%s/replacements.vmt", pszPathName, replacementFiles[i] );
+ if ( g_pFullFileSystem->FileExists(szBaseName) )
+ {
+ KeyValues *pKV = g_pFullFileSystem->LoadKeyValues( IFileSystem::TYPE_VMT, szBaseName );
+ if (pKV)
+ {
+ V_sprintf_safe( szBaseName, "%s/%s/", pszPathName, replacementFiles[i] );
+ m_Replacements.Insert( szBaseName, pKV );
+ }
+ }
+ }
+
+ replacementFiles.PurgeAndDeleteElements();
+}
+
+void CMaterialSystem::PreloadReplacements( )
+{
+ int nIndex = m_Replacements.First();
+ while( m_Replacements.IsValidIndex( nIndex ) )
+ {
+ m_Replacements.Element( nIndex )->deleteThis();
+
+ nIndex = m_Replacements.Next( nIndex );
+ }
+ m_Replacements.Purge();
+
+ COM_TimestampedLog( "LoadReplacementMaterials(): Begin" );
+ LoadReplacementMaterials();
+ COM_TimestampedLog( "LoadReplacementMaterials(): End" );
+
+ m_bReplacementFilesValid = true;
+}
+
+
+IMaterialProxy *CMaterialSystem::DetermineProxyReplacements( IMaterial *pMaterial, KeyValues *pFallbackKeyValues )
+{
+ CReplacementProxy *pReplacementProxy = NULL;
+
+ if ( !g_pMaterialSystemHardwareConfig->SupportsPixelShaders_2_0() )
+ {
+ return NULL;
+ }
+
+ if ( !m_bReplacementFilesValid )
+ {
+ PreloadReplacements();
+ }
+
+ const char *pszMaterialName = pMaterial->GetName();
+
+ char szCheckPath[ MAX_PATH ], szCheckName[ MAX_PATH ], szLastPath[ MAX_PATH ];
+ const char *pszShadername = pFallbackKeyValues->GetName();
+
+ V_strcpy_safe( szLastPath, pszMaterialName );
+ int nLength = strlen( szLastPath ) - strlen( REPLACEMENT_NAME );
+ if ( nLength > 0 && strcmpi( &szLastPath[ nLength ], REPLACEMENT_NAME ) == 0 )
+ {
+ return NULL;
+ }
+
+ while( 1 )
+ {
+ const char *pszRemoveSlashes;
+ V_ExtractFilePath( szLastPath, szCheckPath, sizeof( szCheckPath ) );
+
+ pszRemoveSlashes = szCheckPath;
+ while ( ( *pszRemoveSlashes ) != 0 && ( ( *pszRemoveSlashes ) == '/' || ( *pszRemoveSlashes ) == '\\' ) )
+ {
+ pszRemoveSlashes++;
+ }
+
+ V_sprintf_safe( szCheckName, "materials/%s", pszRemoveSlashes );
+ int nIndex = m_Replacements.Find( szCheckName );
+
+ if ( m_Replacements.IsValidIndex( nIndex ) )
+ {
+ KeyValues *pKV = m_Replacements.Element( nIndex );
+
+ KeyValues *pTemplatesKV = pKV->FindKey( "templates" );
+ KeyValues *pPatternsKV = pKV->FindKey( "patterns" );
+
+ const char *pszFileName = V_GetFileName( pszMaterialName );
+
+ if ( !pTemplatesKV || !pPatternsKV )
+ {
+ Warning( "Replacements: Invalid KV file %s\n", szCheckName );
+ }
+ else
+ {
+ for ( KeyValues *pSubKey = pPatternsKV->GetFirstSubKey(); pSubKey; pSubKey = pSubKey->GetNextKey() )
+ {
+ const char *pszReplacementName = pSubKey->GetName();
+
+// Msg( " Sub: %s\n", pSubKey->GetName() );
+ if ( strnicmp( pszFileName, pszReplacementName, strlen( pszReplacementName ) ) == 0 )
+ { // We found a replacement!
+ const char *pszTemplateName = pSubKey->GetString( "template", NULL );
+ KeyValues *pReplacementMaterial = NULL;
+
+ if ( pszTemplateName && pTemplatesKV )
+ {
+ KeyValues *pTemplateKV = pTemplatesKV->FindKey( pszTemplateName );
+ if ( pTemplateKV )
+ {
+ pTemplateKV = pTemplateKV->FindKey( pszShadername );
+
+ if ( pTemplateKV && pTemplateKV->GetFirstSubKey() )
+ {
+ pReplacementMaterial = pTemplateKV->GetFirstSubKey()->MakeCopy();
+ }
+ }
+ }
+ else
+ {
+ if ( pSubKey->GetFirstSubKey() )
+ {
+ pReplacementMaterial = pSubKey->GetFirstSubKey()->MakeCopy();
+ }
+ }
+
+ if ( !pReplacementMaterial )
+ {
+ break;
+ }
+
+ if ( pReplacementMaterial->GetInt( "$copyall" ) == 1 )
+ {
+ for( KeyValues *pCopyKV = pFallbackKeyValues->GetFirstSubKey(); pCopyKV; pCopyKV = pCopyKV->GetNextKey() )
+ {
+ const char *pszCopyValue = pReplacementMaterial->GetString( pCopyKV->GetName(), NULL );
+ if ( !pszCopyValue )
+ {
+ pReplacementMaterial->SetString( pCopyKV->GetName(), pCopyKV->GetString() );
+ }
+ }
+ }
+ else
+ {
+ int nReplaceIndex = 0;
+
+ while( pszReplacementForceCopy[nReplaceIndex] )
+ {
+ const char *pszCopyValue = pFallbackKeyValues->GetString( pszReplacementForceCopy[nReplaceIndex], NULL );
+ if ( pszCopyValue )
+ {
+ pReplacementMaterial->SetString( pszReplacementForceCopy[nReplaceIndex], pszCopyValue );
+ }
+ nReplaceIndex++;
+ }
+ }
+
+ for( KeyValues *pSearchKV = pReplacementMaterial->GetFirstSubKey(); pSearchKV; pSearchKV = pSearchKV->GetNextKey() )
+ {
+ const char *pszValue = pSearchKV->GetString();
+ if ( pszValue[ 0 ] == '$' )
+ {
+ const char *pszCopyValue = pFallbackKeyValues->GetString( pszValue, NULL );
+ if ( pszCopyValue )
+ {
+ pSearchKV->SetStringValue( pszCopyValue );
+ }
+ else
+ {
+ pSearchKV->SetStringValue( "" );
+ }
+ }
+ }
+ pReplacementProxy = new CReplacementProxy();
+ pReplacementProxy->Init( pMaterial, pReplacementMaterial );
+
+ break;
+ }
+ }
+ }
+
+ if ( pReplacementProxy == NULL )
+ {
+// Msg( "Failed to find: %s\n", GetName() );
+ }
+
+ break;
+ }
+
+ if ( szCheckPath[ 0 ] == 0 )
+ {
+ break;
+ }
+
+ strcpy( szLastPath, szCheckPath );
+ }
+
+ return pReplacementProxy;
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+void CMaterialSystem::CompactMemory()
+{
+ for ( int i = 0; i < ARRAYSIZE(m_QueuedRenderContexts); i++)
+ {
+ m_QueuedRenderContexts[i].CompactMemory();
+ }
+}
+
+void CMaterialSystem::OnRenderingAsyncComplete()
+{
+ Assert( m_pActiveAsyncJob == NULL );
+
+ // Update the texture manager, which may cause some textures to become available for compositing.
+ // Because updating textures may cause textures to swap out their active texture handles, this can only be done
+ // while the async job is not running.
+ bool bThreadHadOwnership = m_bThreadHasOwnership;
+
+ TextureManager()->UpdatePostAsync();
+
+ if ( bThreadHadOwnership && !m_bThreadHasOwnership )
+ ThreadAcquire( true );
+}
+
+//-----------------------------------------------------------------------------
+// Material + texture related commands
+//-----------------------------------------------------------------------------
+void CMaterialSystem::DebugPrintUsedMaterials( const CCommand &args )
+{
+ if( args.ArgC() == 1 )
+ {
+ DebugPrintUsedMaterials( NULL, false );
+ }
+ else
+ {
+ DebugPrintUsedMaterials( args[ 1 ], false );
+ }
+}
+
+void CMaterialSystem::DebugPrintUsedMaterialsVerbose( const CCommand &args )
+{
+ if( args.ArgC() == 1 )
+ {
+ DebugPrintUsedMaterials( NULL, true );
+ }
+ else
+ {
+ DebugPrintUsedMaterials( args[ 1 ], true );
+ }
+}
+
+void CMaterialSystem::DebugPrintUsedTextures( const CCommand &args )
+{
+ DebugPrintUsedTextures();
+}
+
+#if defined( _X360 )
+void CMaterialSystem::ListUsedMaterials( const CCommand &args )
+{
+ ListUsedMaterials();
+}
+#endif // !_X360
+
+void CMaterialSystem::ReloadAllMaterials( const CCommand &args )
+{
+ ReloadMaterials( NULL );
+}
+
+void CMaterialSystem::ReloadMaterials( const CCommand &args )
+{
+ if( args.ArgC() != 2 )
+ {
+ ConWarning( "Usage: mat_reloadmaterial material_name_substring\n"
+ " or mat_reloadmaterial substring1*substring2*...*substringN\n" );
+ return;
+ }
+ ReloadMaterials( args[ 1 ] );
+}
+
+void CMaterialSystem::ReloadTextures( const CCommand &args )
+{
+ ReloadTextures();
+}
+
+CON_COMMAND( mat_hdr_enabled, "Report if HDR is enabled for debugging" )
+{
+ if( HardwareConfig() && HardwareConfig()->GetHDREnabled() )
+ {
+ Warning( "HDR Enabled\n" );
+ }
+ else
+ {
+ Warning( "HDR Disabled\n" );
+ }
+}
+
+
+static int ReadListFromFile(CUtlVector<char*>* outReplacementMaterials, const char *pszPathName)
+{
+ Assert(outReplacementMaterials != NULL);
+ Assert(pszPathName != NULL);
+
+ CUtlBuffer fileContents;
+ if ( !g_pFullFileSystem->ReadFile( pszPathName, NULL, fileContents ) )
+ return 0;
+
+ const char* seps[] = { "\r", "\r\n", "\n" };
+ V_SplitString2( (char*)fileContents.Base(), seps, ARRAYSIZE(seps), *outReplacementMaterials );
+
+
+ return outReplacementMaterials->Size();
+}