diff --git a/.gitignore b/.gitignore index f651c31..3f2cd93 100644 --- a/.gitignore +++ b/.gitignore @@ -1,38 +1,300 @@ -# Files to ignore when considering what GIT should commit. +#Ignore my HL2 Install Directory +/HL2_base/ -# Visual Studio -.vs/ -*.suo -*.user -*.sln.docstates -*.obj -*.pch -*.tlog -*.log -*.scc -*.exp -*.ilk -*.lastbuildstate -vc100.pdb -ipch -*.sdf -*.opensdf -*.idb -*.vcxproj -*.filters -*.vpc_crc -*.sln +#Ignore the sapi folder. +src/utils/sapi51/ + +#Specifically Ignore Builds +game/*/bin/ +game/bin/*.exe +game/bin/*.dll +game/bin/*/*.dll +installer/files/hlvr/bin/ +installer/output/*.exe +installer/output/*.zip + +#Not Marulu's DLL +!d3d9.dll + +#Ignore list, made of the many untracked files +game/*/cfg/config.cfg +game/*/cfg/server_blacklist.txt +game/*/gamestate.txt +game/*/maps/graphs/ +game/*/save/ +game/*/screenshots/ +game/*/downloadlists/ +game/*/stats.txt +game/*/voice_ban.dt +src/game/client/Release_*/ +src/game/server/Release_*/ +mapdev/ +decompiled 0.42/ *.sentinel +#Lib files that are generated by game.sln +src/lib/public/mathlib.lib +src/lib/public/raytrace.lib +src/lib/public/tier1.lib +src/lib/public/vgui_controls.lib -# OSX/Linux build products -*.mak -*.mak.vpc_crc -*.xcodeproj/ -obj*/ -!devtools/*.mak -!utils/smdlexp/smdlexp.mak -# Linux VPC products -compile_commands.json -*.project +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. + +# User-specific files +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Build results +[Dd]ebug_*/ +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease_*/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +bld/ +[Oo]bj/ +[Ll]og/ + +# Visual Studio 2015 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUNIT +*.VisualState.xml +TestResult.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# DNX +project.lock.json +artifacts/ + +*_i.c +*_p.c +*_i.h +*.ilk +*.meta +*.obj +*.pch +*.pdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# JustCode is a .NET coding add-in +.JustCode + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# TODO: Comment the next line if you want to checkin your web deploy settings +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# The packages folder can be ignored because of Package Restore +**/packages/* +# except build/, which is used as an MSBuild target. +!**/packages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/packages/repositories.config +# NuGet v3's project.json files produces more ignoreable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.pfx +*.publishsettings +node_modules/ +orleans.codegen.cs + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm + +# SQL Server files +*.mdf +*.ldf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# JetBrains Rider +.idea/ +*.sln.iml + +# Solutions must be rebuilt by each user, per-machine +*.vcxproj +*.vcxproj.filters +*.vcxproj.vpc_crc +*.sln diff --git a/dx9sdk/include/Msvidctl.tlb b/dx9sdk/include/Msvidctl.tlb deleted file mode 100644 index b501106..0000000 Binary files a/dx9sdk/include/Msvidctl.tlb and /dev/null differ diff --git a/dx9sdk/include/Tuner.tlb b/dx9sdk/include/Tuner.tlb deleted file mode 100644 index c90fc4e..0000000 Binary files a/dx9sdk/include/Tuner.tlb and /dev/null differ diff --git a/dx9sdk/utilities/dx_proxy.pdb b/dx9sdk/utilities/dx_proxy.pdb deleted file mode 100644 index 1265370..0000000 Binary files a/dx9sdk/utilities/dx_proxy.pdb and /dev/null differ diff --git a/engine/voice_codecs/speex/source/libspeex/libspeex.opt b/engine/voice_codecs/speex/source/libspeex/libspeex.opt deleted file mode 100644 index 3a9f5b9..0000000 Binary files a/engine/voice_codecs/speex/source/libspeex/libspeex.opt and /dev/null differ diff --git a/engine/voice_codecs/speex/source/win32/libspeex/2015/debug/libspeex.lib b/engine/voice_codecs/speex/source/win32/libspeex/2015/debug/libspeex.lib deleted file mode 100644 index aec2237..0000000 Binary files a/engine/voice_codecs/speex/source/win32/libspeex/2015/debug/libspeex.lib and /dev/null differ diff --git a/engine/voice_codecs/speex/source/win32/libspeex/2015/release/libspeex.lib b/engine/voice_codecs/speex/source/win32/libspeex/2015/release/libspeex.lib deleted file mode 100644 index 78ee73c..0000000 Binary files a/engine/voice_codecs/speex/source/win32/libspeex/2015/release/libspeex.lib and /dev/null differ diff --git a/engine/voice_codecs/speex/source/win32/libspeex/release/libspeex.lib b/engine/voice_codecs/speex/source/win32/libspeex/release/libspeex.lib deleted file mode 100644 index 8be14a8..0000000 Binary files a/engine/voice_codecs/speex/source/win32/libspeex/release/libspeex.lib and /dev/null differ diff --git a/lib/public/2013/release/cryptlib.lib b/lib/public/2013/release/cryptlib.lib deleted file mode 100644 index ade7046..0000000 Binary files a/lib/public/2013/release/cryptlib.lib and /dev/null differ diff --git a/materialsystem/shaderapidx9/ShaderShadowDx10.h b/materialsystem/shaderapidx9/ShaderShadowDx10.h new file mode 100644 index 0000000..ff987c0 --- /dev/null +++ b/materialsystem/shaderapidx9/ShaderShadowDx10.h @@ -0,0 +1,176 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//===========================================================================// + +#ifndef SHADERSHADOWDX10_H +#define SHADERSHADOWDX10_H + +#ifdef _WIN32 +#pragma once +#endif + +#include "shaderapi/ishaderapi.h" +#include "shaderapi/ishadershadow.h" + + +//----------------------------------------------------------------------------- +// The empty shader shadow +//----------------------------------------------------------------------------- +class CShaderShadowDx10 : public IShaderShadow +{ +public: + CShaderShadowDx10(); + virtual ~CShaderShadowDx10(); + + // Sets the default *shadow* state + void SetDefaultState(); + + // Methods related to depth buffering + void DepthFunc( ShaderDepthFunc_t depthFunc ); + void EnableDepthWrites( bool bEnable ); + void EnableDepthTest( bool bEnable ); + void EnablePolyOffset( PolygonOffsetMode_t nOffsetMode ); + + // Suppresses/activates color writing + void EnableColorWrites( bool bEnable ); + void EnableAlphaWrites( bool bEnable ); + + // Methods related to alpha blending + void EnableBlending( bool bEnable ); + void BlendFunc( ShaderBlendFactor_t srcFactor, ShaderBlendFactor_t dstFactor ); + + // Alpha testing + void EnableAlphaTest( bool bEnable ); + void AlphaFunc( ShaderAlphaFunc_t alphaFunc, float alphaRef /* [0-1] */ ); + + // Wireframe/filled polygons + void PolyMode( ShaderPolyModeFace_t face, ShaderPolyMode_t polyMode ); + + // Back face culling + void EnableCulling( bool bEnable ); + + // constant color + transparency + void EnableConstantColor( bool bEnable ); + + // Indicates the vertex format for use with a vertex shader + // The flags to pass in here come from the VertexFormatFlags_t enum + // If pTexCoordDimensions is *not* specified, we assume all coordinates + // are 2-dimensional + void VertexShaderVertexFormat( unsigned int flags, + int numTexCoords, int* pTexCoordDimensions, + int userDataSize ); + + // Indicates we're going to light the model + void EnableLighting( bool bEnable ); + void EnableSpecular( bool bEnable ); + + // vertex blending + void EnableVertexBlend( bool bEnable ); + + // per texture unit stuff + void OverbrightValue( TextureStage_t stage, float value ); + void EnableTexture( Sampler_t stage, bool bEnable ); + void EnableTexGen( TextureStage_t stage, bool bEnable ); + void TexGen( TextureStage_t stage, ShaderTexGenParam_t param ); + + // alternate method of specifying per-texture unit stuff, more flexible and more complicated + // Can be used to specify different operation per channel (alpha/color)... + void EnableCustomPixelPipe( bool bEnable ); + void CustomTextureStages( int stageCount ); + void CustomTextureOperation( TextureStage_t stage, ShaderTexChannel_t channel, + ShaderTexOp_t op, ShaderTexArg_t arg1, ShaderTexArg_t arg2 ); + + // indicates what per-vertex data we're providing + void DrawFlags( unsigned int drawFlags ); + + // A simpler method of dealing with alpha modulation + void EnableAlphaPipe( bool bEnable ); + void EnableConstantAlpha( bool bEnable ); + void EnableVertexAlpha( bool bEnable ); + void EnableTextureAlpha( TextureStage_t stage, bool bEnable ); + + // GR - Separate alpha blending + void EnableBlendingSeparateAlpha( bool bEnable ); + void BlendFuncSeparateAlpha( ShaderBlendFactor_t srcFactor, ShaderBlendFactor_t dstFactor ); + + // Sets the vertex and pixel shaders + void SetVertexShader( const char *pFileName, int vshIndex ); + void SetPixelShader( const char *pFileName, int pshIndex ); + + // Convert from linear to gamma color space on writes to frame buffer. + void EnableSRGBWrite( bool bEnable ) + { + } + + void EnableSRGBRead( Sampler_t stage, bool bEnable ) + { + } + + virtual void FogMode( ShaderFogMode_t fogMode ) + { + } + virtual void SetDiffuseMaterialSource( ShaderMaterialSource_t materialSource ) + { + } + + virtual void SetMorphFormat( MorphFormat_t flags ) + { + } + + virtual void EnableStencil( bool bEnable ) + { + } + virtual void StencilFunc( ShaderStencilFunc_t stencilFunc ) + { + } + virtual void StencilPassOp( ShaderStencilOp_t stencilOp ) + { + } + virtual void StencilFailOp( ShaderStencilOp_t stencilOp ) + { + } + virtual void StencilDepthFailOp( ShaderStencilOp_t stencilOp ) + { + } + virtual void StencilReference( int nReference ) + { + } + virtual void StencilMask( int nMask ) + { + } + virtual void StencilWriteMask( int nMask ) + { + } + + virtual void DisableFogGammaCorrection( bool bDisable ) + { + //FIXME: empty for now. + } + virtual void FogMode( ShaderFogMode_t fogMode, bool bVertexFog ) + { + //FIXME: empty for now. + } + + // Alpha to coverage + void EnableAlphaToCoverage( bool bEnable ); + + void SetShadowDepthFiltering( Sampler_t stage ); + + // More alpha blending state + void BlendOp( ShaderBlendOp_t blendOp ); + void BlendOpSeparateAlpha( ShaderBlendOp_t blendOp ); + + bool m_IsTranslucent; + bool m_IsAlphaTested; + bool m_bIsDepthWriteEnabled; + bool m_bUsesVertexAndPixelShaders; +}; + + +extern CShaderShadowDx10* g_pShaderShadowDx10; + +#endif // SHADERSHADOWDX10_H \ No newline at end of file diff --git a/materialsystem/shaderapidx9/TransitionTable.cpp b/materialsystem/shaderapidx9/TransitionTable.cpp new file mode 100644 index 0000000..ab12389 --- /dev/null +++ b/materialsystem/shaderapidx9/TransitionTable.cpp @@ -0,0 +1,1924 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//===========================================================================// + +#define DISABLE_PROTECTED_THINGS +#include "togl/rendermechanism.h" +#include "TransitionTable.h" +#include "recording.h" +#include "shaderapidx8.h" +#include "shaderapi/ishaderutil.h" +#include "tier1/convar.h" +#include "materialsystem/imaterialsystemhardwareconfig.h" +#include "vertexshaderdx8.h" +#include "tier0/vprof.h" +#include "shaderdevicedx8.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + + +enum +{ + TEXTURE_STAGE_BIT_COUNT = 4, + TEXTURE_STAGE_MAX_STAGE = 1 << TEXTURE_STAGE_BIT_COUNT, + TEXTURE_STAGE_MASK = TEXTURE_STAGE_MAX_STAGE - 1, + + TEXTURE_OP_BIT_COUNT = 7 - TEXTURE_STAGE_BIT_COUNT, + TEXTURE_OP_SHIFT = TEXTURE_STAGE_BIT_COUNT, + TEXTURE_OP_MASK = ((1 << TEXTURE_OP_BIT_COUNT) - 1) << TEXTURE_OP_SHIFT, +}; + + +//----------------------------------------------------------------------------- +// Texture op compressing/uncompressing +//----------------------------------------------------------------------------- +inline unsigned char TextureOp( TextureStateFunc_t func, int stage ) +{ + // This fails if we've added too many texture stages states to fit in a byte. + COMPILE_TIME_ASSERT( TEXTURE_STATE_COUNT < (1 << TEXTURE_OP_BIT_COUNT) ); + Assert( stage < TEXTURE_STAGE_MAX_STAGE ); + + return ((func << TEXTURE_OP_SHIFT) & TEXTURE_OP_MASK) | (stage & TEXTURE_STAGE_MASK); +} + +inline void GetTextureOp( unsigned char nBits, TextureStateFunc_t *pFunc, int *pStage ) +{ + *pStage = (nBits & TEXTURE_STAGE_MASK); + *pFunc = (TextureStateFunc_t)((nBits & TEXTURE_OP_MASK) >> TEXTURE_OP_SHIFT); +} + + +//----------------------------------------------------------------------------- +// Stats +//----------------------------------------------------------------------------- +static int s_pRenderTransitions[RENDER_STATE_COUNT]; +static int s_pTextureTransitions[TEXTURE_STATE_COUNT][TEXTURE_STAGE_MAX_STAGE]; + + +//----------------------------------------------------------------------------- +// Singleton +//----------------------------------------------------------------------------- +CTransitionTable *g_pTransitionTable = NULL; + +#ifdef DEBUG_BOARD_STATE +inline ShadowState_t& BoardState() +{ + return g_pTransitionTable->BoardState(); +} +#endif + +inline CTransitionTable::CurrentState_t& CurrentState() +{ + return g_pTransitionTable->CurrentState(); +} + + +//----------------------------------------------------------------------------- +// Less functions +//----------------------------------------------------------------------------- +bool CTransitionTable::ShadowStateDictLessFunc::Less( const CTransitionTable::ShadowStateDictEntry_t &src1, const CTransitionTable::ShadowStateDictEntry_t &src2, void *pCtx ) +{ + return src1.m_nChecksum < src2.m_nChecksum; +} + +bool CTransitionTable::SnapshotDictLessFunc::Less( const CTransitionTable::SnapshotDictEntry_t &src1, const CTransitionTable::SnapshotDictEntry_t &src2, void *pCtx ) +{ + return src1.m_nChecksum < src2.m_nChecksum; +} + +bool CTransitionTable::UniqueSnapshotLessFunc::Less( const CTransitionTable::TransitionList_t &src1, const CTransitionTable::TransitionList_t &src2, void *pCtx ) +{ + return src1.m_NumOperations > src2.m_NumOperations; +} + + +//----------------------------------------------------------------------------- +// Constructor, destructor +//----------------------------------------------------------------------------- +CTransitionTable::CTransitionTable() : m_DefaultStateSnapshot(-1), + m_CurrentShadowId(-1), m_CurrentSnapshotId(-1), m_TransitionOps( 0, 8192 ), m_ShadowStateList( 0, 256 ), + m_TransitionTable( 0, 256 ), m_SnapshotList( 0, 256 ), + m_ShadowStateDict(0, 256 ), + m_SnapshotDict( 0, 256 ), + m_UniqueTransitions( 0, 4096 ) +{ + Assert( !g_pTransitionTable ); + g_pTransitionTable = this; + +#ifdef DEBUG_BOARD_STATE + memset( &m_BoardState, 0, sizeof( m_BoardState ) ); + memset( &m_BoardShaderState, 0, sizeof( m_BoardShaderState ) ); +#endif +} + +CTransitionTable::~CTransitionTable() +{ + Assert( g_pTransitionTable == this ); + g_pTransitionTable = NULL; +} + + +//----------------------------------------------------------------------------- +// Initialization, shutdown +//----------------------------------------------------------------------------- +bool CTransitionTable::Init( ) +{ + return true; +} + +void CTransitionTable::Shutdown( ) +{ + Reset(); +} + + +//----------------------------------------------------------------------------- +// Creates a shadow, adding an entry into the shadow list and transition table +//----------------------------------------------------------------------------- +StateSnapshot_t CTransitionTable::CreateStateSnapshot( ShadowStateId_t shadowStateId, const ShadowShaderState_t& currentShaderState ) +{ + StateSnapshot_t snapshotId = m_SnapshotList.AddToTail(); + + // Copy our snapshot into the list + SnapshotShaderState_t &shaderState = m_SnapshotList[snapshotId]; + shaderState.m_ShadowStateId = shadowStateId; + memcpy( &shaderState.m_ShaderState, ¤tShaderState, sizeof(ShadowShaderState_t) ); + memset( shaderState.m_ShaderState.m_nReserved, 0, sizeof( shaderState.m_ShaderState.m_nReserved ) ); + shaderState.m_nReserved = 0; // needed to get a good CRC + shaderState.m_nReserved2 = 0; + + // Insert entry into the lookup table + SnapshotDictEntry_t insert; + + CRC32_Init( &insert.m_nChecksum ); + CRC32_ProcessBuffer( &insert.m_nChecksum, &shaderState, sizeof(SnapshotShaderState_t) ); + CRC32_Final( &insert.m_nChecksum ); + + insert.m_nSnapshot = snapshotId; + m_SnapshotDict.Insert( insert ); + + return snapshotId; +} + + +//----------------------------------------------------------------------------- +// Creates a shadow, adding an entry into the shadow list and transition table +//----------------------------------------------------------------------------- +CTransitionTable::ShadowStateId_t CTransitionTable::CreateShadowState( const ShadowState_t ¤tState ) +{ + int newShaderState = m_ShadowStateList.AddToTail(); + + // Copy our snapshot into the list + memcpy( &m_ShadowStateList[newShaderState], ¤tState, sizeof(ShadowState_t) ); + + // all existing states must transition to the new state + int i; + for ( i = 0; i < newShaderState; ++i ) + { + // Add a new transition to all existing states + int newElem = m_TransitionTable[i].AddToTail(); + m_TransitionTable[i][newElem].m_FirstOperation = INVALID_TRANSITION_OP; + m_TransitionTable[i][newElem].m_NumOperations = 0; + } + + // Add a new vector for this transition + int newTransitionElem = m_TransitionTable.AddToTail(); + m_TransitionTable[newTransitionElem].EnsureCapacity( 32 ); + Assert( newShaderState == newTransitionElem ); + + for ( i = 0; i <= newShaderState; ++i ) + { + // Add a new transition from all existing states + int newElem = m_TransitionTable[newShaderState].AddToTail(); + m_TransitionTable[newShaderState][newElem].m_FirstOperation = INVALID_TRANSITION_OP; + m_TransitionTable[newShaderState][newElem].m_NumOperations = 0; + } + + // Insert entry into the lookup table + ShadowStateDictEntry_t insert; + + CRC32_Init( &insert.m_nChecksum ); + CRC32_ProcessBuffer( &insert.m_nChecksum, &m_ShadowStateList[newShaderState], sizeof(ShadowState_t) ); + CRC32_Final( &insert.m_nChecksum ); + + insert.m_nShadowStateId = newShaderState; + m_ShadowStateDict.Insert( insert ); + + return newShaderState; +} + + +//----------------------------------------------------------------------------- +// Finds a snapshot, if it exists. Or creates a new one if it doesn't. +//----------------------------------------------------------------------------- +CTransitionTable::ShadowStateId_t CTransitionTable::FindShadowState( const ShadowState_t& currentState ) const +{ + ShadowStateDictEntry_t find; + + CRC32_Init( &find.m_nChecksum ); + CRC32_ProcessBuffer( &find.m_nChecksum, ¤tState, sizeof(ShadowState_t) ); + CRC32_Final( &find.m_nChecksum ); + + int nDictCount = m_ShadowStateDict.Count(); + int i = m_ShadowStateDict.FindLessOrEqual( find ); + if ( i < 0 ) + return (ShadowStateId_t)-1; + + for ( ; i < nDictCount; ++i ) + { + const ShadowStateDictEntry_t &entry = m_ShadowStateDict[i]; + + // Didn't find a match + if ( entry.m_nChecksum > find.m_nChecksum ) + break; + + if ( entry.m_nChecksum != find.m_nChecksum ) + continue; + + ShadowStateId_t nShadowState = entry.m_nShadowStateId; + if (!memcmp(&m_ShadowStateList[nShadowState], ¤tState, sizeof(ShadowState_t) )) + return nShadowState; + } + + // Need to create a new one + return (ShadowStateId_t)-1; +} + + +//----------------------------------------------------------------------------- +// Finds a snapshot, if it exists. Or creates a new one if it doesn't. +//----------------------------------------------------------------------------- +StateSnapshot_t CTransitionTable::FindStateSnapshot( ShadowStateId_t id, const ShadowShaderState_t& currentState ) const +{ + SnapshotShaderState_t temp; + temp.m_ShaderState = currentState; + temp.m_ShadowStateId = id; + memset( temp.m_ShaderState.m_nReserved, 0, sizeof( temp.m_ShaderState.m_nReserved ) ); + temp.m_nReserved = 0; // needed to get a good CRC + temp.m_nReserved2 = 0; + + SnapshotDictEntry_t find; + + CRC32_Init( &find.m_nChecksum ); + CRC32_ProcessBuffer( &find.m_nChecksum, &temp, sizeof(temp) ); + CRC32_Final( &find.m_nChecksum ); + + int nDictCount = m_SnapshotDict.Count(); + int i = m_SnapshotDict.FindLessOrEqual( find ); + if ( i < 0 ) + return (StateSnapshot_t)-1; + + for ( ; i < nDictCount; ++i ) + { + // Didn't find a match + if ( m_SnapshotDict[i].m_nChecksum > find.m_nChecksum ) + break; + + if ( m_SnapshotDict[i].m_nChecksum != find.m_nChecksum ) + continue; + + StateSnapshot_t nShapshot = m_SnapshotDict[i].m_nSnapshot; + if ( (id == m_SnapshotList[nShapshot].m_ShadowStateId) && + !memcmp(&m_SnapshotList[nShapshot].m_ShaderState, ¤tState, sizeof(ShadowShaderState_t)) ) + { + return nShapshot; + } + } + + // Need to create a new one + return (StateSnapshot_t)-1; +} + + +//----------------------------------------------------------------------------- +// Used to clear the transition table when we know it's become invalid. +//----------------------------------------------------------------------------- +void CTransitionTable::Reset() +{ + m_ShadowStateList.RemoveAll(); + m_SnapshotList.RemoveAll(); + m_TransitionTable.RemoveAll(); + m_TransitionOps.RemoveAll(); + m_ShadowStateDict.RemoveAll(); + m_SnapshotDict.RemoveAll(); + m_UniqueTransitions.RemoveAll(); + m_CurrentShadowId = -1; + m_CurrentSnapshotId = -1; + m_DefaultStateSnapshot = -1; +} + + +//----------------------------------------------------------------------------- +// Sets the texture stage state +//----------------------------------------------------------------------------- +#ifdef _WIN32 +#pragma warning( disable : 4189 ) +#endif + +static inline void SetTextureStageState( int stage, D3DTEXTURESTAGESTATETYPE state, DWORD val ) +{ +#if !defined( _X360 ) + Assert( !g_pShaderDeviceDx8->IsDeactivated() ); + Dx9Device()->SetTextureStageState( stage, state, val ); +#endif +} + +//Moved to a #define so every instance of this skips unsupported render states at compile time +#define SetSamplerState( _stage, _state, _val ) \ + { \ + if ( (_state != D3DSAMP_NOTSUPPORTED) ) \ + { \ + Assert( !g_pShaderDeviceDx8->IsDeactivated() ); \ + Dx9Device()->SetSamplerState( _stage, _state, _val ); \ + } \ + } + +//Moved to a #define so every instance of this skips unsupported render states at compile time +#define SetRenderState( _state, _val ) \ + { \ + if ( _state != D3DRS_NOTSUPPORTED ) \ + { \ + Assert( !g_pShaderDeviceDx8->IsDeactivated() ); \ + Dx9Device()->SetRenderState( _state, _val ); \ + } \ + } + +#ifdef DX_TO_GL_ABSTRACTION + #define SetRenderStateConstMacro( state, val ) { if ( state != D3DRS_NOTSUPPORTED ) Dx9Device()->SetRenderStateConstInline( state, val ); } +#else + #define SetRenderStateConstMacro( state, val ) SetRenderState( state, val ) +#endif + +#ifdef _WIN32 +#pragma warning( default : 4189 ) +#endif + +//----------------------------------------------------------------------------- +// Methods that actually apply the state +//----------------------------------------------------------------------------- +#ifdef DEBUG_BOARD_STATE + +static bool g_SpewTransitions = false; + +#define UPDATE_BOARD_RENDER_STATE( _d3dState, _state ) \ + { \ + BoardState().m_ ## _state = shaderState.m_ ## _state; \ + if (g_SpewTransitions) \ + { \ + char buf[128]; \ + sprintf( buf, "Apply %s : %d\n", #_d3dState, shaderState.m_ ## _state ); \ + Plat_DebugString(buf); \ + } \ + } + +#define UPDATE_BOARD_TEXTURE_STAGE_STATE( _d3dState, _state, _stage ) \ + { \ + BoardState().m_TextureStage[_stage].m_ ## _state = shaderState.m_TextureStage[_stage].m_ ## _state; \ + if (g_SpewTransitions) \ + { \ + char buf[128]; \ + sprintf( buf, "Apply Tex %s (%d): %d\n", #_d3dState, _stage, shaderState.m_TextureStage[_stage].m_ ## _state ); \ + Plat_DebugString(buf); \ + } \ + } + +#define UPDATE_BOARD_SAMPLER_STATE( _d3dState, _state, _stage ) \ + { \ + BoardState().m_SamplerState[_stage].m_ ## _state = shaderState.m_SamplerState[_stage].m_ ## _state; \ + if (g_SpewTransitions) \ + { \ + char buf[128]; \ + sprintf( buf, "Apply SamplerSate %s (%d): %d\n", #_d3dState, stage, shaderState.m_SamplerState[_stage].m_ ## _state ); \ + Plat_DebugString(buf); \ + } \ + } + +#else + +#define UPDATE_BOARD_RENDER_STATE( _d3dState, _state ) {} +#define UPDATE_BOARD_TEXTURE_STAGE_STATE( _d3dState, _state, _stage ) {} +#define UPDATE_BOARD_SAMPLER_STATE( _d3dState, _state, _stage ) {} + +#endif + + +#define APPLY_RENDER_STATE_FUNC( _d3dState, _state ) \ + void Apply ## _state( const ShadowState_t& shaderState, int arg ) \ + { \ + SetRenderState( _d3dState, shaderState.m_ ## _state ); \ + UPDATE_BOARD_RENDER_STATE( _d3dState, _state ); \ + } + +#define APPLY_TEXTURE_STAGE_STATE_FUNC( _d3dState, _state ) \ + void Apply ## _state( const ShadowState_t& shaderState, int stage ) \ + { \ + SetTextureStageState( stage, _d3dState, shaderState.m_TextureStage[stage].m_ ## _state ); \ + UPDATE_BOARD_TEXTURE_STAGE_STATE( _d3dState, _state, stage ); \ + } + +#define APPLY_SAMPLER_STATE_FUNC( _d3dState, _state ) \ + void Apply ## _state( const ShadowState_t& shaderState, int stage ) \ + { \ + SetSamplerState( stage, _d3dState, shaderState.m_SamplerState[stage].m_ ## _state ); \ + UPDATE_BOARD_SAMPLER_STATE( _d3dState, _state, stage ); \ + } + + +// Special overridden sampler state to turn on Fetch4 on ATI hardware (and 360?) +void ApplyFetch4Enable( const ShadowState_t& shaderState, int stage ) +{ + if ( ShaderAPI()->SupportsFetch4() ) + { + SetSamplerState( stage, ATISAMP_FETCH4, shaderState.m_SamplerState[stage].m_Fetch4Enable ? ATI_FETCH4_ENABLE : ATI_FETCH4_DISABLE ); + } + + UPDATE_BOARD_SAMPLER_STATE( ATISAMP_FETCH4, Fetch4Enable, stage ); +} + +#ifdef DX_TO_GL_ABSTRACTION +void ApplyShadowFilterEnable( const ShadowState_t& shaderState, int stage ) +{ + SetSamplerState( stage, D3DSAMP_SHADOWFILTER, shaderState.m_SamplerState[stage].m_ShadowFilterEnable ); + + UPDATE_BOARD_SAMPLER_STATE( D3DSAMP_SHADOWFILTER, ShadowFilterEnable, stage ); +} +#endif + + +//APPLY_RENDER_STATE_FUNC( D3DRS_ZWRITEENABLE, ZWriteEnable ) +//APPLY_RENDER_STATE_FUNC( D3DRS_COLORWRITEENABLE, ColorWriteEnable ) +APPLY_RENDER_STATE_FUNC( D3DRS_FILLMODE, FillMode ) +APPLY_RENDER_STATE_FUNC( D3DRS_LIGHTING, Lighting ) +APPLY_RENDER_STATE_FUNC( D3DRS_SPECULARENABLE, SpecularEnable ) +APPLY_RENDER_STATE_FUNC( D3DRS_DIFFUSEMATERIALSOURCE, DiffuseMaterialSource ) +APPLY_TEXTURE_STAGE_STATE_FUNC( D3DTSS_TEXCOORDINDEX, TexCoordIndex ) + + +void ApplyZWriteEnable( const ShadowState_t& shaderState, int arg ) +{ + SetRenderStateConstMacro( D3DRS_ZWRITEENABLE, shaderState.m_ZWriteEnable ); +#if defined( _X360 ) + //SetRenderStateConstMacro( D3DRS_HIZWRITEENABLE, shaderState.m_ZWriteEnable ? D3DHIZ_AUTOMATIC : D3DHIZ_DISABLE ); +#endif + + UPDATE_BOARD_RENDER_STATE( D3DRS_ZWRITEENABLE, ZWriteEnable ); +} + +void ApplyColorWriteEnable( const ShadowState_t& shaderState, int arg ) +{ + SetRenderState( D3DRS_COLORWRITEENABLE, shaderState.m_ColorWriteEnable ); + g_pTransitionTable->CurrentState().m_ColorWriteEnable = shaderState.m_ColorWriteEnable; + + UPDATE_BOARD_RENDER_STATE( D3DRS_COLORWRITEENABLE, ColorWriteEnable ); +} + +void ApplySRGBReadEnable( const ShadowState_t& shaderState, int stage ) +{ +# if ( !defined( _X360 ) ) + { + SetSamplerState( stage, D3DSAMP_SRGBTEXTURE, shaderState.m_SamplerState[stage].m_SRGBReadEnable ); + } +# else + { + ShaderAPI()->ApplySRGBReadState( stage, shaderState.m_SamplerState[stage].m_SRGBReadEnable ); + } +# endif + + UPDATE_BOARD_SAMPLER_STATE( D3DSAMP_SRGBTEXTURE, SRGBReadEnable, stage ); +} + + +void ApplySRGBWriteEnable( const ShadowState_t& shadowState, int stageUnused ) +{ + g_pTransitionTable->ApplySRGBWriteEnable( shadowState ); +} + + +void CTransitionTable::ApplySRGBWriteEnable( const ShadowState_t& shaderState ) +{ + // ApplySRGBWriteEnable set to true means that the shader is writing linear values. + if ( CurrentState().m_bLinearColorSpaceFrameBufferEnable ) + { + // The shader had better be writing linear values since we can't convert to gamma here. + // Can't leave this assert here since there are cases where the shader is doing the right thing. + // This is good to test occasionally to make sure that the shaders are doing the right thing. + // Assert( shaderState.m_SRGBWriteEnable ); + + // render target is linear + SetRenderStateConstMacro( D3DRS_SRGBWRITEENABLE, 0 ); + ShaderAPI()->EnabledSRGBWrite( false ); + + // fog isn't fixed-function with linear frame buffers, so don't bother with that here. + } + else + { + // render target is gamma + + // SRGBWrite enable can affect the space in which fog color is defined + if ( HardwareConfig()->NeedsShaderSRGBConversion() ) + { + if ( HardwareConfig()->SupportsPixelShaders_2_b() ) //in 2b supported devices, we never actually enable SRGB writes, but instead handle the conversion in the pixel shader. But we want all other code to be unaware. + { + SetRenderStateConstMacro( D3DRS_SRGBWRITEENABLE, 0 ); + } + else + { + SetRenderStateConstMacro( D3DRS_SRGBWRITEENABLE, shaderState.m_SRGBWriteEnable ); + } + } + else + { + SetRenderStateConstMacro( D3DRS_SRGBWRITEENABLE, shaderState.m_SRGBWriteEnable ); + } + + ShaderAPI()->EnabledSRGBWrite( shaderState.m_SRGBWriteEnable ); + + if ( HardwareConfig()->SpecifiesFogColorInLinearSpace() ) + { + ShaderAPI()->ApplyFogMode( shaderState.m_FogMode, shaderState.m_SRGBWriteEnable, shaderState.m_bDisableFogGammaCorrection ); + } + } + +#ifdef _DEBUG + BoardState().m_SRGBWriteEnable = shaderState.m_SRGBWriteEnable; + if (g_SpewTransitions) + { + char buf[128]; + sprintf( buf, "Apply %s : %d\n", "D3DRS_SRGBWRITEENABLE", shaderState.m_SRGBWriteEnable ); + Plat_DebugString(buf); + } +#endif +} + +void ApplyDisableFogGammaCorrection( const ShadowState_t& shadowState, int stageUnused ) +{ + ShaderAPI()->ApplyFogMode( shadowState.m_FogMode, shadowState.m_SRGBWriteEnable, shadowState.m_bDisableFogGammaCorrection ); + +#ifdef DEBUG_BOARD_STATE + g_pTransitionTable->BoardState().m_bDisableFogGammaCorrection = shadowState.m_bDisableFogGammaCorrection; +#endif +} + + + + +void ApplyDepthTest( const ShadowState_t& state, int stage ) +{ + g_pTransitionTable->ApplyDepthTest( state ); +} + +void CTransitionTable::SetZEnable( D3DZBUFFERTYPE nEnable ) +{ + if (m_CurrentState.m_ZEnable != nEnable ) + { + SetRenderStateConstMacro( D3DRS_ZENABLE, nEnable ); +#if defined( _X360 ) + //SetRenderState( D3DRS_HIZENABLE, nEnable ? D3DHIZ_AUTOMATIC : D3DHIZ_DISABLE ); +#endif + m_CurrentState.m_ZEnable = nEnable; + } +} + +void CTransitionTable::SetZFunc( D3DCMPFUNC nCmpFunc ) +{ + if (m_CurrentState.m_ZFunc != nCmpFunc ) + { + SetRenderStateConstMacro( D3DRS_ZFUNC, nCmpFunc ); + m_CurrentState.m_ZFunc = nCmpFunc; + } +} + +void CTransitionTable::ApplyDepthTest( const ShadowState_t& state ) +{ + SetZEnable( state.m_ZEnable ); + if (state.m_ZEnable != D3DZB_FALSE) + { + SetZFunc( state.m_ZFunc ); + } + if (m_CurrentState.m_ZBias != state.m_ZBias) + { + ShaderAPI()->ApplyZBias( state ); + m_CurrentState.m_ZBias = (PolygonOffsetMode_t) state.m_ZBias; // Cast two bits from m_ZBias + } + +#ifdef DEBUG_BOARD_STATE + // This isn't quite true, but it's necessary for other error checking to work + BoardState().m_ZEnable = state.m_ZEnable; + BoardState().m_ZFunc = state.m_ZFunc; + BoardState().m_ZBias = state.m_ZBias; +#endif +} + +void ApplyAlphaTest( const ShadowState_t& state, int stage ) +{ + g_pTransitionTable->ApplyAlphaTest( state ); +} + +void CTransitionTable::ApplyAlphaTest( const ShadowState_t& state ) +{ + if (m_CurrentState.m_AlphaTestEnable != state.m_AlphaTestEnable) + { + SetRenderStateConstMacro( D3DRS_ALPHATESTENABLE, state.m_AlphaTestEnable ); + m_CurrentState.m_AlphaTestEnable = state.m_AlphaTestEnable; + } + + if (state.m_AlphaTestEnable) + { + // Set the blend state here... + if (m_CurrentState.m_AlphaFunc != state.m_AlphaFunc) + { + SetRenderStateConstMacro( D3DRS_ALPHAFUNC, state.m_AlphaFunc ); + m_CurrentState.m_AlphaFunc = state.m_AlphaFunc; + } + + if (m_CurrentState.m_AlphaRef != state.m_AlphaRef) + { + SetRenderStateConstMacro( D3DRS_ALPHAREF, state.m_AlphaRef ); + m_CurrentState.m_AlphaRef = state.m_AlphaRef; + } + } + +#ifdef DEBUG_BOARD_STATE + // This isn't quite true, but it's necessary for other error checking to work + BoardState().m_AlphaTestEnable = state.m_AlphaTestEnable; + BoardState().m_AlphaFunc = state.m_AlphaFunc; + BoardState().m_AlphaRef = state.m_AlphaRef; +#endif +} + +void ApplyAlphaBlend( const ShadowState_t& state, int stage ) +{ + g_pTransitionTable->ApplyAlphaBlend( state ); +} + +void CTransitionTable::ApplyAlphaBlend( const ShadowState_t& state ) +{ + if (m_CurrentState.m_AlphaBlendEnable != state.m_AlphaBlendEnable) + { + SetRenderStateConstMacro( D3DRS_ALPHABLENDENABLE, state.m_AlphaBlendEnable ); + m_CurrentState.m_AlphaBlendEnable = state.m_AlphaBlendEnable; + } + + if (state.m_AlphaBlendEnable) + { + // Set the blend state here... + if (m_CurrentState.m_SrcBlend != state.m_SrcBlend) + { + SetRenderStateConstMacro( D3DRS_SRCBLEND, state.m_SrcBlend ); + m_CurrentState.m_SrcBlend = state.m_SrcBlend; + } + + if (m_CurrentState.m_DestBlend != state.m_DestBlend) + { + SetRenderStateConstMacro( D3DRS_DESTBLEND, state.m_DestBlend ); + m_CurrentState.m_DestBlend = state.m_DestBlend; + } + + if (m_CurrentState.m_BlendOp != state.m_BlendOp ) + { + SetRenderStateConstMacro( D3DRS_BLENDOP, state.m_BlendOp ); + m_CurrentState.m_BlendOp = state.m_BlendOp; + } + } + +#ifdef DEBUG_BOARD_STATE + // This isn't quite true, but it's necessary for other error checking to work + BoardState().m_AlphaBlendEnable = state.m_AlphaBlendEnable; + BoardState().m_SrcBlend = state.m_SrcBlend; + BoardState().m_DestBlend = state.m_DestBlend; + BoardState().m_BlendOp = state.m_BlendOp; +#endif +} + +void ApplySeparateAlphaBlend( const ShadowState_t& state, int stage ) +{ + g_pTransitionTable->ApplySeparateAlphaBlend( state ); +} + +void CTransitionTable::ApplySeparateAlphaBlend( const ShadowState_t& state ) +{ + if (m_CurrentState.m_SeparateAlphaBlendEnable != state.m_SeparateAlphaBlendEnable) + { + SetRenderStateConstMacro( D3DRS_SEPARATEALPHABLENDENABLE, state.m_SeparateAlphaBlendEnable ); + m_CurrentState.m_SeparateAlphaBlendEnable = state.m_SeparateAlphaBlendEnable; + } + + if (state.m_SeparateAlphaBlendEnable) + { + // Set the blend state here... + if (m_CurrentState.m_SrcBlendAlpha != state.m_SrcBlendAlpha) + { + SetRenderStateConstMacro( D3DRS_SRCBLENDALPHA, state.m_SrcBlendAlpha ); + m_CurrentState.m_SrcBlendAlpha = state.m_SrcBlendAlpha; + } + + if (m_CurrentState.m_DestBlendAlpha != state.m_DestBlendAlpha) + { + SetRenderStateConstMacro( D3DRS_DESTBLENDALPHA, state.m_DestBlendAlpha ); + m_CurrentState.m_DestBlendAlpha = state.m_DestBlendAlpha; + } + + if (m_CurrentState.m_BlendOpAlpha != state.m_BlendOpAlpha ) + { + SetRenderStateConstMacro( D3DRS_BLENDOPALPHA, state.m_BlendOpAlpha ); + m_CurrentState.m_BlendOpAlpha = state.m_BlendOpAlpha; + } + } + +#ifdef DEBUG_BOARD_STATE + // This isn't quite true, but it's necessary for other error checking to work + BoardState().m_SeparateAlphaBlendEnable = state.m_SeparateAlphaBlendEnable; + BoardState().m_SrcBlendAlpha = state.m_SrcBlendAlpha; + BoardState().m_DestBlendAlpha = state.m_DestBlendAlpha; + BoardState().m_BlendOpAlpha = state.m_BlendOpAlpha; +#endif +} + +//----------------------------------------------------------------------------- +// Applies alpha texture op +//----------------------------------------------------------------------------- +void ApplyColorTextureStage( const ShadowState_t& state, int stage ) +{ + g_pTransitionTable->ApplyColorTextureStage( state, stage ); +} + +void ApplyAlphaTextureStage( const ShadowState_t& state, int stage ) +{ + g_pTransitionTable->ApplyAlphaTextureStage( state, stage ); +} + +void CTransitionTable::ApplyColorTextureStage( const ShadowState_t& state, int stage ) +{ + D3DTEXTUREOP op = state.m_TextureStage[stage].m_ColorOp; + int arg1 = state.m_TextureStage[stage].m_ColorArg1; + int arg2 = state.m_TextureStage[stage].m_ColorArg2; + + if (m_CurrentState.m_TextureStage[stage].m_ColorOp != op) + { + SetTextureStageState( stage, D3DTSS_COLOROP, op ); + m_CurrentState.m_TextureStage[stage].m_ColorOp = op; + } + + if (op != D3DTOP_DISABLE) + { + if (m_CurrentState.m_TextureStage[stage].m_ColorArg1 != arg1) + { + SetTextureStageState( stage, D3DTSS_COLORARG1, arg1 ); + m_CurrentState.m_TextureStage[stage].m_ColorArg1 = arg1; + } + if (m_CurrentState.m_TextureStage[stage].m_ColorArg2 != arg2) + { + SetTextureStageState( stage, D3DTSS_COLORARG2, arg2 ); + m_CurrentState.m_TextureStage[stage].m_ColorArg2 = arg2; + } + } + +#ifdef DEBUG_BOARD_STATE + // This isn't quite true, but it's necessary for other error checking to work + BoardState().m_TextureStage[stage].m_ColorOp = op; + BoardState().m_TextureStage[stage].m_ColorArg1 = arg1; + BoardState().m_TextureStage[stage].m_ColorArg2 = arg2; +#endif +} + +void CTransitionTable::ApplyAlphaTextureStage( const ShadowState_t& state, int stage ) +{ + D3DTEXTUREOP op = state.m_TextureStage[stage].m_AlphaOp; + int arg1 = state.m_TextureStage[stage].m_AlphaArg1; + int arg2 = state.m_TextureStage[stage].m_AlphaArg2; + + if (m_CurrentState.m_TextureStage[stage].m_AlphaOp != op) + { + SetTextureStageState( stage, D3DTSS_ALPHAOP, op ); + m_CurrentState.m_TextureStage[stage].m_AlphaOp = op; + } + + if (op != D3DTOP_DISABLE) + { + if (m_CurrentState.m_TextureStage[stage].m_AlphaArg1 != arg1) + { + SetTextureStageState( stage, D3DTSS_ALPHAARG1, arg1 ); + m_CurrentState.m_TextureStage[stage].m_AlphaArg1 = arg1; + } + if (m_CurrentState.m_TextureStage[stage].m_AlphaArg2 != arg2) + { + SetTextureStageState( stage, D3DTSS_ALPHAARG2, arg2 ); + m_CurrentState.m_TextureStage[stage].m_AlphaArg2 = arg2; + } + } + +#ifdef DEBUG_BOARD_STATE + // This isn't quite true, but it's necessary for other error checking to work + BoardState().m_TextureStage[stage].m_AlphaOp = op; + BoardState().m_TextureStage[stage].m_AlphaArg1 = arg1; + BoardState().m_TextureStage[stage].m_AlphaArg2 = arg2; +#endif +} + + +void ApplyActivateFixedFunction( const ShadowState_t& state, int stage ) +{ + int nStageCount = HardwareConfig()->GetTextureStageCount(); + for ( int i = 0; i < nStageCount; ++i ) + { + g_pTransitionTable->ApplyColorTextureStage( state, i ); + g_pTransitionTable->ApplyAlphaTextureStage( state, i ); + } +} + + +//----------------------------------------------------------------------------- +// Enables textures +//----------------------------------------------------------------------------- +void ApplyTextureEnable( const ShadowState_t& state, int stage ) +{ + // This may well enable/disable textures that are already enabled/disabled + // but the ShaderAPI will handle that + int i; + int nSamplerCount = HardwareConfig()->GetSamplerCount(); + for ( i = 0; i < nSamplerCount; ++i ) + { + ShaderAPI()->ApplyTextureEnable( state, i ); + +#ifdef DEBUG_BOARD_STATE + BoardState().m_SamplerState[i].m_TextureEnable = state.m_SamplerState[i].m_TextureEnable; +#endif + } + + // Needed to prevent mat_dxlevel assertions +#ifdef DEBUG_BOARD_STATE + for ( i = nSamplerCount; i < MAX_SAMPLERS; ++i ) + { + BoardState().m_SamplerState[i].m_TextureEnable = false; + } +#endif +} + + +//----------------------------------------------------------------------------- +// All transitions below this point depend on dynamic render state +// FIXME: Eliminate these virtual calls? +//----------------------------------------------------------------------------- +void ApplyCullEnable( const ShadowState_t& state, int arg ) +{ + ShaderAPI()->ApplyCullEnable( state.m_CullEnable ); + +#ifdef DEBUG_BOARD_STATE + BoardState().m_CullEnable = state.m_CullEnable; +#endif +} + +//----------------------------------------------------------------------------- +void ApplyAlphaToCoverage( const ShadowState_t& state, int arg ) +{ + ShaderAPI()->ApplyAlphaToCoverage( state.m_EnableAlphaToCoverage ); + +#ifdef DEBUG_BOARD_STATE + BoardState().m_EnableAlphaToCoverage = state.m_EnableAlphaToCoverage; +#endif +} + +//----------------------------------------------------------------------------- +void ApplyVertexBlendEnable( const ShadowState_t& state, int stage ) +{ + ShaderAPI()->SetVertexBlendState( state.m_VertexBlendEnable ? -1 : 0 ); + +#ifdef DEBUG_BOARD_STATE + BoardState().m_VertexBlendEnable = state.m_VertexBlendEnable; +#endif +} + + +//----------------------------------------------------------------------------- +// Outputs the fog mode string +//----------------------------------------------------------------------------- +#ifdef RECORDING +const char *ShaderFogModeToString( ShaderFogMode_t fogMode ) +{ + switch( fogMode ) + { + case SHADER_FOGMODE_DISABLED: + return "SHADER_FOGMODE_DISABLED"; + case SHADER_FOGMODE_OO_OVERBRIGHT: + return "SHADER_FOGMODE_OO_OVERBRIGHT"; + case SHADER_FOGMODE_BLACK: + return "SHADER_FOGMODE_BLACK"; + case SHADER_FOGMODE_GREY: + return "SHADER_FOGMODE_GREY"; + case SHADER_FOGMODE_FOGCOLOR: + return "SHADER_FOGMODE_FOGCOLOR"; + case SHADER_FOGMODE_WHITE: + return "SHADER_FOGMODE_WHITE"; + case SHADER_FOGMODE_NUMFOGMODES: + return "SHADER_FOGMODE_NUMFOGMODES"; + default: + return "ERROR"; + } +} +#endif + +// Uses GetConfig().overbright and GetSceneFogMode, so +// will have to fix up the state manually when those change. +void ApplyFogMode( const ShadowState_t& state, int arg ) +{ +#ifdef RECORDING + char buf[1024]; + sprintf( buf, "ApplyFogMode( %s )", ShaderFogModeToString( state.m_FogMode ) ); + RECORD_DEBUG_STRING( buf ); +#endif + + ShaderAPI()->ApplyFogMode( state.m_FogMode, state.m_SRGBWriteEnable, state.m_bDisableFogGammaCorrection ); + +#ifdef DEBUG_BOARD_STATE + BoardState().m_FogMode = state.m_FogMode; +#endif +} + + +//----------------------------------------------------------------------------- +// Function tables mapping enum to function +//----------------------------------------------------------------------------- +ApplyStateFunc_t s_pRenderFunctionTable[] = +{ + ApplyDepthTest, + ApplyZWriteEnable, + ApplyColorWriteEnable, + ApplyAlphaTest, + ApplyFillMode, + ApplyLighting, + ApplySpecularEnable, + ApplySRGBWriteEnable, + ApplyAlphaBlend, + ApplySeparateAlphaBlend, + ApplyCullEnable, + ApplyVertexBlendEnable, + ApplyFogMode, + ApplyActivateFixedFunction, + ApplyTextureEnable, // Enables textures on *all* stages + ApplyDiffuseMaterialSource, + ApplyDisableFogGammaCorrection, + ApplyAlphaToCoverage, +}; + +ApplyStateFunc_t s_pTextureFunctionTable[] = +{ + ApplyTexCoordIndex, + ApplySRGBReadEnable, + ApplyFetch4Enable, +#ifdef DX_TO_GL_ABSTRACTION + ApplyShadowFilterEnable, +#endif + // Fixed function states + ApplyColorTextureStage, + ApplyAlphaTextureStage, +}; + + +//----------------------------------------------------------------------------- +// Creates an entry in the state transition table +//----------------------------------------------------------------------------- +inline void CTransitionTable::AddTransition( RenderStateFunc_t func ) +{ + int nElem = m_TransitionOps.AddToTail(); + TransitionOp_t &op = m_TransitionOps[nElem]; + op.m_nInfo.m_bIsTextureCode = false; + op.m_nInfo.m_nOpCode = func; + + // Stats +// ++s_pRenderTransitions[ func ]; +} + +inline void CTransitionTable::AddTextureTransition( TextureStateFunc_t func, int stage ) +{ + int nElem = m_TransitionOps.AddToTail(); + TransitionOp_t &op = m_TransitionOps[nElem]; + op.m_nInfo.m_bIsTextureCode = true; + op.m_nInfo.m_nOpCode = TextureOp( func, stage ); + + // Stats +// ++s_pTextureTransitions[ func ][stage]; +} + +#define ADD_RENDER_STATE_TRANSITION( _state ) \ + if (bForce || (toState.m_ ## _state != fromState.m_ ## _state)) \ + { \ + AddTransition( RENDER_STATE_ ## _state ); \ + ++numOps; \ + } + +#define ADD_TEXTURE_STAGE_STATE_TRANSITION( _stage, _state )\ + if (bForce || (toState.m_TextureStage[_stage].m_ ## _state != fromState.m_TextureStage[_stage].m_ ## _state)) \ + { \ + Assert( _stage < MAX_TEXTURE_STAGES ); \ + AddTextureTransition( TEXTURE_STATE_ ## _state, _stage ); \ + ++numOps; \ + } + +#define ADD_SAMPLER_STATE_TRANSITION( _stage, _state )\ + if (bForce || (toState.m_SamplerState[_stage].m_ ## _state != fromState.m_SamplerState[_stage].m_ ## _state)) \ + { \ + Assert( _stage < MAX_SAMPLERS ); \ + AddTextureTransition( TEXTURE_STATE_ ## _state, _stage ); \ + ++numOps; \ + } + +int CTransitionTable::CreateNormalTransitions( const ShadowState_t& fromState, const ShadowState_t& toState, bool bForce ) +{ + int numOps = 0; + + // Special case for alpha blending to eliminate extra transitions + bool blendEnableDifferent = (toState.m_AlphaBlendEnable != fromState.m_AlphaBlendEnable); + bool srcBlendDifferent = toState.m_AlphaBlendEnable && (toState.m_SrcBlend != fromState.m_SrcBlend); + bool destBlendDifferent = toState.m_AlphaBlendEnable && (toState.m_DestBlend != fromState.m_DestBlend); + bool blendOpDifferent = toState.m_AlphaBlendEnable && ( toState.m_BlendOp != fromState.m_BlendOp ); + if (bForce || blendOpDifferent || blendEnableDifferent || srcBlendDifferent || destBlendDifferent) + { + AddTransition( RENDER_STATE_AlphaBlend ); + ++numOps; + } + + // Shouldn't have m_SeparateAlphaBlendEnable set unless m_AlphaBlendEnable is also set. + Assert ( toState.m_AlphaBlendEnable || !toState.m_SeparateAlphaBlendEnable ); + bool blendSeparateAlphaEnableDifferent = (toState.m_SeparateAlphaBlendEnable != fromState.m_SeparateAlphaBlendEnable); + bool srcBlendAlphaDifferent = toState.m_SeparateAlphaBlendEnable && (toState.m_SrcBlendAlpha != fromState.m_SrcBlendAlpha); + bool destBlendAlphaDifferent = toState.m_SeparateAlphaBlendEnable && (toState.m_DestBlendAlpha != fromState.m_DestBlendAlpha); + bool blendOpAlphaDifferent = toState.m_SeparateAlphaBlendEnable && ( toState.m_BlendOpAlpha != fromState.m_BlendOpAlpha ); + if (bForce || blendOpAlphaDifferent || blendSeparateAlphaEnableDifferent || srcBlendAlphaDifferent || destBlendAlphaDifferent) + { + AddTransition( RENDER_STATE_SeparateAlphaBlend ); + ++numOps; + } + + bool bAlphaTestEnableDifferent = (toState.m_AlphaTestEnable != fromState.m_AlphaTestEnable); + bool bAlphaFuncDifferent = toState.m_AlphaTestEnable && (toState.m_AlphaFunc != fromState.m_AlphaFunc); + bool bAlphaRefDifferent = toState.m_AlphaTestEnable && (toState.m_AlphaRef != fromState.m_AlphaRef); + if (bForce || bAlphaTestEnableDifferent || bAlphaFuncDifferent || bAlphaRefDifferent) + { + AddTransition( RENDER_STATE_AlphaTest ); + ++numOps; + } + + bool bDepthTestEnableDifferent = (toState.m_ZEnable != fromState.m_ZEnable); + bool bDepthFuncDifferent = (toState.m_ZEnable != D3DZB_FALSE) && (toState.m_ZFunc != fromState.m_ZFunc); + bool bDepthBiasDifferent = (toState.m_ZBias != fromState.m_ZBias); + if (bForce || bDepthTestEnableDifferent || bDepthFuncDifferent || bDepthBiasDifferent) + { + AddTransition( RENDER_STATE_DepthTest ); + ++numOps; + } + + if ( bForce || (toState.m_UsingFixedFunction && !fromState.m_UsingFixedFunction) ) + { + AddTransition( RENDER_STATE_ActivateFixedFunction ); + ++numOps; + } + + if ( bForce || (toState.m_bDisableFogGammaCorrection != fromState.m_bDisableFogGammaCorrection) ) + { + AddTransition( RENDER_STATE_DisableFogGammaCorrection ); + ++numOps; + } + + int nStageCount = HardwareConfig()->GetTextureStageCount(); + int i; + for ( i = 0; i < nStageCount; ++i ) + { + // Special case for texture stage ops to eliminate extra transitions + // NOTE: If we're forcing transitions, then ActivateFixedFunction above will take care of all these transitions + if ( !bForce && toState.m_UsingFixedFunction && fromState.m_UsingFixedFunction ) + { + const TextureStageShadowState_t& fromTexture = fromState.m_TextureStage[i]; + const TextureStageShadowState_t& toTexture = toState.m_TextureStage[i]; + + bool fromEnabled = (fromTexture.m_ColorOp != D3DTOP_DISABLE); + bool toEnabled = (toTexture.m_ColorOp != D3DTOP_DISABLE); + if ( fromEnabled || toEnabled ) + { + bool opDifferent = (toTexture.m_ColorOp != fromTexture.m_ColorOp); + bool arg1Different = (toTexture.m_ColorArg1 != fromTexture.m_ColorArg1); + bool arg2Different = (toTexture.m_ColorArg2 != fromTexture.m_ColorArg2); + if (opDifferent || arg1Different || arg2Different ) + { + AddTextureTransition( TEXTURE_STATE_ColorTextureStage, i ); + ++numOps; + } + } + + fromEnabled = (fromTexture.m_AlphaOp != D3DTOP_DISABLE); + toEnabled = (toTexture.m_AlphaOp != D3DTOP_DISABLE); + if ( fromEnabled || toEnabled ) + { + bool opDifferent = (toTexture.m_AlphaOp != fromTexture.m_AlphaOp); + bool arg1Different = (toTexture.m_AlphaArg1 != fromTexture.m_AlphaArg1); + bool arg2Different = (toTexture.m_AlphaArg2 != fromTexture.m_AlphaArg2); + if (opDifferent || arg1Different || arg2Different ) + { + AddTextureTransition( TEXTURE_STATE_AlphaTextureStage, i ); + ++numOps; + } + } + } + + ADD_TEXTURE_STAGE_STATE_TRANSITION( i, TexCoordIndex ); + } + + int nSamplerCount = HardwareConfig()->GetSamplerCount(); + for ( int i = 0; i < nSamplerCount; ++i ) + { + ADD_SAMPLER_STATE_TRANSITION( i, SRGBReadEnable ); + ADD_SAMPLER_STATE_TRANSITION( i, Fetch4Enable ); +#ifdef DX_TO_GL_ABSTRACTION + ADD_SAMPLER_STATE_TRANSITION( i, ShadowFilterEnable ); +#endif + } + + return numOps; +} + +void CTransitionTable::CreateTransitionTableEntry( int to, int from ) +{ + // You added or removed a state to the enums but not to the function table lists! + COMPILE_TIME_ASSERT( sizeof(s_pRenderFunctionTable) == sizeof(ApplyStateFunc_t) * RENDER_STATE_COUNT ); + COMPILE_TIME_ASSERT( sizeof(s_pTextureFunctionTable) == sizeof(ApplyStateFunc_t) * TEXTURE_STATE_COUNT ); + + // If from < 0, that means add *all* transitions into it. + unsigned int firstElem = m_TransitionOps.Count(); + unsigned short numOps = 0; + + const ShadowState_t& toState = m_ShadowStateList[to]; + const ShadowState_t& fromState = (from >= 0) ? m_ShadowStateList[from] : m_ShadowStateList[to]; + bool bForce = (from < 0); + + ADD_RENDER_STATE_TRANSITION( ZWriteEnable ) + ADD_RENDER_STATE_TRANSITION( ColorWriteEnable ) + ADD_RENDER_STATE_TRANSITION( FillMode ) + ADD_RENDER_STATE_TRANSITION( Lighting ) + ADD_RENDER_STATE_TRANSITION( SpecularEnable ) + ADD_RENDER_STATE_TRANSITION( SRGBWriteEnable ) + ADD_RENDER_STATE_TRANSITION( DiffuseMaterialSource ) + + // Some code for the non-trivial transitions + numOps += CreateNormalTransitions( fromState, toState, bForce ); + + // NOTE: From here on down are transitions that depend on dynamic state + // and which can therefore not appear in the state block + ADD_RENDER_STATE_TRANSITION( CullEnable ) + ADD_RENDER_STATE_TRANSITION( EnableAlphaToCoverage ) + ADD_RENDER_STATE_TRANSITION( VertexBlendEnable ) + + // NOTE! : Have to do the extra check for changes in m_UsingFixedFunction + // since d3d fog state is different if you are using fixed function vs. + // using a vsh/psh. + // This code is derived from: ADD_RENDER_STATE_TRANSITION( FogMode ) + // If ADD_RENDER_STATE_TRANSITION ever changes, this needs to be updated! + // This is another reason to try to have very little fixed function in the dx8/dx9 path. + if( bForce || (toState.m_FogMode != fromState.m_FogMode ) || + ( toState.m_UsingFixedFunction != fromState.m_UsingFixedFunction ) ) + { + AddTransition( RENDER_STATE_FogMode ); + ++numOps; + } + + bool bDifferentTexturesEnabled = false; + int nSamplerCount = HardwareConfig()->GetSamplerCount(); + for ( int i = 0; i < nSamplerCount; ++i ) + { + if ( toState.m_SamplerState[i].m_TextureEnable != fromState.m_SamplerState[i].m_TextureEnable ) + { + bDifferentTexturesEnabled = true; + break; + } + } + + if ( bForce || bDifferentTexturesEnabled ) + { + AddTransition( RENDER_STATE_TextureEnable ); + ++numOps; + } + + // Look for identical transition lists, and use those instead... + TransitionList_t& transition = (from >= 0) ? + m_TransitionTable[to][from] : m_DefaultTransition; + Assert( numOps <= 255 ); + transition.m_NumOperations = numOps; + + // This condition can happen, and is valid. It occurs when we snapshot + // state but do not generate a transition function for that state + if (numOps == 0) + { + transition.m_FirstOperation = INVALID_TRANSITION_OP; + return; + } + + // An optimization to try to early out of the identical transition check + // taking advantage of the fact that the matrix is usually diagonal. + unsigned int nFirstTest = INVALID_TRANSITION_OP; + if (from >= 0) + { + TransitionList_t &diagonalList = m_TransitionTable[from][to]; + if ( diagonalList.m_NumOperations == numOps ) + { + nFirstTest = diagonalList.m_FirstOperation; + } + } + + unsigned int identicalListFirstElem = FindIdenticalTransitionList( firstElem, numOps, nFirstTest ); + if (identicalListFirstElem == INVALID_TRANSITION_OP) + { + transition.m_FirstOperation = firstElem; + m_UniqueTransitions.Insert( transition ); + Assert( (int)firstElem + (int)numOps < 16777215 ); + + if( (int)firstElem + (int)numOps >= 16777215 ) + { + Warning("**** WARNING: Transition table overflow. Grab Brian\n"); + } + } + else + { + // Remove the transitions ops we made; use the duplicate copy + transition.m_FirstOperation = identicalListFirstElem; + m_TransitionOps.RemoveMultiple( firstElem, numOps ); + } +} + + +//----------------------------------------------------------------------------- +// Tests a snapshot to see if it can be used +//----------------------------------------------------------------------------- + +#define PERFORM_RENDER_STATE_TRANSITION( _state, _func ) \ + ::Apply ## _func( _state, 0 ); +#define PERFORM_TEXTURE_STAGE_STATE_TRANSITION( _state, _stage, _func ) \ + ::Apply ## _func( _state, _stage ); +#define PERFORM_SAMPLER_STATE_TRANSITION( _state, _stage, _func ) \ + ::Apply ## _func( _state, _stage ); + +bool CTransitionTable::TestShadowState( const ShadowState_t& state, const ShadowShaderState_t &shaderState ) +{ + PERFORM_RENDER_STATE_TRANSITION( state, DepthTest ) + PERFORM_RENDER_STATE_TRANSITION( state, ZWriteEnable ) + PERFORM_RENDER_STATE_TRANSITION( state, ColorWriteEnable ) + PERFORM_RENDER_STATE_TRANSITION( state, AlphaTest ) + PERFORM_RENDER_STATE_TRANSITION( state, FillMode ) + PERFORM_RENDER_STATE_TRANSITION( state, Lighting ) + PERFORM_RENDER_STATE_TRANSITION( state, SpecularEnable ) + PERFORM_RENDER_STATE_TRANSITION( state, SRGBWriteEnable ) + PERFORM_RENDER_STATE_TRANSITION( state, AlphaBlend ) + PERFORM_RENDER_STATE_TRANSITION( state, SeparateAlphaBlend ) + PERFORM_RENDER_STATE_TRANSITION( state, CullEnable ) + PERFORM_RENDER_STATE_TRANSITION( state, AlphaToCoverage ) + PERFORM_RENDER_STATE_TRANSITION( state, VertexBlendEnable ) + PERFORM_RENDER_STATE_TRANSITION( state, FogMode ) + PERFORM_RENDER_STATE_TRANSITION( state, ActivateFixedFunction ) + PERFORM_RENDER_STATE_TRANSITION( state, TextureEnable ) + PERFORM_RENDER_STATE_TRANSITION( state, DiffuseMaterialSource ) + + int i; + int nStageCount = HardwareConfig()->GetTextureStageCount(); + for ( i = 0; i < nStageCount; ++i ) + { + PERFORM_TEXTURE_STAGE_STATE_TRANSITION( state, i, ColorTextureStage ); + PERFORM_TEXTURE_STAGE_STATE_TRANSITION( state, i, AlphaTextureStage ); + PERFORM_TEXTURE_STAGE_STATE_TRANSITION( state, i, TexCoordIndex ); + } + + int nSamplerCount = HardwareConfig()->GetSamplerCount(); + for ( i = 0; i < nSamplerCount; ++i ) + { + PERFORM_SAMPLER_STATE_TRANSITION( state, i, SRGBReadEnable ); + PERFORM_SAMPLER_STATE_TRANSITION( state, i, Fetch4Enable ); +#ifdef DX_TO_GL_ABSTRACTION + PERFORM_SAMPLER_STATE_TRANSITION( state, i, ShadowFilterEnable ); +#endif + } + + // Just make sure we've got a good snapshot + RECORD_COMMAND( DX8_VALIDATE_DEVICE, 0 ); + +#if !defined( _X360 ) + DWORD numPasses; + HRESULT hr = Dx9Device()->ValidateDevice( &numPasses ); + bool ok = !FAILED(hr); +#else + bool ok = true; +#endif + + // Now set the board state to match the default state + ApplyTransition( m_DefaultTransition, m_DefaultStateSnapshot ); + + ShaderManager()->SetVertexShader( shaderState.m_VertexShader ); + ShaderManager()->SetPixelShader( shaderState.m_PixelShader ); + + return ok; +} + +//----------------------------------------------------------------------------- +// Finds identical transition lists and shares them +//----------------------------------------------------------------------------- +unsigned int CTransitionTable::FindIdenticalTransitionList( unsigned int firstElem, + unsigned short numOps, unsigned int nFirstTest ) const +{ + VPROF("CTransitionTable::FindIdenticalTransitionList"); + // As it turns out, this works most of the time + if ( nFirstTest != INVALID_TRANSITION_OP ) + { + const TransitionOp_t *pCurrOp = &m_TransitionOps[firstElem]; + const TransitionOp_t *pTestOp = &m_TransitionOps[nFirstTest]; + if ( !memcmp( pCurrOp, pTestOp, numOps * sizeof(TransitionOp_t) ) ) + return nFirstTest; + } + + // Look for a common list + const TransitionOp_t &op = m_TransitionOps[firstElem]; + + int nCount = m_UniqueTransitions.Count(); + for ( int i = 0; i < nCount; ++i ) + { + const TransitionList_t &list = m_UniqueTransitions[i]; + + // We can early out here because we've sorted the unique transitions + // descending by count + if ( list.m_NumOperations < numOps ) + return INVALID_TRANSITION_OP; + + // If we don't find a match in the first + int nPotentialMatch; + int nLastTest = list.m_FirstOperation + list.m_NumOperations - numOps; + for ( nPotentialMatch = list.m_FirstOperation; nPotentialMatch <= nLastTest; ++nPotentialMatch ) + { + // Find the first match + const TransitionOp_t &testOp = m_TransitionOps[nPotentialMatch]; + if ( testOp.m_nBits == op.m_nBits ) + break; + } + + // No matches found, continue + if ( nPotentialMatch > nLastTest ) + continue; + + // Ok, found a match of the first op, lets see if they all match + if ( numOps == 1 ) + return nPotentialMatch; + + const TransitionOp_t *pCurrOp = &m_TransitionOps[firstElem + 1]; + const TransitionOp_t *pTestOp = &m_TransitionOps[nPotentialMatch + 1]; + if ( !memcmp( pCurrOp, pTestOp, (numOps - 1) * sizeof(TransitionOp_t) ) ) + return nPotentialMatch; + } + return INVALID_TRANSITION_OP; +} + + +//----------------------------------------------------------------------------- +// Create startup snapshot +//----------------------------------------------------------------------------- +void CTransitionTable::TakeDefaultStateSnapshot( ) +{ + if (m_DefaultStateSnapshot == -1) + { + m_DefaultStateSnapshot = TakeSnapshot(); + + // This will create a transition which sets *all* shadowed state + CreateTransitionTableEntry( m_DefaultStateSnapshot, -1 ); + } +} + + +//----------------------------------------------------------------------------- +// Applies the transition list +//----------------------------------------------------------------------------- +void CTransitionTable::ApplyTransitionList( int snapshot, int nFirstOp, int nOpCount ) +{ + VPROF("CTransitionTable::ApplyTransitionList"); + // Don't bother if there's nothing to do + if (nOpCount > 0) + { + // Trying to avoid function overhead here + ShadowState_t& shadowState = m_ShadowStateList[snapshot]; + TransitionOp_t* pTransitionOp = &m_TransitionOps[nFirstOp]; + + for (int i = 0; i < nOpCount; ++i ) + { + // invoke the transition method + if ( pTransitionOp->m_nInfo.m_bIsTextureCode ) + { + TextureStateFunc_t code; + int nStage; + GetTextureOp( pTransitionOp->m_nInfo.m_nOpCode, &code, &nStage ); + (*s_pTextureFunctionTable[code])( shadowState, nStage ); + } + else + { + (*s_pRenderFunctionTable[pTransitionOp->m_nInfo.m_nOpCode])( shadowState, 0 ); + } + ++pTransitionOp; + } + } +} + + +//----------------------------------------------------------------------------- +// Apply startup snapshot +//----------------------------------------------------------------------------- +#ifdef _WIN32 +#pragma warning( disable : 4189 ) +#endif + +void CTransitionTable::ApplyTransition( TransitionList_t& list, int snapshot ) +{ + VPROF("CTransitionTable::ApplyTransition"); + if ( g_pShaderDeviceDx8->IsDeactivated() ) + return; + + // Transition lists when using state blocks have 2 parts: the first + // is the stateblock part, which is states that are not related to + // dynamic state at all; followed by states that *are* affected by dynamic state + int nFirstOp = list.m_FirstOperation; + int nOpCount = list.m_NumOperations; + + ApplyTransitionList( snapshot, nFirstOp, nOpCount ); + + // Semi-hacky code to override what the transitions are doing + PerformShadowStateOverrides(); + + // Set the current snapshot id + m_CurrentShadowId = snapshot; + +#ifdef DEBUG_BOARD_STATE + // Copy over the board states that aren't explicitly in the transition table + // so the assertion works... + + int i; + int nSamplerCount = HardwareConfig()->GetSamplerCount(); + for ( i = nSamplerCount; i < MAX_SAMPLERS; ++i ) + { + m_BoardState.m_SamplerState[i].m_TextureEnable = + CurrentShadowState()->m_SamplerState[i].m_TextureEnable; + } + + int nTextureStageCount = HardwareConfig()->GetTextureStageCount(); + for ( i = nTextureStageCount; i < MAX_TEXTURE_STAGES; ++i ) + { + memcpy( &m_BoardState.m_TextureStage[i], &CurrentShadowState()->m_TextureStage[i], sizeof(TextureStageShadowState_t) ); + } + m_BoardState.m_UsingFixedFunction = CurrentShadowState()->m_UsingFixedFunction; + + // State blocks bypass the code that sets the board state +#ifdef _DEBUG + // NOTE: A memcmp here isn't enough since we don't set alpha args in cases where the op is nothing. + // Assert( !memcmp( &m_BoardState, &CurrentShadowState(), sizeof(m_BoardState) ) ); + const ShadowState_t &testState1 = *CurrentShadowState(); + ShadowState_t testState2 = m_BoardState; + + if ( testState1.m_ZEnable == D3DZB_FALSE ) + { + testState2.m_ZBias = testState1.m_ZBias; + testState2.m_ZFunc = testState1.m_ZFunc; + } + + if ( !testState1.m_AlphaTestEnable ) + { + testState2.m_AlphaRef = testState1.m_AlphaRef; + testState2.m_AlphaFunc = testState1.m_AlphaFunc; + } + for( i = 0; i < nTextureStageCount; i++ ) + { + if ( !testState1.m_UsingFixedFunction ) + { + testState2.m_TextureStage[i].m_ColorOp = testState1.m_TextureStage[i].m_ColorOp; + testState2.m_TextureStage[i].m_ColorArg1 = testState1.m_TextureStage[i].m_ColorArg1; + testState2.m_TextureStage[i].m_ColorArg2 = testState1.m_TextureStage[i].m_ColorArg2; + testState2.m_TextureStage[i].m_AlphaOp = testState1.m_TextureStage[i].m_AlphaOp; + testState2.m_TextureStage[i].m_AlphaArg1 = testState1.m_TextureStage[i].m_AlphaArg1; + testState2.m_TextureStage[i].m_AlphaArg2 = testState1.m_TextureStage[i].m_AlphaArg2; + } + else + { + if ( testState1.m_TextureStage[i].m_ColorOp == D3DTOP_DISABLE ) + { + testState2.m_TextureStage[i].m_ColorArg1 = testState1.m_TextureStage[i].m_ColorArg1; + testState2.m_TextureStage[i].m_ColorArg2 = testState1.m_TextureStage[i].m_ColorArg2; + } + if ( testState1.m_TextureStage[i].m_AlphaOp == D3DTOP_DISABLE ) + { + testState2.m_TextureStage[i].m_AlphaArg1 = testState1.m_TextureStage[i].m_AlphaArg1; + testState2.m_TextureStage[i].m_AlphaArg2 = testState1.m_TextureStage[i].m_AlphaArg2; + } + } + } + + Assert( !memcmp( &testState1, &testState2, sizeof( testState1 ) ) ); +#endif +#endif +} + +#ifdef _WIN32 +#pragma warning( default : 4189 ) +#endif + +//----------------------------------------------------------------------------- +// Takes a snapshot, hooks it into the material +//----------------------------------------------------------------------------- +StateSnapshot_t CTransitionTable::TakeSnapshot( ) +{ + // Do any final computation of the shadow state + ShaderShadow()->ComputeAggregateShadowState(); + + // Get the current snapshot + const ShadowState_t& currentState = ShaderShadow()->GetShadowState(); + + // Create a new snapshot + ShadowStateId_t shadowStateId = FindShadowState( currentState ); + if (shadowStateId == -1) + { + // Create entry in state transition table + shadowStateId = CreateShadowState( currentState ); + + // Now create new transition entries + for (int to = 0; to < shadowStateId; ++to) + { + CreateTransitionTableEntry( to, shadowStateId ); + } + + for (int from = 0; from < shadowStateId; ++from) + { + CreateTransitionTableEntry( shadowStateId, from ); + } + } + + const ShadowShaderState_t& currentShaderState = ShaderShadow()->GetShadowShaderState(); + StateSnapshot_t snapshotId = FindStateSnapshot( shadowStateId, currentShaderState ); + if (snapshotId == -1) + { + // Create entry in state transition table + snapshotId = CreateStateSnapshot( shadowStateId, currentShaderState ); + } + + return snapshotId; +} + + +//----------------------------------------------------------------------------- +// Apply shader state (stuff that doesn't lie in the transition table) +//----------------------------------------------------------------------------- +void CTransitionTable::ApplyShaderState( const ShadowState_t &shadowState, const ShadowShaderState_t &shaderState ) +{ + VPROF("CTransitionTable::ApplyShaderState"); + // Don't bother testing against the current state because there + // could well be dynamic state modifiers affecting this too.... + if ( !shadowState.m_UsingFixedFunction ) + { + // FIXME: Improve early-binding of vertex shader index + ShaderManager()->SetVertexShader( shaderState.m_VertexShader ); + ShaderManager()->SetPixelShader( shaderState.m_PixelShader ); + +#ifdef DEBUG_BOARD_STATE + BoardShaderState().m_VertexShader = shaderState.m_VertexShader; + BoardShaderState().m_PixelShader = shaderState.m_PixelShader; + BoardShaderState().m_nStaticVshIndex = shaderState.m_nStaticVshIndex; + BoardShaderState().m_nStaticPshIndex = shaderState.m_nStaticPshIndex; +#endif + } + else + { + ShaderManager()->SetVertexShader( INVALID_SHADER ); + ShaderManager()->SetPixelShader( INVALID_SHADER ); +#if defined( _X360 ) + // no fixed function support + Assert( 0 ); +#endif + +#ifdef DEBUG_BOARD_STATE + BoardShaderState().m_VertexShader = INVALID_SHADER; + BoardShaderState().m_PixelShader = INVALID_SHADER; + BoardShaderState().m_nStaticVshIndex = 0; + BoardShaderState().m_nStaticPshIndex = 0; +#endif + } +} + +//----------------------------------------------------------------------------- +// Makes the board state match the snapshot +//----------------------------------------------------------------------------- +void CTransitionTable::UseSnapshot( StateSnapshot_t snapshotId ) +{ + VPROF("CTransitionTable::UseSnapshot"); + ShadowStateId_t id = m_SnapshotList[snapshotId].m_ShadowStateId; + if (m_CurrentSnapshotId != snapshotId) + { + // First apply things that are in the transition table + if ( m_CurrentShadowId != id ) + { + TransitionList_t& transition = m_TransitionTable[id][m_CurrentShadowId]; + ApplyTransition( transition, id ); + } + + // NOTE: There is an opportunity here to set non-dynamic state that we don't + // store in the transition list if we ever need it. + + m_CurrentSnapshotId = snapshotId; + } + + // NOTE: This occurs regardless of whether the snapshot changed because it depends + // on dynamic state (namely, the dynamic vertex + pixel shader index) + // Followed by things that are not + ApplyShaderState( m_ShadowStateList[id], m_SnapshotList[snapshotId].m_ShaderState ); + +#ifdef _DEBUG + // NOTE: We can't ship with this active because mod makers may well violate this rule + // We don't want no stinking fixed-function on hardware that has vertex and pixel shaders. . + // This could cause a serious perf hit. + if( HardwareConfig()->SupportsVertexAndPixelShaders() ) + { +// Assert( !CurrentShadowState().m_UsingFixedFunction ); + } +#endif +} + + +//----------------------------------------------------------------------------- +// Cause the board to match the default state snapshot +//----------------------------------------------------------------------------- +void CTransitionTable::UseDefaultState( ) +{ + VPROF("CTransitionTable::UseDefaultState"); + // Need to blat these out because they are tested during transitions + m_CurrentState.m_AlphaBlendEnable = false; + m_CurrentState.m_SrcBlend = D3DBLEND_ONE; + m_CurrentState.m_DestBlend = D3DBLEND_ZERO; + m_CurrentState.m_BlendOp = D3DBLENDOP_ADD; + SetRenderStateConstMacro( D3DRS_ALPHABLENDENABLE, m_CurrentState.m_AlphaBlendEnable ); + SetRenderStateConstMacro( D3DRS_SRCBLEND, m_CurrentState.m_SrcBlend ); + SetRenderStateConstMacro( D3DRS_DESTBLEND, m_CurrentState.m_DestBlend ); + SetRenderStateConstMacro( D3DRS_BLENDOP, m_CurrentState.m_BlendOp ); + + m_CurrentState.m_SeparateAlphaBlendEnable = false; + m_CurrentState.m_SrcBlendAlpha = D3DBLEND_ONE; + m_CurrentState.m_DestBlendAlpha = D3DBLEND_ZERO; + m_CurrentState.m_BlendOpAlpha = D3DBLENDOP_ADD; + SetRenderStateConstMacro( D3DRS_SEPARATEALPHABLENDENABLE, m_CurrentState.m_SeparateAlphaBlendEnable ); + SetRenderStateConstMacro( D3DRS_SRCBLENDALPHA, m_CurrentState.m_SrcBlendAlpha ); + SetRenderStateConstMacro( D3DRS_DESTBLENDALPHA, m_CurrentState.m_DestBlendAlpha ); + SetRenderStateConstMacro( D3DRS_BLENDOPALPHA, m_CurrentState.m_BlendOpAlpha ); + + m_CurrentState.m_ZEnable = D3DZB_TRUE; + m_CurrentState.m_ZFunc = D3DCMP_LESSEQUAL; + m_CurrentState.m_ZBias = SHADER_POLYOFFSET_DISABLE; + SetRenderStateConstMacro( D3DRS_ZENABLE, m_CurrentState.m_ZEnable ); +#if defined( _X360 ) + //SetRenderStateConstMacro( D3DRS_HIZENABLE, m_CurrentState.m_ZEnable ? D3DHIZ_AUTOMATIC : D3DHIZ_DISABLE ); +#endif + SetRenderStateConstMacro( D3DRS_ZFUNC, m_CurrentState.m_ZFunc ); + + m_CurrentState.m_AlphaTestEnable = false; + m_CurrentState.m_AlphaFunc = D3DCMP_GREATEREQUAL; + m_CurrentState.m_AlphaRef = 0; + SetRenderStateConstMacro( D3DRS_ALPHATESTENABLE, m_CurrentState.m_AlphaTestEnable ); + SetRenderStateConstMacro( D3DRS_ALPHAFUNC, m_CurrentState.m_AlphaFunc ); + SetRenderStateConstMacro( D3DRS_ALPHAREF, m_CurrentState.m_AlphaRef ); + + int nTextureStages = ShaderAPI()->GetActualTextureStageCount(); + for ( int i = 0; i < nTextureStages; ++i) + { + TextureStage(i).m_ColorOp = D3DTOP_DISABLE; + TextureStage(i).m_ColorArg1 = D3DTA_TEXTURE; + TextureStage(i).m_ColorArg2 = (i == 0) ? D3DTA_DIFFUSE : D3DTA_CURRENT; + TextureStage(i).m_AlphaOp = D3DTOP_DISABLE; + TextureStage(i).m_AlphaArg1 = D3DTA_TEXTURE; + TextureStage(i).m_AlphaArg2 = (i == 0) ? D3DTA_DIFFUSE : D3DTA_CURRENT; + + SetTextureStageState( i, D3DTSS_COLOROP, TextureStage(i).m_ColorOp ); + SetTextureStageState( i, D3DTSS_COLORARG1, TextureStage(i).m_ColorArg1 ); + SetTextureStageState( i, D3DTSS_COLORARG2, TextureStage(i).m_ColorArg2 ); + SetTextureStageState( i, D3DTSS_ALPHAOP, TextureStage(i).m_AlphaOp ); + SetTextureStageState( i, D3DTSS_ALPHAARG1, TextureStage(i).m_AlphaArg1 ); + SetTextureStageState( i, D3DTSS_ALPHAARG2, TextureStage(i).m_AlphaArg2 ); + } + + int nSamplerCount = ShaderAPI()->GetActualSamplerCount(); + for ( int i = 0; i < nSamplerCount; ++i) + { + SetSamplerState( i, D3DSAMP_SRGBTEXTURE, SamplerState(i).m_SRGBReadEnable ); + + // Set default Fetch4 state on parts which support it + if ( ShaderAPI()->SupportsFetch4() ) + { + SetSamplerState( i, ATISAMP_FETCH4, SamplerState(i).m_Fetch4Enable ? ATI_FETCH4_ENABLE : ATI_FETCH4_DISABLE ); + } + +#ifdef DX_TO_GL_ABSTRACTION + SetSamplerState( i, D3DSAMP_SHADOWFILTER, SamplerState(i).m_ShadowFilterEnable ); +#endif + } + + // Disable z overrides... + m_CurrentState.m_bOverrideDepthEnable = false; + m_CurrentState.m_bOverrideAlphaWriteEnable = false; + m_CurrentState.m_bOverrideColorWriteEnable = false; + m_CurrentState.m_ForceDepthFuncEquals = false; + m_CurrentState.m_bLinearColorSpaceFrameBufferEnable = false; + ApplyTransition( m_DefaultTransition, m_DefaultStateSnapshot ); + + ShaderManager()->SetVertexShader( INVALID_SHADER ); + ShaderManager()->SetPixelShader( INVALID_SHADER ); + + m_CurrentSnapshotId = -1; +} + + +//----------------------------------------------------------------------------- +// Snapshotted state overrides +//----------------------------------------------------------------------------- +void CTransitionTable::ForceDepthFuncEquals( bool bEnable ) +{ + if( bEnable != m_CurrentState.m_ForceDepthFuncEquals ) + { + // Do this so that we can call this from within the rendering code + // See OverrideDepthEnable + PerformShadowStateOverrides for a version + // that isn't expected to be called from within rendering code + if( !ShaderAPI()->IsRenderingMesh() ) + { + ShaderAPI()->FlushBufferedPrimitives(); + } + + m_CurrentState.m_ForceDepthFuncEquals = bEnable; + + if( bEnable ) + { + SetZFunc( D3DCMP_EQUAL ); + } + else + { + if ( CurrentShadowState() ) + { + SetZFunc( CurrentShadowState()->m_ZFunc ); + } + } + } +} + +void CTransitionTable::OverrideDepthEnable( bool bEnable, bool bDepthEnable ) +{ + if ( bEnable != m_CurrentState.m_bOverrideDepthEnable ) + { + ShaderAPI()->FlushBufferedPrimitives(); + m_CurrentState.m_bOverrideDepthEnable = bEnable; + m_CurrentState.m_OverrideZWriteEnable = bDepthEnable ? D3DZB_TRUE : D3DZB_FALSE; + + if ( m_CurrentState.m_bOverrideDepthEnable ) + { + SetZEnable( D3DZB_TRUE ); + SetRenderStateConstMacro( D3DRS_ZWRITEENABLE, m_CurrentState.m_OverrideZWriteEnable ); +#if defined( _X360 ) + //SetRenderStateConstMacro( D3DRS_HIZWRITEENABLE, m_CurrentState.m_OverrideZWriteEnable ? D3DHIZ_AUTOMATIC : D3DHIZ_DISABLE ); +#endif + } + else + { + if ( CurrentShadowState() ) + { + SetZEnable( CurrentShadowState()->m_ZEnable ); + SetRenderStateConstMacro( D3DRS_ZWRITEENABLE, CurrentShadowState()->m_ZWriteEnable ); +#if defined( _X360 ) + //SetRenderStateConstMacro( D3DRS_HIZWRITEENABLE, CurrentShadowState()->m_ZWriteEnable ? D3DHIZ_AUTOMATIC : D3DHIZ_DISABLE ); +#endif + } + } + } +} + +void CTransitionTable::OverrideAlphaWriteEnable( bool bOverrideEnable, bool bAlphaWriteEnable ) +{ + if ( bOverrideEnable != m_CurrentState.m_bOverrideAlphaWriteEnable ) + { + ShaderAPI()->FlushBufferedPrimitives(); + m_CurrentState.m_bOverrideAlphaWriteEnable = bOverrideEnable; + m_CurrentState.m_bOverriddenAlphaWriteValue = bAlphaWriteEnable; + + DWORD dwSetValue = m_CurrentState.m_ColorWriteEnable; + if ( m_CurrentState.m_bOverrideAlphaWriteEnable ) + { + if( m_CurrentState.m_bOverriddenAlphaWriteValue ) + { + dwSetValue |= D3DCOLORWRITEENABLE_ALPHA; + } + else + { + dwSetValue &= ~D3DCOLORWRITEENABLE_ALPHA; + } + } + else + { + if ( CurrentShadowState() ) + { + //probably being paranoid, but only copy the alpha flag from the shadow state + dwSetValue &= ~D3DCOLORWRITEENABLE_ALPHA; + dwSetValue |= CurrentShadowState()->m_ColorWriteEnable & D3DCOLORWRITEENABLE_ALPHA; + } + } + + if( dwSetValue != m_CurrentState.m_ColorWriteEnable ) + { + m_CurrentState.m_ColorWriteEnable = dwSetValue; + SetRenderState( D3DRS_COLORWRITEENABLE, m_CurrentState.m_ColorWriteEnable ); + } + } +} + +void CTransitionTable::OverrideColorWriteEnable( bool bOverrideEnable, bool bColorWriteEnable ) +{ + if ( bOverrideEnable != m_CurrentState.m_bOverrideColorWriteEnable ) + { + ShaderAPI()->FlushBufferedPrimitives(); + m_CurrentState.m_bOverrideColorWriteEnable = bOverrideEnable; + m_CurrentState.m_bOverriddenColorWriteValue = bColorWriteEnable; + + DWORD dwSetValue = m_CurrentState.m_ColorWriteEnable; + if ( m_CurrentState.m_bOverrideColorWriteEnable ) + { + if( m_CurrentState.m_bOverriddenColorWriteValue ) + { + dwSetValue |= (D3DCOLORWRITEENABLE_RED | D3DCOLORWRITEENABLE_GREEN | D3DCOLORWRITEENABLE_BLUE); + } + else + { + dwSetValue &= ~(D3DCOLORWRITEENABLE_RED | D3DCOLORWRITEENABLE_GREEN | D3DCOLORWRITEENABLE_BLUE); + } + } + else + { + if ( CurrentShadowState() ) + { + //probably being paranoid, but only copy the alpha flag from the shadow state + dwSetValue &= ~(D3DCOLORWRITEENABLE_RED | D3DCOLORWRITEENABLE_GREEN | D3DCOLORWRITEENABLE_BLUE); + dwSetValue |= CurrentShadowState()->m_ColorWriteEnable & (D3DCOLORWRITEENABLE_RED | D3DCOLORWRITEENABLE_GREEN | D3DCOLORWRITEENABLE_BLUE); + } + } + + if( dwSetValue != m_CurrentState.m_ColorWriteEnable ) + { + m_CurrentState.m_ColorWriteEnable = dwSetValue; + SetRenderState( D3DRS_COLORWRITEENABLE, m_CurrentState.m_ColorWriteEnable ); + } + } +} + +void CTransitionTable::EnableLinearColorSpaceFrameBuffer( bool bEnable ) +{ + if ( m_CurrentState.m_bLinearColorSpaceFrameBufferEnable != bEnable && CurrentShadowState() ) + { + ShaderAPI()->FlushBufferedPrimitives(); + m_CurrentState.m_bLinearColorSpaceFrameBufferEnable = bEnable; + ApplySRGBWriteEnable( *CurrentShadowState() ); + } +} + +//----------------------------------------------------------------------------- +// Perform state block overrides +//----------------------------------------------------------------------------- +void CTransitionTable::PerformShadowStateOverrides( ) +{ + VPROF("CTransitionTable::PerformShadowStateOverrides"); + // Deal with funky overrides here, because the state blocks can't... + if ( m_CurrentState.m_ForceDepthFuncEquals ) + { + SetZFunc( D3DCMP_EQUAL ); + } + + if ( m_CurrentState.m_bOverrideDepthEnable ) + { + SetZEnable( D3DZB_TRUE ); + SetRenderStateConstMacro( D3DRS_ZWRITEENABLE, m_CurrentState.m_OverrideZWriteEnable ); +#if defined( _X360 ) + //SetRenderStateConstMacro( D3DRS_HIZWRITEENABLE, m_CurrentState.m_OverrideZWriteEnable ? D3DHIZ_AUTOMATIC : D3DHIZ_DISABLE ); +#endif + } + + if ( m_CurrentState.m_bOverrideAlphaWriteEnable ) + { + DWORD dwSetValue = m_CurrentState.m_ColorWriteEnable & ~D3DCOLORWRITEENABLE_ALPHA; + dwSetValue |= m_CurrentState.m_bOverriddenAlphaWriteValue ? D3DCOLORWRITEENABLE_ALPHA : 0; + if ( dwSetValue != m_CurrentState.m_ColorWriteEnable ) + { + m_CurrentState.m_ColorWriteEnable = dwSetValue; + SetRenderState( D3DRS_COLORWRITEENABLE, m_CurrentState.m_ColorWriteEnable ); + } + } + + if ( m_CurrentState.m_bOverrideColorWriteEnable ) + { + DWORD dwSetValue = m_CurrentState.m_ColorWriteEnable & ~(D3DCOLORWRITEENABLE_RED | D3DCOLORWRITEENABLE_GREEN | D3DCOLORWRITEENABLE_BLUE); + dwSetValue |= m_CurrentState.m_bOverriddenColorWriteValue ? (D3DCOLORWRITEENABLE_RED | D3DCOLORWRITEENABLE_GREEN | D3DCOLORWRITEENABLE_BLUE) : 0; + if ( dwSetValue != m_CurrentState.m_ColorWriteEnable ) + { + m_CurrentState.m_ColorWriteEnable = dwSetValue; + SetRenderState( D3DRS_COLORWRITEENABLE, m_CurrentState.m_ColorWriteEnable ); + } + } +} diff --git a/materialsystem/shaderapidx9/TransitionTable.h b/materialsystem/shaderapidx9/TransitionTable.h new file mode 100644 index 0000000..730943b --- /dev/null +++ b/materialsystem/shaderapidx9/TransitionTable.h @@ -0,0 +1,407 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// + +#ifndef TRANSITION_TABLE_H +#define TRANSITION_TABLE_H + +#ifdef _WIN32 +#pragma once +#endif + +#include "utlvector.h" +#include "shadershadowdx8.h" +#include "UtlSortVector.h" +#include "checksum_crc.h" +#include "shaderapi/ishaderapi.h" + +// Required for DEBUG_BOARD_STATE +#include "shaderapidx8_global.h" + + +//----------------------------------------------------------------------------- +// Forward declarations +//----------------------------------------------------------------------------- +struct IDirect3DStateBlock9; +//----------------------------------------------------------------------------- +// Enumeration for ApplyStateFunc_ts +//----------------------------------------------------------------------------- +// Any function that does not require a texture stage +// NOTE: If you change this, change the function table s_pRenderFunctionTable[] below!! +enum RenderStateFunc_t +{ + RENDER_STATE_DepthTest = 0, + RENDER_STATE_ZWriteEnable, + RENDER_STATE_ColorWriteEnable, + RENDER_STATE_AlphaTest, + RENDER_STATE_FillMode, + RENDER_STATE_Lighting, + RENDER_STATE_SpecularEnable, + RENDER_STATE_SRGBWriteEnable, + RENDER_STATE_AlphaBlend, + RENDER_STATE_SeparateAlphaBlend, + RENDER_STATE_CullEnable, + RENDER_STATE_VertexBlendEnable, + RENDER_STATE_FogMode, + RENDER_STATE_ActivateFixedFunction, + RENDER_STATE_TextureEnable, + RENDER_STATE_DiffuseMaterialSource, + RENDER_STATE_DisableFogGammaCorrection, + RENDER_STATE_EnableAlphaToCoverage, + + RENDER_STATE_COUNT, +}; + + +// Any function that requires a texture stage +// NOTE: If you change this, change the function table s_pTextureFunctionTable[] below!! +enum TextureStateFunc_t +{ + TEXTURE_STATE_TexCoordIndex = 0, + TEXTURE_STATE_SRGBReadEnable, + TEXTURE_STATE_Fetch4Enable, +#ifdef DX_TO_GL_ABSTRACTION + TEXTURE_STATE_ShadowFilterEnable, +#endif + // Fixed function states + TEXTURE_STATE_ColorTextureStage, + TEXTURE_STATE_AlphaTextureStage, + TEXTURE_STATE_COUNT +}; + + +//----------------------------------------------------------------------------- +// Types related to transition table entries +//----------------------------------------------------------------------------- +typedef void (*ApplyStateFunc_t)( const ShadowState_t& shadowState, int arg ); + + +//----------------------------------------------------------------------------- +// The DX8 implementation of the transition table +//----------------------------------------------------------------------------- +class CTransitionTable +{ +public: + struct CurrentTextureStageState_t + { + D3DTEXTUREOP m_ColorOp; + int m_ColorArg1; + int m_ColorArg2; + D3DTEXTUREOP m_AlphaOp; + int m_AlphaArg1; + int m_AlphaArg2; + }; + struct CurrentSamplerState_t + { + bool m_SRGBReadEnable; + bool m_Fetch4Enable; + bool m_ShadowFilterEnable; + }; + struct CurrentState_t + { + // Everything in this 'CurrentState' structure is a state whose value we don't care about + // under certain circumstances, (which therefore can diverge from the shadow state), + // or states which we override in the dynamic pass. + + // Alpha state + bool m_AlphaBlendEnable; + D3DBLEND m_SrcBlend; + D3DBLEND m_DestBlend; + D3DBLENDOP m_BlendOp; + + // GR - Separate alpha state + bool m_SeparateAlphaBlendEnable; + D3DBLEND m_SrcBlendAlpha; + D3DBLEND m_DestBlendAlpha; + D3DBLENDOP m_BlendOpAlpha; + + // Depth testing states + D3DZBUFFERTYPE m_ZEnable; + D3DCMPFUNC m_ZFunc; + PolygonOffsetMode_t m_ZBias; + + // Alpha testing states + bool m_AlphaTestEnable; + D3DCMPFUNC m_AlphaFunc; + int m_AlphaRef; + + bool m_ForceDepthFuncEquals; + bool m_bOverrideDepthEnable; + D3DZBUFFERTYPE m_OverrideZWriteEnable; + + bool m_bOverrideAlphaWriteEnable; + bool m_bOverriddenAlphaWriteValue; + bool m_bOverrideColorWriteEnable; + bool m_bOverriddenColorWriteValue; + DWORD m_ColorWriteEnable; + + bool m_bLinearColorSpaceFrameBufferEnable; + + bool m_StencilEnable; + D3DCMPFUNC m_StencilFunc; + int m_StencilRef; + int m_StencilMask; + DWORD m_StencilFail; + DWORD m_StencilZFail; + DWORD m_StencilPass; + int m_StencilWriteMask; + + // Texture stage state + CurrentTextureStageState_t m_TextureStage[MAX_TEXTURE_STAGES]; + CurrentSamplerState_t m_SamplerState[MAX_SAMPLERS]; + }; + +public: + // constructor, destructor + CTransitionTable( ); + virtual ~CTransitionTable(); + + // Initialization, shutdown + bool Init( ); + void Shutdown( ); + + // Resets the snapshots... + void Reset(); + + // Takes a snapshot + StateSnapshot_t TakeSnapshot( ); + + // Take startup snapshot + void TakeDefaultStateSnapshot( ); + + // Makes the board state match the snapshot + void UseSnapshot( StateSnapshot_t snapshotId ); + + // Cause the board to match the default state snapshot + void UseDefaultState(); + + // Snapshotted state overrides + void ForceDepthFuncEquals( bool bEnable ); + void OverrideDepthEnable( bool bEnable, bool bDepthEnable ); + void OverrideAlphaWriteEnable( bool bOverrideEnable, bool bAlphaWriteEnable ); + void OverrideColorWriteEnable( bool bOverrideEnable, bool bColorWriteEnable ); + void EnableLinearColorSpaceFrameBuffer( bool bEnable ); + + // Returns a particular snapshot + const ShadowState_t &GetSnapshot( StateSnapshot_t snapshotId ) const; + const ShadowShaderState_t &GetSnapshotShader( StateSnapshot_t snapshotId ) const; + + // Gets the current shadow state + const ShadowState_t *CurrentShadowState() const; + const ShadowShaderState_t *CurrentShadowShaderState() const; + + // Return the current shapshot + int CurrentSnapshot() const { return m_CurrentSnapshotId; } + + CurrentState_t& CurrentState() { return m_CurrentState; } + +#ifdef DEBUG_BOARD_STATE + ShadowState_t& BoardState() { return m_BoardState; } + ShadowShaderState_t& BoardShaderState() { return m_BoardShaderState; } +#endif + + // The following are meant to be used by the transition table only +public: + // Applies alpha blending + void ApplyAlphaBlend( const ShadowState_t& state ); + // GR - separate alpha blend + void ApplySeparateAlphaBlend( const ShadowState_t& state ); + void ApplyAlphaTest( const ShadowState_t& state ); + void ApplyDepthTest( const ShadowState_t& state ); + + // Applies alpha texture op + void ApplyColorTextureStage( const ShadowState_t& state, int stage ); + void ApplyAlphaTextureStage( const ShadowState_t& state, int stage ); + + void ApplySRGBWriteEnable( const ShadowState_t& state ); +private: + enum + { + INVALID_TRANSITION_OP = 0xFFFFFF + }; + + typedef short ShadowStateId_t; + + // For the transition table + struct TransitionList_t + { + unsigned int m_FirstOperation : 24; + unsigned int m_NumOperations : 8; + }; + + union TransitionOp_t + { + unsigned char m_nBits; + struct + { + unsigned char m_nOpCode : 7; + unsigned char m_bIsTextureCode : 1; + } m_nInfo; + }; + + struct SnapshotShaderState_t + { + ShadowShaderState_t m_ShaderState; + ShadowStateId_t m_ShadowStateId; + unsigned short m_nReserved; // Pad to 2 ints + unsigned int m_nReserved2; + }; + + struct ShadowStateDictEntry_t + { + CRC32_t m_nChecksum; + ShadowStateId_t m_nShadowStateId; + }; + + struct SnapshotDictEntry_t + { + CRC32_t m_nChecksum; + StateSnapshot_t m_nSnapshot; + }; + + class ShadowStateDictLessFunc + { + public: + bool Less( const ShadowStateDictEntry_t &src1, const ShadowStateDictEntry_t &src2, void *pCtx ); + }; + + class SnapshotDictLessFunc + { + public: + bool Less( const SnapshotDictEntry_t &src1, const SnapshotDictEntry_t &src2, void *pCtx ); + }; + + class UniqueSnapshotLessFunc + { + public: + bool Less( const TransitionList_t &src1, const TransitionList_t &src2, void *pCtx ); + }; + + CurrentTextureStageState_t &TextureStage( int stage ) { return m_CurrentState.m_TextureStage[stage]; } + const CurrentTextureStageState_t &TextureStage( int stage ) const { return m_CurrentState.m_TextureStage[stage]; } + + CurrentSamplerState_t &SamplerState( int stage ) { return m_CurrentState.m_SamplerState[stage]; } + const CurrentSamplerState_t &SamplerState( int stage ) const { return m_CurrentState.m_SamplerState[stage]; } + + // creates state snapshots + ShadowStateId_t CreateShadowState( const ShadowState_t ¤tState ); + StateSnapshot_t CreateStateSnapshot( ShadowStateId_t shadowStateId, const ShadowShaderState_t& currentShaderState ); + + // finds state snapshots + ShadowStateId_t FindShadowState( const ShadowState_t& currentState ) const; + StateSnapshot_t FindStateSnapshot( ShadowStateId_t id, const ShadowShaderState_t& currentState ) const; + + // Finds identical transition lists + unsigned int FindIdenticalTransitionList( unsigned int firstElem, + unsigned short numOps, unsigned int nFirstTest ) const; + + // Adds a transition + void AddTransition( RenderStateFunc_t func ); + void AddTextureTransition( TextureStateFunc_t func, int stage ); + + // Apply a transition + void ApplyTransition( TransitionList_t& list, int snapshot ); + + // Creates an entry in the transition table + void CreateTransitionTableEntry( int to, int from ); + + // Checks if a state is valid + bool TestShadowState( const ShadowState_t& state, const ShadowShaderState_t &shaderState ); + + // Perform state block overrides + void PerformShadowStateOverrides( ); + + // Applies the transition list + void ApplyTransitionList( int snapshot, int nFirstOp, int nOpCount ); + + // Apply shader state (stuff that doesn't lie in the transition table) + void ApplyShaderState( const ShadowState_t &shadowState, const ShadowShaderState_t &shaderState ); + + // Wrapper for the non-standard transitions for stateblock + non-stateblock cases + int CreateNormalTransitions( const ShadowState_t& fromState, const ShadowState_t& toState, bool bForce ); + + // State setting methods + void SetZEnable( D3DZBUFFERTYPE nEnable ); + void SetZFunc( D3DCMPFUNC nCmpFunc ); + +private: + // Sets up the default state + StateSnapshot_t m_DefaultStateSnapshot; + TransitionList_t m_DefaultTransition; + ShadowState_t m_DefaultShadowState; + + // The current snapshot id + ShadowStateId_t m_CurrentShadowId; + StateSnapshot_t m_CurrentSnapshotId; + + // Maintains a list of all used snapshot transition states + CUtlVector< ShadowState_t > m_ShadowStateList; + + // Lookup table for fast snapshot finding + CUtlSortVector< ShadowStateDictEntry_t, ShadowStateDictLessFunc > m_ShadowStateDict; + + // The snapshot transition table + CUtlVector< CUtlVector< TransitionList_t > > m_TransitionTable; + + // List of unique transitions + CUtlSortVector< TransitionList_t, UniqueSnapshotLessFunc > m_UniqueTransitions; + + // Stores all state transition operations + CUtlVector< TransitionOp_t > m_TransitionOps; + + // Stores all state for a particular snapshot + CUtlVector< SnapshotShaderState_t > m_SnapshotList; + + // Lookup table for fast snapshot finding + CUtlSortVector< SnapshotDictEntry_t, SnapshotDictLessFunc > m_SnapshotDict; + + // The current board state. + CurrentState_t m_CurrentState; + +#ifdef DEBUG_BOARD_STATE + // Maintains the total shadow state + ShadowState_t m_BoardState; + ShadowShaderState_t m_BoardShaderState; +#endif +}; + + +//----------------------------------------------------------------------------- +// Inline methods +//----------------------------------------------------------------------------- +inline const ShadowState_t &CTransitionTable::GetSnapshot( StateSnapshot_t snapshotId ) const +{ + Assert( (snapshotId >= 0) && (snapshotId < m_SnapshotList.Count()) ); + return m_ShadowStateList[m_SnapshotList[snapshotId].m_ShadowStateId]; +} + +inline const ShadowShaderState_t &CTransitionTable::GetSnapshotShader( StateSnapshot_t snapshotId ) const +{ + Assert( (snapshotId >= 0) && (snapshotId < m_SnapshotList.Count()) ); + return m_SnapshotList[snapshotId].m_ShaderState; +} + +inline const ShadowState_t *CTransitionTable::CurrentShadowState() const +{ + if ( m_CurrentShadowId == -1 ) + return NULL; + + Assert( (m_CurrentShadowId >= 0) && (m_CurrentShadowId < m_ShadowStateList.Count()) ); + return &m_ShadowStateList[m_CurrentShadowId]; +} + +inline const ShadowShaderState_t *CTransitionTable::CurrentShadowShaderState() const +{ + if ( m_CurrentShadowId == -1 ) + return NULL; + + Assert( (m_CurrentShadowId >= 0) && (m_CurrentShadowId < m_ShadowStateList.Count()) ); + return &m_SnapshotList[m_CurrentShadowId].m_ShaderState; +} + + +#endif // TRANSITION_TABLE_H diff --git a/materialsystem/shaderapidx9/colorformatdx8.cpp b/materialsystem/shaderapidx9/colorformatdx8.cpp new file mode 100644 index 0000000..daf3e50 --- /dev/null +++ b/materialsystem/shaderapidx9/colorformatdx8.cpp @@ -0,0 +1,727 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//===========================================================================// + +#define DISABLE_PROTECTED_THINGS +#include "togl/rendermechanism.h" +#include "locald3dtypes.h" +#include "colorformatdx8.h" +#include "shaderapidx8_global.h" +#include "bitmap/imageformat.h" +#include "shaderapi/ishaderutil.h" +#include "tier0/dbg.h" +#include "tier1/strtools.h" +#include "shaderdevicedx8.h" + + +// Must be last +#include "tier0/memdbgon.h" + + +//----------------------------------------------------------------------------- +// Figures out what texture formats we support +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +// globals +//----------------------------------------------------------------------------- + +// Texture formats supported by DX driver[vertextexture][render target][non filterable] +static D3DFORMAT g_D3DColorFormat[NUM_IMAGE_FORMATS][2][2][2]; +static UINT g_DisplayAdapter; +static D3DDEVTYPE g_DeviceType; +static ImageFormat g_DeviceFormat; +static bool g_bSupportsD24S8; +static bool g_bSupportsD24X8; +static bool g_bSupportsD16; +static bool g_bSupportsD24X4S4; +static bool g_bSupportsD15S1; + +//----------------------------------------------------------------------------- +// Determines what formats we actually *do* support +//----------------------------------------------------------------------------- +static bool TestTextureFormat( D3DFORMAT format, bool bIsRenderTarget, + bool bIsVertexTexture, bool bIsFilterableRequired ) +{ + int nUsage = bIsRenderTarget ? D3DUSAGE_RENDERTARGET : 0; + if ( bIsVertexTexture ) + { + // vertex textures never need filtering + nUsage |= D3DUSAGE_QUERY_VERTEXTEXTURE; + } + if ( bIsFilterableRequired ) + { + nUsage |= D3DUSAGE_QUERY_FILTER; + } + + HRESULT hr; + + // IHV depth texture formats require a slightly different check... + if ( !IsX360() && bIsRenderTarget && ( ( format == NVFMT_RAWZ ) || ( format == NVFMT_INTZ ) || + ( format == D3DFMT_D16 ) || ( format == D3DFMT_D24S8 ) || + ( format == ATIFMT_D16 ) || ( format == ATIFMT_D24S8 ) ) ) + { + hr = D3D()->CheckDeviceFormat( + g_DisplayAdapter, g_DeviceType, ImageLoader::ImageFormatToD3DFormat( g_DeviceFormat ), + D3DUSAGE_DEPTHSTENCIL, D3DRTYPE_TEXTURE, format ); + } + else if ( !IsX360() || !bIsRenderTarget ) + { + // See if we can do it! + hr = D3D()->CheckDeviceFormat( + g_DisplayAdapter, g_DeviceType, ImageLoader::ImageFormatToD3DFormat( g_DeviceFormat ), + nUsage, D3DRTYPE_TEXTURE, format ); + } + else // 360 + { + // 360 can only validate render targets as surface display format + hr = D3D()->CheckDeviceFormat( g_DisplayAdapter, g_DeviceType, format, 0, D3DRTYPE_SURFACE, format ); + } + + return SUCCEEDED( hr ); +} + +D3DFORMAT GetNearestD3DColorFormat( ImageFormat fmt, + bool isRenderTarget, bool bIsVertexTexture, + bool bIsFilterableRequired) +{ + switch(fmt) + { + case IMAGE_FORMAT_RGBA8888: + case IMAGE_FORMAT_ABGR8888: + case IMAGE_FORMAT_ARGB8888: + case IMAGE_FORMAT_BGRA8888: + if (TestTextureFormat(D3DFMT_A8R8G8B8, isRenderTarget, bIsVertexTexture, bIsFilterableRequired)) + return D3DFMT_A8R8G8B8; + if (TestTextureFormat(D3DFMT_A4R4G4B4, isRenderTarget, bIsVertexTexture, bIsFilterableRequired)) + return D3DFMT_A4R4G4B4; + break; + +#if defined( _X360 ) + case IMAGE_FORMAT_LINEAR_RGBA8888: + case IMAGE_FORMAT_LINEAR_ABGR8888: + case IMAGE_FORMAT_LINEAR_ARGB8888: + case IMAGE_FORMAT_LINEAR_BGRA8888: + // same as above - all xxxx8888 RGBA ordering funnels to d3d a8r8g8b8 + if ( TestTextureFormat( D3DFMT_LIN_A8R8G8B8, isRenderTarget, bIsVertexTexture, bIsFilterableRequired ) ) + return D3DFMT_LIN_A8R8G8B8; + break; +#endif + +#if defined( _X360 ) + case IMAGE_FORMAT_LINEAR_BGRX8888: + if ( TestTextureFormat( D3DFMT_LIN_X8R8G8B8, isRenderTarget, bIsVertexTexture, bIsFilterableRequired ) ) + return D3DFMT_LIN_X8R8G8B8; + break; +#endif + + case IMAGE_FORMAT_BGRX8888: + // We want this format to return exactly it's equivalent so that + // when we create render targets to blit to from the framebuffer, + // the CopyRect won't fail due to format mismatches. + if (TestTextureFormat(D3DFMT_X8R8G8B8, isRenderTarget, bIsVertexTexture, bIsFilterableRequired)) + return D3DFMT_X8R8G8B8; + + // fall through. . . . + case IMAGE_FORMAT_RGB888: + case IMAGE_FORMAT_BGR888: +#if !defined( _X360 ) + if (TestTextureFormat(D3DFMT_R8G8B8, isRenderTarget, bIsVertexTexture, bIsFilterableRequired)) + return D3DFMT_R8G8B8; +#endif + if (TestTextureFormat(D3DFMT_X8R8G8B8, isRenderTarget, bIsVertexTexture, bIsFilterableRequired)) + return D3DFMT_X8R8G8B8; + if (TestTextureFormat(D3DFMT_A8R8G8B8, isRenderTarget, bIsVertexTexture, bIsFilterableRequired)) + return D3DFMT_A8R8G8B8; + if (TestTextureFormat(D3DFMT_R5G6B5, isRenderTarget, bIsVertexTexture, bIsFilterableRequired)) + return D3DFMT_R5G6B5; + if (TestTextureFormat(D3DFMT_X1R5G5B5, isRenderTarget, bIsVertexTexture, bIsFilterableRequired)) + return D3DFMT_X1R5G5B5; + if (TestTextureFormat(D3DFMT_A1R5G5B5, isRenderTarget, bIsVertexTexture, bIsFilterableRequired)) + return D3DFMT_A1R5G5B5; + break; + + case IMAGE_FORMAT_BGR565: + case IMAGE_FORMAT_RGB565: + if (TestTextureFormat(D3DFMT_R5G6B5, isRenderTarget, bIsVertexTexture, bIsFilterableRequired)) + return D3DFMT_R5G6B5; + if (TestTextureFormat(D3DFMT_X1R5G5B5, isRenderTarget, bIsVertexTexture, bIsFilterableRequired)) + return D3DFMT_X1R5G5B5; + if (TestTextureFormat(D3DFMT_A1R5G5B5, isRenderTarget, bIsVertexTexture, bIsFilterableRequired)) + return D3DFMT_A1R5G5B5; +#if !defined( _X360 ) + if (TestTextureFormat(D3DFMT_R8G8B8, isRenderTarget, bIsVertexTexture, bIsFilterableRequired)) + return D3DFMT_R8G8B8; +#endif + if (TestTextureFormat(D3DFMT_X8R8G8B8, isRenderTarget, bIsVertexTexture, bIsFilterableRequired)) + return D3DFMT_X8R8G8B8; + if (TestTextureFormat(D3DFMT_A8R8G8B8, isRenderTarget, bIsVertexTexture, bIsFilterableRequired)) + return D3DFMT_A8R8G8B8; + break; + + case IMAGE_FORMAT_BGRX5551: + if (TestTextureFormat(D3DFMT_X1R5G5B5, isRenderTarget, bIsVertexTexture, bIsFilterableRequired)) + return D3DFMT_X1R5G5B5; + if (TestTextureFormat(D3DFMT_A1R5G5B5, isRenderTarget, bIsVertexTexture, bIsFilterableRequired)) + return D3DFMT_A1R5G5B5; + if (TestTextureFormat(D3DFMT_R5G6B5, isRenderTarget, bIsVertexTexture, bIsFilterableRequired)) + return D3DFMT_R5G6B5; +#if !defined( _X360 ) + if (TestTextureFormat(D3DFMT_R8G8B8, isRenderTarget, bIsVertexTexture, bIsFilterableRequired)) + return D3DFMT_R8G8B8; +#endif + if (TestTextureFormat(D3DFMT_X8R8G8B8, isRenderTarget, bIsVertexTexture, bIsFilterableRequired)) + return D3DFMT_X8R8G8B8; + if (TestTextureFormat(D3DFMT_A8R8G8B8, isRenderTarget, bIsVertexTexture, bIsFilterableRequired)) + return D3DFMT_A8R8G8B8; + break; + +#if defined( _X360 ) + case IMAGE_FORMAT_LINEAR_BGRX5551: + if ( TestTextureFormat( D3DFMT_LIN_X1R5G5B5, isRenderTarget, bIsVertexTexture, bIsFilterableRequired ) ) + return D3DFMT_LIN_X1R5G5B5; + break; +#endif + + case IMAGE_FORMAT_BGRA5551: + if (TestTextureFormat(D3DFMT_A1R5G5B5, isRenderTarget, bIsVertexTexture, bIsFilterableRequired)) + return D3DFMT_A1R5G5B5; + if (TestTextureFormat(D3DFMT_A4R4G4B4, isRenderTarget, bIsVertexTexture, bIsFilterableRequired)) + return D3DFMT_A4R4G4B4; + if (TestTextureFormat(D3DFMT_A8R8G8B8, isRenderTarget, bIsVertexTexture, bIsFilterableRequired)) + return D3DFMT_A8R8G8B8; + break; + + case IMAGE_FORMAT_BGRA4444: + if (TestTextureFormat(D3DFMT_A4R4G4B4, isRenderTarget, bIsVertexTexture, bIsFilterableRequired)) + return D3DFMT_A4R4G4B4; + if (TestTextureFormat(D3DFMT_A8R8G8B8, isRenderTarget, bIsVertexTexture, bIsFilterableRequired)) + return D3DFMT_A8R8G8B8; + break; + + case IMAGE_FORMAT_I8: + if (TestTextureFormat(D3DFMT_L8, isRenderTarget, bIsVertexTexture, bIsFilterableRequired)) + return D3DFMT_L8; + if (TestTextureFormat(D3DFMT_A8R8G8B8, isRenderTarget, bIsVertexTexture, bIsFilterableRequired)) + return D3DFMT_A8R8G8B8; + break; + +#if defined( _X360 ) + case IMAGE_FORMAT_LINEAR_I8: + if ( TestTextureFormat( D3DFMT_LIN_L8, isRenderTarget, bIsVertexTexture, bIsFilterableRequired ) ) + return D3DFMT_LIN_L8; + break; +#endif + + case IMAGE_FORMAT_IA88: + if (TestTextureFormat(D3DFMT_A8L8, isRenderTarget, bIsVertexTexture, bIsFilterableRequired)) + return D3DFMT_A8L8; + if (TestTextureFormat(D3DFMT_A8R8G8B8, isRenderTarget, bIsVertexTexture, bIsFilterableRequired)) + return D3DFMT_A8R8G8B8; + break; + + case IMAGE_FORMAT_A8: + if (TestTextureFormat(D3DFMT_A8, isRenderTarget, bIsVertexTexture, bIsFilterableRequired)) + return D3DFMT_A8; + if (TestTextureFormat(D3DFMT_A8R8G8B8, isRenderTarget, bIsVertexTexture, bIsFilterableRequired)) + return D3DFMT_A8R8G8B8; + break; + + case IMAGE_FORMAT_DXT1: + case IMAGE_FORMAT_DXT1_ONEBITALPHA: + case IMAGE_FORMAT_DXT1_RUNTIME: + if (TestTextureFormat(D3DFMT_DXT1, isRenderTarget, bIsVertexTexture, bIsFilterableRequired)) + return D3DFMT_DXT1; + break; + + case IMAGE_FORMAT_DXT3: + if (TestTextureFormat(D3DFMT_DXT3, isRenderTarget, bIsVertexTexture, bIsFilterableRequired )) + return D3DFMT_DXT3; + break; + + case IMAGE_FORMAT_DXT5: + case IMAGE_FORMAT_DXT5_RUNTIME: + if (TestTextureFormat(D3DFMT_DXT5, isRenderTarget, bIsVertexTexture, bIsFilterableRequired )) + return D3DFMT_DXT5; + break; + + case IMAGE_FORMAT_UV88: + if (TestTextureFormat(D3DFMT_V8U8, isRenderTarget, bIsVertexTexture, bIsFilterableRequired )) + return D3DFMT_V8U8; + break; + + case IMAGE_FORMAT_UVWQ8888: + if (TestTextureFormat(D3DFMT_Q8W8V8U8, isRenderTarget, bIsVertexTexture, bIsFilterableRequired )) + return D3DFMT_Q8W8V8U8; + break; + + case IMAGE_FORMAT_UVLX8888: + if (TestTextureFormat(D3DFMT_X8L8V8U8, isRenderTarget, bIsVertexTexture, bIsFilterableRequired )) + return D3DFMT_X8L8V8U8; + break; + + case IMAGE_FORMAT_RGBA16161616F: + if ( TestTextureFormat( D3DFMT_A16B16G16R16F, isRenderTarget, bIsVertexTexture, bIsFilterableRequired ) ) + return D3DFMT_A16B16G16R16F; + if ( TestTextureFormat( D3DFMT_A16B16G16R16, isRenderTarget, bIsVertexTexture, bIsFilterableRequired ) ) + return D3DFMT_A16B16G16R16; + break; + + case IMAGE_FORMAT_RGBA16161616: + if ( TestTextureFormat( D3DFMT_A16B16G16R16, isRenderTarget, bIsVertexTexture, bIsFilterableRequired ) ) + return D3DFMT_A16B16G16R16; + if ( TestTextureFormat( D3DFMT_A16B16G16R16F, isRenderTarget, bIsVertexTexture, bIsFilterableRequired ) ) + return D3DFMT_A16B16G16R16F; + break; + +#if defined( _X360 ) + case IMAGE_FORMAT_LINEAR_RGBA16161616: + if ( TestTextureFormat( D3DFMT_LIN_A16B16G16R16, isRenderTarget, bIsVertexTexture, bIsFilterableRequired ) ) + return D3DFMT_LIN_A16B16G16R16; + break; +#endif + + case IMAGE_FORMAT_R32F: + if ( TestTextureFormat( D3DFMT_R32F, isRenderTarget, bIsVertexTexture, bIsFilterableRequired ) ) + return D3DFMT_R32F; + break; + + case IMAGE_FORMAT_RGBA32323232F: + if ( TestTextureFormat( D3DFMT_A32B32G32R32F, isRenderTarget, bIsVertexTexture, bIsFilterableRequired ) ) + return D3DFMT_A32B32G32R32F; + break; + +#if defined( _X360 ) + case IMAGE_FORMAT_X360_DST16: + return D3DFMT_D16; + + case IMAGE_FORMAT_X360_DST24: + return D3DFMT_D24S8; + + case IMAGE_FORMAT_X360_DST24F: + return D3DFMT_D24FS8; + + case IMAGE_FORMAT_LE_BGRX8888: + return D3DFMT_LE_X8R8G8B8; + + case IMAGE_FORMAT_LE_BGRA8888: + return D3DFMT_LE_A8R8G8B8; +#endif + + // nVidia overloads DST formats as texture formats + case IMAGE_FORMAT_NV_DST16: + if ( TestTextureFormat( D3DFMT_D16, isRenderTarget, bIsVertexTexture, bIsFilterableRequired ) ) + return D3DFMT_D16; + break; + + case IMAGE_FORMAT_NV_DST24: + if ( TestTextureFormat( D3DFMT_D24S8, isRenderTarget, bIsVertexTexture, bIsFilterableRequired ) ) + return D3DFMT_D24S8; + break; + case IMAGE_FORMAT_NV_INTZ: + if ( TestTextureFormat( NVFMT_INTZ, isRenderTarget, bIsVertexTexture, false ) ) + return NVFMT_INTZ; + break; + + case IMAGE_FORMAT_NV_RAWZ: + if ( TestTextureFormat( NVFMT_RAWZ, isRenderTarget, bIsVertexTexture, false ) ) + return NVFMT_RAWZ; + break; + + case IMAGE_FORMAT_NV_NULL: + if ( TestTextureFormat( NVFMT_NULL, isRenderTarget, bIsVertexTexture, false ) ) + return NVFMT_NULL; + break; + + case IMAGE_FORMAT_ATI_DST16: + if ( TestTextureFormat( ATIFMT_D16, isRenderTarget, bIsVertexTexture, false ) ) + return ATIFMT_D16; + break; + + case IMAGE_FORMAT_ATI_DST24: + if ( TestTextureFormat( ATIFMT_D24S8, isRenderTarget, bIsVertexTexture, false ) ) + return ATIFMT_D24S8; + break; + + case IMAGE_FORMAT_ATI2N: + if ( TestTextureFormat( ATIFMT_ATI2N, isRenderTarget, bIsVertexTexture, bIsFilterableRequired ) ) + return ATIFMT_ATI2N; + break; + + case IMAGE_FORMAT_ATI1N: + if ( TestTextureFormat( ATIFMT_ATI1N, isRenderTarget, bIsVertexTexture, bIsFilterableRequired ) ) + return ATIFMT_ATI1N; + break; + } + + return D3DFMT_UNKNOWN; +} + +void InitializeColorInformation( UINT displayAdapter, D3DDEVTYPE deviceType, + ImageFormat displayFormat ) +{ + g_DisplayAdapter = displayAdapter; + g_DeviceType = deviceType; + g_DeviceFormat = displayFormat; + + int fmt = 0; + while ( fmt < NUM_IMAGE_FORMATS ) + { + for ( int nVertexTexture = 0; nVertexTexture <= 1; ++nVertexTexture ) + { + for ( int nRenderTarget = 0; nRenderTarget <= 1; ++nRenderTarget ) + { + for ( int nFilterable = 0; nFilterable <= 1; ++nFilterable ) + { + g_D3DColorFormat[fmt][nVertexTexture][nRenderTarget][nFilterable] = + GetNearestD3DColorFormat( (ImageFormat)fmt, nRenderTarget != 0, nVertexTexture != 0, nFilterable != 0 ); + } + } + } + ++fmt; + } + + // Check the depth formats + HRESULT hr = D3D()->CheckDeviceFormat( + g_DisplayAdapter, g_DeviceType, ImageLoader::ImageFormatToD3DFormat( g_DeviceFormat ), + D3DUSAGE_DEPTHSTENCIL, D3DRTYPE_SURFACE, D3DFMT_D24S8 ); + g_bSupportsD24S8 = !FAILED(hr); + + hr = D3D()->CheckDeviceFormat( + g_DisplayAdapter, g_DeviceType, ImageLoader::ImageFormatToD3DFormat( g_DeviceFormat ), + D3DUSAGE_DEPTHSTENCIL, D3DRTYPE_SURFACE, D3DFMT_D24X8 ); + g_bSupportsD24X8 = !FAILED(hr); + + hr = D3D()->CheckDeviceFormat( + g_DisplayAdapter, g_DeviceType, ImageLoader::ImageFormatToD3DFormat( g_DeviceFormat ), + D3DUSAGE_DEPTHSTENCIL, D3DRTYPE_SURFACE, D3DFMT_D16 ); + g_bSupportsD16 = !FAILED(hr); + +#if !defined( _X360 ) + hr = D3D()->CheckDeviceFormat( + g_DisplayAdapter, g_DeviceType, ImageLoader::ImageFormatToD3DFormat( g_DeviceFormat ), + D3DUSAGE_DEPTHSTENCIL, D3DRTYPE_SURFACE, D3DFMT_D24X4S4 ); + g_bSupportsD24X4S4 = !FAILED(hr); +#else + g_bSupportsD24X4S4 = false; +#endif + +#if !defined( _X360 ) + hr = D3D()->CheckDeviceFormat( + g_DisplayAdapter, g_DeviceType, ImageLoader::ImageFormatToD3DFormat( g_DeviceFormat ), + D3DUSAGE_DEPTHSTENCIL, D3DRTYPE_SURFACE, D3DFMT_D15S1 ); + g_bSupportsD15S1 = !FAILED(hr); +#else + g_bSupportsD15S1 = false; +#endif +} + + +//----------------------------------------------------------------------------- +// Returns true if compressed textures are supported +//----------------------------------------------------------------------------- +bool D3DSupportsCompressedTextures() +{ + return (g_D3DColorFormat[IMAGE_FORMAT_DXT1][0][0][0] != D3DFMT_UNKNOWN) && + (g_D3DColorFormat[IMAGE_FORMAT_DXT3][0][0][0] != D3DFMT_UNKNOWN) && + (g_D3DColorFormat[IMAGE_FORMAT_DXT5][0][0][0] != D3DFMT_UNKNOWN); +} + + +//----------------------------------------------------------------------------- +// Returns closest supported format +//----------------------------------------------------------------------------- +ImageFormat FindNearestSupportedFormat( ImageFormat format, bool bIsVertexTexture, bool bIsRenderTarget, bool bFilterableRequired ) +{ + return ImageLoader::D3DFormatToImageFormat( g_D3DColorFormat[format][bIsVertexTexture][bIsRenderTarget][bFilterableRequired] ); +} + + +//----------------------------------------------------------------------------- +// Returns true if compressed textures are supported +//----------------------------------------------------------------------------- +bool D3DSupportsDepthTexture(D3DFORMAT format) +{ + // See if we can do it! + HRESULT hr = D3D()->CheckDeviceFormat( + g_DisplayAdapter, g_DeviceType, ImageLoader::ImageFormatToD3DFormat(g_DeviceFormat), + D3DUSAGE_DEPTHSTENCIL, D3DRTYPE_TEXTURE, format); + + return !FAILED(hr); +} + + +//----------------------------------------------------------------------------- +// Returns true if the depth format is compatible with the display +//----------------------------------------------------------------------------- +static inline bool IsDepthFormatCompatible( int nAdapter, ImageFormat displayFormat, ImageFormat renderTargetFormat, D3DFORMAT depthFormat ) +{ + D3DFORMAT d3dDisplayFormat = ImageLoader::ImageFormatToD3DFormat( displayFormat ); + D3DFORMAT d3dRenderTargetFormat = ImageLoader::ImageFormatToD3DFormat( renderTargetFormat ); + + // Verify that the depth format is compatible. + HRESULT hr = D3D()->CheckDepthStencilMatch( nAdapter, DX8_DEVTYPE, + d3dDisplayFormat, d3dRenderTargetFormat, depthFormat); + return !FAILED(hr); +} + +//----------------------------------------------------------------------------- +// Finds the nearest supported depth buffer format +//----------------------------------------------------------------------------- +D3DFORMAT FindNearestSupportedDepthFormat( int nAdapter, ImageFormat displayFormat, ImageFormat renderTargetFormat, D3DFORMAT depthFormat ) +{ + // This is the default case, used for rendering to the main render target + Assert( displayFormat != IMAGE_FORMAT_UNKNOWN && renderTargetFormat != IMAGE_FORMAT_UNKNOWN ); + + switch (depthFormat) + { +#if defined( _X360 ) + case D3DFMT_D24FS8: + return D3DFMT_D24FS8; + + case D3DFMT_LIN_D24S8: + if ( g_bSupportsD24S8 && IsDepthFormatCompatible( nAdapter, displayFormat, renderTargetFormat, D3DFMT_LIN_D24S8 ) ) + return D3DFMT_LIN_D24S8; +#endif + case D3DFMT_D24S8: + if ( g_bSupportsD24S8 && IsDepthFormatCompatible( nAdapter, displayFormat, renderTargetFormat, D3DFMT_D24S8 ) ) + return D3DFMT_D24S8; +#if !defined( _X360 ) + if ( g_bSupportsD24X4S4 && IsDepthFormatCompatible( nAdapter, displayFormat, renderTargetFormat, D3DFMT_D24X4S4 ) ) + return D3DFMT_D24X4S4; + if ( g_bSupportsD15S1 && IsDepthFormatCompatible( nAdapter, displayFormat, renderTargetFormat, D3DFMT_D15S1 ) ) + return D3DFMT_D15S1; +#endif + if ( g_bSupportsD24X8 && IsDepthFormatCompatible( nAdapter, displayFormat, renderTargetFormat, D3DFMT_D24X8 ) ) + return D3DFMT_D24X8; + if ( g_bSupportsD16 && IsDepthFormatCompatible( nAdapter, displayFormat, renderTargetFormat, D3DFMT_D16 ) ) + return D3DFMT_D16; + break; + + case D3DFMT_D24X8: + if ( g_bSupportsD24X8 && IsDepthFormatCompatible( nAdapter, displayFormat, renderTargetFormat, D3DFMT_D24X8 ) ) + return D3DFMT_D24X8; + if ( g_bSupportsD24S8 && IsDepthFormatCompatible( nAdapter, displayFormat, renderTargetFormat, D3DFMT_D24S8 ) ) + return D3DFMT_D24S8; +#if !defined( _X360 ) + if ( g_bSupportsD24X4S4 && IsDepthFormatCompatible( nAdapter, displayFormat, renderTargetFormat, D3DFMT_D24X4S4 ) ) + return D3DFMT_D24X4S4; +#endif + if ( g_bSupportsD16 && IsDepthFormatCompatible( nAdapter, displayFormat, renderTargetFormat, D3DFMT_D16 ) ) + return D3DFMT_D16; +#if !defined( _X360 ) + if ( g_bSupportsD15S1 && IsDepthFormatCompatible( nAdapter, displayFormat, renderTargetFormat, D3DFMT_D15S1 ) ) + return D3DFMT_D15S1; +#endif + break; + + case D3DFMT_D16: + if ( g_bSupportsD16 && IsDepthFormatCompatible( nAdapter, displayFormat, renderTargetFormat, D3DFMT_D16 ) ) + return D3DFMT_D16; +#if !defined( _X360 ) + if ( g_bSupportsD15S1 && IsDepthFormatCompatible( nAdapter, displayFormat, renderTargetFormat, D3DFMT_D15S1 ) ) + return D3DFMT_D15S1; +#endif + if ( g_bSupportsD24X8 && IsDepthFormatCompatible( nAdapter, displayFormat, renderTargetFormat, D3DFMT_D24X8 ) ) + return D3DFMT_D24X8; + if ( g_bSupportsD24S8 && IsDepthFormatCompatible( nAdapter, displayFormat, renderTargetFormat, D3DFMT_D24S8 ) ) + return D3DFMT_D24S8; +#if !defined( _X360 ) + if ( g_bSupportsD24X4S4 && IsDepthFormatCompatible( nAdapter, displayFormat, renderTargetFormat, D3DFMT_D24X4S4 ) ) + return D3DFMT_D24X4S4; +#endif + break; + } + + Assert( 0 ); + return D3DFMT_D16; +} + + +//----------------------------------------------------------------------------- +// Is a display buffer valid? +//----------------------------------------------------------------------------- +static inline bool IsFrameBufferFormatValid( UINT displayAdapter, D3DDEVTYPE deviceType, + D3DFORMAT displayFormat, D3DFORMAT backBufferFormat, bool bIsWindowed ) +{ + HRESULT hr = D3D()->CheckDeviceType( displayAdapter, deviceType, displayFormat, + backBufferFormat, bIsWindowed ); + return !FAILED(hr); +} + + +//----------------------------------------------------------------------------- +// Finds the nearest supported frame buffer format +//----------------------------------------------------------------------------- +ImageFormat FindNearestSupportedBackBufferFormat( UINT displayAdapter, + D3DDEVTYPE deviceType, ImageFormat displayFormat, ImageFormat backBufferFormat, bool bIsWindowed ) +{ + D3DFORMAT d3dDisplayFormat = ImageLoader::ImageFormatToD3DFormat( displayFormat ); + switch (backBufferFormat) + { + case IMAGE_FORMAT_RGBA8888: + case IMAGE_FORMAT_ABGR8888: + case IMAGE_FORMAT_ARGB8888: + case IMAGE_FORMAT_BGRA8888: + case IMAGE_FORMAT_BGRA4444: // This is not supported ever; bump up to 32 bit + if (IsFrameBufferFormatValid( displayAdapter, deviceType, d3dDisplayFormat, D3DFMT_A8R8G8B8, bIsWindowed )) + return IMAGE_FORMAT_BGRA8888; + + // Bye, bye dest alpha + if (IsFrameBufferFormatValid( displayAdapter, deviceType, d3dDisplayFormat, D3DFMT_X8R8G8B8, bIsWindowed )) + return IMAGE_FORMAT_BGRX8888; + + if (IsFrameBufferFormatValid( displayAdapter, deviceType, d3dDisplayFormat, D3DFMT_R5G6B5, bIsWindowed )) + return IMAGE_FORMAT_BGR565; + + if (IsFrameBufferFormatValid( displayAdapter, deviceType, d3dDisplayFormat, D3DFMT_A1R5G5B5, bIsWindowed )) + return IMAGE_FORMAT_BGRA5551; + + if (IsFrameBufferFormatValid( displayAdapter, deviceType, d3dDisplayFormat, D3DFMT_X1R5G5B5, bIsWindowed )) + return IMAGE_FORMAT_BGRX5551; + + return IMAGE_FORMAT_UNKNOWN; + + case IMAGE_FORMAT_RGB888: + case IMAGE_FORMAT_BGR888: + case IMAGE_FORMAT_RGB888_BLUESCREEN: + case IMAGE_FORMAT_BGRX8888: + if (IsFrameBufferFormatValid( displayAdapter, deviceType, d3dDisplayFormat, D3DFMT_X8R8G8B8, bIsWindowed )) + return IMAGE_FORMAT_BGRX8888; + + if (IsFrameBufferFormatValid( displayAdapter, deviceType, d3dDisplayFormat, D3DFMT_A8R8G8B8, bIsWindowed )) + return IMAGE_FORMAT_BGRA8888; + + if (IsFrameBufferFormatValid( displayAdapter, deviceType, d3dDisplayFormat, D3DFMT_R5G6B5, bIsWindowed )) + return IMAGE_FORMAT_BGR565; + + if (IsFrameBufferFormatValid( displayAdapter, deviceType, d3dDisplayFormat, D3DFMT_A1R5G5B5, bIsWindowed )) + return IMAGE_FORMAT_BGRA5551; + + if (IsFrameBufferFormatValid( displayAdapter, deviceType, d3dDisplayFormat, D3DFMT_X1R5G5B5, bIsWindowed )) + return IMAGE_FORMAT_BGRX5551; + + return IMAGE_FORMAT_UNKNOWN; + + case IMAGE_FORMAT_RGB565: + case IMAGE_FORMAT_BGR565: + if (IsFrameBufferFormatValid( displayAdapter, deviceType, d3dDisplayFormat, D3DFMT_R5G6B5, bIsWindowed )) + return IMAGE_FORMAT_BGR565; + + if (IsFrameBufferFormatValid( displayAdapter, deviceType, d3dDisplayFormat, D3DFMT_A1R5G5B5, bIsWindowed )) + return IMAGE_FORMAT_BGRA5551; + + if (IsFrameBufferFormatValid( displayAdapter, deviceType, d3dDisplayFormat, D3DFMT_X1R5G5B5, bIsWindowed )) + return IMAGE_FORMAT_BGRX5551; + + if (IsFrameBufferFormatValid( displayAdapter, deviceType, d3dDisplayFormat, D3DFMT_X8R8G8B8, bIsWindowed )) + return IMAGE_FORMAT_BGRX8888; + + if (IsFrameBufferFormatValid( displayAdapter, deviceType, d3dDisplayFormat, D3DFMT_A8R8G8B8, bIsWindowed )) + return IMAGE_FORMAT_BGRA8888; + + return IMAGE_FORMAT_UNKNOWN; + + case IMAGE_FORMAT_BGRX5551: + if (IsFrameBufferFormatValid( displayAdapter, deviceType, d3dDisplayFormat, D3DFMT_X1R5G5B5, bIsWindowed )) + return IMAGE_FORMAT_BGRX5551; + + if (IsFrameBufferFormatValid( displayAdapter, deviceType, d3dDisplayFormat, D3DFMT_A1R5G5B5, bIsWindowed )) + return IMAGE_FORMAT_BGRA5551; + + if (IsFrameBufferFormatValid( displayAdapter, deviceType, d3dDisplayFormat, D3DFMT_R5G6B5, bIsWindowed )) + return IMAGE_FORMAT_BGR565; + + if (IsFrameBufferFormatValid( displayAdapter, deviceType, d3dDisplayFormat, D3DFMT_X8R8G8B8, bIsWindowed )) + return IMAGE_FORMAT_BGRX8888; + + if (IsFrameBufferFormatValid( displayAdapter, deviceType, d3dDisplayFormat, D3DFMT_A8R8G8B8, bIsWindowed )) + return IMAGE_FORMAT_BGRA8888; + + return IMAGE_FORMAT_UNKNOWN; + + case IMAGE_FORMAT_BGRA5551: + if (IsFrameBufferFormatValid( displayAdapter, deviceType, d3dDisplayFormat, D3DFMT_A1R5G5B5, bIsWindowed )) + return IMAGE_FORMAT_BGRA5551; + + if (IsFrameBufferFormatValid( displayAdapter, deviceType, d3dDisplayFormat, D3DFMT_X1R5G5B5, bIsWindowed )) + return IMAGE_FORMAT_BGRX5551; + + if (IsFrameBufferFormatValid( displayAdapter, deviceType, d3dDisplayFormat, D3DFMT_R5G6B5, bIsWindowed )) + return IMAGE_FORMAT_BGR565; + + if (IsFrameBufferFormatValid( displayAdapter, deviceType, d3dDisplayFormat, D3DFMT_A8R8G8B8, bIsWindowed )) + return IMAGE_FORMAT_BGRA8888; + + if (IsFrameBufferFormatValid( displayAdapter, deviceType, d3dDisplayFormat, D3DFMT_X8R8G8B8, bIsWindowed )) + return IMAGE_FORMAT_BGRX8888; + + return IMAGE_FORMAT_UNKNOWN; + } + + return IMAGE_FORMAT_UNKNOWN; +} + +#if defined( _X360 ) +const char *D3DFormatName( D3DFORMAT d3dFormat ) +{ + if ( IS_D3DFORMAT_SRGB( d3dFormat ) ) + { + // sanitize the format from possible sRGB state for comparison purposes + d3dFormat = MAKE_NON_SRGB_FMT( d3dFormat ); + } + + switch ( d3dFormat ) + { + case D3DFMT_A8R8G8B8: + return "D3DFMT_A8R8G8B8"; + case D3DFMT_LIN_A8R8G8B8: + return "D3DFMT_LIN_A8R8G8B8"; + case D3DFMT_X8R8G8B8: + return "D3DFMT_X8R8G8B8"; + case D3DFMT_LIN_X8R8G8B8: + return "D3DFMT_LIN_X8R8G8B8"; + case D3DFMT_R5G6B5: + return "D3DFMT_R5G6B5"; + case D3DFMT_X1R5G5B5: + return "D3DFMT_X1R5G5B5"; + case D3DFMT_A1R5G5B5: + return "D3DFMT_A1R5G5B5"; + case D3DFMT_A4R4G4B4: + return "D3DFMT_A4R4G4B4"; + case D3DFMT_L8: + return "D3DFMT_L8"; + case D3DFMT_A8L8: + return "D3DFMT_A8L8"; + case D3DFMT_A8: + return "D3DFMT_A8"; + case D3DFMT_DXT1: + return "D3DFMT_DXT1"; + case D3DFMT_DXT3: + return "D3DFMT_DXT3"; + case D3DFMT_DXT5: + return "D3DFMT_DXT5"; + case D3DFMT_V8U8: + return "D3DFMT_V8U8"; + case D3DFMT_Q8W8V8U8: + return "D3DFMT_Q8W8V8U8"; + case D3DFMT_D16: + return "D3DFMT_D16"; + case D3DFMT_D24S8: + return "D3DFMT_D24S8"; + case D3DFMT_D24FS8: + return "D3DFMT_D24FS8"; + case D3DFMT_LIN_D24S8: + return "D3DFMT_LIN_D24S8"; + case D3DFMT_A16B16G16R16: + return "D3DFMT_A16B16G16R16"; + case D3DFMT_LIN_A16B16G16R16: + return "D3DFMT_LIN_A16B16G16R16"; + } + return "???"; +} +#endif diff --git a/materialsystem/shaderapidx9/colorformatdx8.h b/materialsystem/shaderapidx9/colorformatdx8.h new file mode 100644 index 0000000..20e9973 --- /dev/null +++ b/materialsystem/shaderapidx9/colorformatdx8.h @@ -0,0 +1,60 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// + +#ifndef COLORFORMATDX8_H +#define COLORFORMATDX8_H + +#include +#include "togl/rendermechanism.h" + +// FOURCC formats for ATI shadow depth textures +#define ATIFMT_D16 ((D3DFORMAT)(MAKEFOURCC('D','F','1','6'))) +#define ATIFMT_D24S8 ((D3DFORMAT)(MAKEFOURCC('D','F','2','4'))) + +// FOURCC formats for ATI2N and ATI1N compressed textures (360 and DX10 parts also do these) +#define ATIFMT_ATI2N ((D3DFORMAT) MAKEFOURCC('A', 'T', 'I', '2')) +#define ATIFMT_ATI1N ((D3DFORMAT) MAKEFOURCC('A', 'T', 'I', '1')) + +// FOURCC formats for nVidia shadow depth textures +#define NVFMT_RAWZ ((D3DFORMAT)(MAKEFOURCC('R','A','W','Z'))) +#define NVFMT_INTZ ((D3DFORMAT)(MAKEFOURCC('I','N','T','Z'))) + +// FOURCC format for nVidia null texture format +#define NVFMT_NULL ((D3DFORMAT)(MAKEFOURCC('N','U','L','L'))) + + +//----------------------------------------------------------------------------- +// Finds the nearest supported frame buffer format +//----------------------------------------------------------------------------- +ImageFormat FindNearestSupportedBackBufferFormat( unsigned int displayAdapter, D3DDEVTYPE deviceType, + ImageFormat displayFormat, ImageFormat backBufferFormat, bool bIsWindowed ); + +//----------------------------------------------------------------------------- +// Initializes the color format informat; call it every time display mode changes +//----------------------------------------------------------------------------- +void InitializeColorInformation( unsigned int displayAdapter, D3DDEVTYPE deviceType, + ImageFormat displayFormat ); + +//----------------------------------------------------------------------------- +// Returns true if compressed textures are supported +//----------------------------------------------------------------------------- +bool D3DSupportsCompressedTextures(); + +//----------------------------------------------------------------------------- +// Returns closest supported format +//----------------------------------------------------------------------------- +ImageFormat FindNearestSupportedFormat( ImageFormat format, bool bIsVertexTexture, bool bIsRenderTarget, bool bFilterableRequired ); + +//----------------------------------------------------------------------------- +// Finds the nearest supported depth buffer format +//----------------------------------------------------------------------------- +D3DFORMAT FindNearestSupportedDepthFormat( int nAdapter, ImageFormat displayFormat, ImageFormat renderTargetFormat, D3DFORMAT depthFormat ); + +const char *D3DFormatName( D3DFORMAT d3dFormat ); + +#endif // COLORFORMATDX8_H diff --git a/materialsystem/shaderapidx9/cvballoctracker.cpp b/materialsystem/shaderapidx9/cvballoctracker.cpp new file mode 100644 index 0000000..8c3cdd0 --- /dev/null +++ b/materialsystem/shaderapidx9/cvballoctracker.cpp @@ -0,0 +1,764 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: tracks VB allocations (and compressed/uncompressed vertex memory usage) +// +//===========================================================================// + +#include "materialsystem/imaterial.h" +#include "imeshdx8.h" +#include "convar.h" +#include "tier1/utlhash.h" +#include "tier1/utlstack.h" + +#include "materialsystem/ivballoctracker.h" + + +//----------------------------------------------------------------------------- +// +// Types +// +//----------------------------------------------------------------------------- + +#if ENABLE_VB_ALLOC_TRACKER + +// FIXME: combine this into the lower bits of VertexFormat_t +typedef uint64 VertexElementMap_t; + +enum Saving_t +{ + SAVING_COMPRESSION = 0, + SAVING_REMOVAL = 1, + SAVING_ALIGNMENT = 2 +}; + +struct ElementData +{ + VertexElement_t element; + int uncompressed; // uncompressed vertex size + int currentCompressed; // current compressed vertex element size + int idealCompressed; // ideal future compressed vertex element size + const char *name; +}; + +class CounterData +{ +public: + CounterData() : m_memCount( 0 ), m_vertCount( 0 ), m_paddingCount( 0 ) + { + for ( int i = 0; i < VERTEX_ELEMENT_NUMELEMENTS; i++ ) + { + m_elementsCompressed[ i ] = 0; + m_elementsUncompressed[ i ] = 0; + } + m_AllocatorName[ 0 ] = 0; + } + + static const int MAX_NAME_SIZE = 128; + int m_memCount; + int m_vertCount; + int m_paddingCount; + int m_elementsCompressed[ VERTEX_ELEMENT_NUMELEMENTS ]; // Number of compressed verts using each element + int m_elementsUncompressed[ VERTEX_ELEMENT_NUMELEMENTS ]; // Number of uncompressed verts using each element + char m_AllocatorName[ MAX_NAME_SIZE ]; +}; + +class AllocData +{ +public: + AllocData( void * buffer, int bufferSize, VertexFormat_t fmt, int numVerts, int allocatorHash ) + : m_buffer( buffer ), m_bufferSize( bufferSize ), m_fmt( fmt ), m_numVerts( numVerts ), m_allocatorHash( allocatorHash ) {} + AllocData() : m_buffer( NULL ), m_bufferSize( 0 ), m_fmt( 0 ), m_numVerts( 0 ), m_allocatorHash( 0 ) {} + + VertexFormat_t m_fmt; + void * m_buffer; + int m_bufferSize; + int m_numVerts; + short m_allocatorHash; +}; + +typedef CUtlHashFixed < CounterData, 64 > CCounterTable; +typedef CUtlHashFixed < AllocData, 4096 > CAllocTable; +typedef CUtlStack < short > CAllocNameHashes; + +#endif // ENABLE_VB_ALLOC_TRACKER + + +class CVBAllocTracker : public IVBAllocTracker +{ +public: + virtual void CountVB( void * buffer, bool isDynamic, int bufferSize, int vertexSize, VertexFormat_t fmt ); + virtual void UnCountVB( void * buffer ); + virtual bool TrackMeshAllocations( const char * allocatorName ); + + void DumpVBAllocs(); + +#if ENABLE_VB_ALLOC_TRACKER + +public: + CVBAllocTracker() : m_bSuperSpew( false ) { m_MeshAllocatorName[0] = 0; } + +private: + + UtlHashFixedHandle_t TrackAlloc( void * buffer, int bufferSize, VertexFormat_t fmt, int numVerts, short allocatorHash ); + bool KillAlloc( void * buffer, int & bufferSize, VertexFormat_t & fmt, int & numVerts, short & allocatorHash ); + + UtlHashFixedHandle_t GetCounterHandle( const char * allocatorName, short allocatorHash ); + + void SpewElements( const char * allocatorName, short nameHash ); + int ComputeVertexSize( VertexElementMap_t map, VertexFormat_t fmt, bool compressed ); + VertexElementMap_t ComputeElementMap( VertexFormat_t fmt, int vertexSize, bool isDynamic ); + void UpdateElements( CounterData & data, VertexFormat_t fmt, int numVerts, int vertexSize, + bool isDynamic, bool isCompressed ); + + int ComputeAlignmentWastage( int bufferSize ); + void AddSaving( int & alreadySaved, int & yetToSave, const char *allocatorName, VertexElement_t element, Saving_t savingType ); + void SpewExpectedSavings( void ); + void UpdateData( const char * allocatorName, short allocatorKey, int bufferSize, VertexFormat_t fmt, + int numVerts, int vertexSize, bool isDynamic, bool isCompressed ); + + const char * GetNameString( int allocatorKey ); + void SpewData( const char * allocatorName, short nameHash = 0 ); + void SpewDataSometimes( int inc ); + + + static const int SPEW_RATE = 64; + static const int MAX_ALLOCATOR_NAME_SIZE = 128; + char m_MeshAllocatorName[ MAX_ALLOCATOR_NAME_SIZE ]; + bool m_bSuperSpew; + + CCounterTable m_VBCountTable; + CAllocTable m_VBAllocTable; + CAllocNameHashes m_VBTableNameHashes; + + // We use a mutex since allocation tracking is accessed from multiple loading threads. + // CThreadFastMutex is used as contention is expected to be low during loading. + CThreadFastMutex m_VBAllocMutex; +#endif // ENABLE_VB_ALLOC_TRACKER +}; + + +//----------------------------------------------------------------------------- +// +// Global data +// +//----------------------------------------------------------------------------- + +#if ENABLE_VB_ALLOC_TRACKER + +// FIXME: do this in a better way: +static const ElementData positionElement = { VERTEX_ELEMENT_POSITION, 12, 12, 8, "POSITION " }; // (UNDONE: need vertex shader to scale, may cause cracking w/ static props) +static const ElementData normalElement = { VERTEX_ELEMENT_NORMAL, 12, 4, 4, "NORMAL " }; // (UNDONE: PC (2x16-byte Ravi method) or 360 (D3DDECLTYPE_HEND3N)) +static const ElementData colorElement = { VERTEX_ELEMENT_COLOR, 4, 4, 4, "COLOR " }; // (already minimal) +static const ElementData specularElement = { VERTEX_ELEMENT_SPECULAR, 4, 4, 4, "SPECULAR " }; // (already minimal) +static const ElementData tangentSElement = { VERTEX_ELEMENT_TANGENT_S, 12, 12, 4, "TANGENT_S " }; // (all-but-unused) +static const ElementData tangentTElement = { VERTEX_ELEMENT_TANGENT_T, 12, 12, 4, "TANGENT_T " }; // (all-but-unused) +static const ElementData wrinkleElement = { VERTEX_ELEMENT_WRINKLE, 4, 4, 0, "WRINKLE " }; // (UNDONE: compress it as a SHORTN in Position.w - is it [0,1]?) +static const ElementData boneIndexElement = { VERTEX_ELEMENT_BONEINDEX, 4, 4, 4, "BONEINDEX " }; // (already minimal) +static const ElementData boneWeight1Element = { VERTEX_ELEMENT_BONEWEIGHTS1, 4, 4, 4, "BONEWEIGHT1 " }; // (unused) +static const ElementData boneWeight2Element = { VERTEX_ELEMENT_BONEWEIGHTS2, 8, 8, 4, "BONEWEIGHT2 " }; // (UNDONE: take care w.r.t cracking in flex regions) +static const ElementData boneWeight3Element = { VERTEX_ELEMENT_BONEWEIGHTS3, 12, 12, 8, "BONEWEIGHT3 " }; // (unused) +static const ElementData boneWeight4Element = { VERTEX_ELEMENT_BONEWEIGHTS4, 16, 16, 8, "BONEWEIGHT4 " }; // (unused) +static const ElementData userData1Element = { VERTEX_ELEMENT_USERDATA1, 4, 4, 4, "USERDATA1 " }; // (unused) +static const ElementData userData2Element = { VERTEX_ELEMENT_USERDATA2, 8, 8, 4, "USERDATA2 " }; // (unused) +static const ElementData userData3Element = { VERTEX_ELEMENT_USERDATA3, 12, 12, 4, "USERDATA3 " }; // (unused) +#if ( COMPRESSED_NORMALS_TYPE == COMPRESSED_NORMALS_SEPARATETANGENTS_SHORT2 ) +static const ElementData userData4Element = { VERTEX_ELEMENT_USERDATA4, 16, 4, 4, "USERDATA4 " }; // (UNDONE: PC (2x16-byte Ravi method) or 360 (D3DDECLTYPE_HEND3N)) +#else // ( COMPRESSED_NORMALS_TYPE == COMPRESSED_NORMALS_COMBINEDTANGENTS_UBYTE4 ) +static const ElementData userData4Element = { VERTEX_ELEMENT_USERDATA4, 16, 0, 0, "USERDATA4 " }; // (UNDONE: PC (2x16-byte Ravi method) or 360 (D3DDECLTYPE_HEND3N)) +#endif +static const ElementData texCoord1D0Element = { VERTEX_ELEMENT_TEXCOORD1D_0, 4, 4, 4, "TEXCOORD1D_0" }; // (not worth compressing) +static const ElementData texCoord1D1Element = { VERTEX_ELEMENT_TEXCOORD1D_1, 4, 4, 4, "TEXCOORD1D_1" }; // (not worth compressing) +static const ElementData texCoord1D2Element = { VERTEX_ELEMENT_TEXCOORD1D_2, 4, 4, 4, "TEXCOORD1D_2" }; // (not worth compressing) +static const ElementData texCoord1D3Element = { VERTEX_ELEMENT_TEXCOORD1D_3, 4, 4, 4, "TEXCOORD1D_3" }; // (not worth compressing) +static const ElementData texCoord1D4Element = { VERTEX_ELEMENT_TEXCOORD1D_4, 4, 4, 4, "TEXCOORD1D_4" }; // (not worth compressing) +static const ElementData texCoord1D5Element = { VERTEX_ELEMENT_TEXCOORD1D_5, 4, 4, 4, "TEXCOORD1D_5" }; // (not worth compressing) +static const ElementData texCoord1D6Element = { VERTEX_ELEMENT_TEXCOORD1D_6, 4, 4, 4, "TEXCOORD1D_6" }; // (not worth compressing) +static const ElementData texCoord1D7Element = { VERTEX_ELEMENT_TEXCOORD1D_7, 4, 4, 4, "TEXCOORD1D_7" }; // (not worth compressing) +static const ElementData texCoord2D0Element = { VERTEX_ELEMENT_TEXCOORD2D_0, 8, 8, 4, "TEXCOORD2D_0" }; // (UNDONE: need vertex shader to take scale, account for clamping) +static const ElementData texCoord2D1Element = { VERTEX_ELEMENT_TEXCOORD2D_1, 8, 8, 4, "TEXCOORD2D_1" }; // (UNDONE: need vertex shader to take scale, account for clamping) +static const ElementData texCoord2D2Element = { VERTEX_ELEMENT_TEXCOORD2D_2, 8, 8, 4, "TEXCOORD2D_2" }; // (all-but-unused) +static const ElementData texCoord2D3Element = { VERTEX_ELEMENT_TEXCOORD2D_3, 8, 8, 4, "TEXCOORD2D_3" }; // (unused) +static const ElementData texCoord2D4Element = { VERTEX_ELEMENT_TEXCOORD2D_4, 8, 8, 4, "TEXCOORD2D_4" }; // (unused) +static const ElementData texCoord2D5Element = { VERTEX_ELEMENT_TEXCOORD2D_5, 8, 8, 4, "TEXCOORD2D_5" }; // (unused) +static const ElementData texCoord2D6Element = { VERTEX_ELEMENT_TEXCOORD2D_6, 8, 8, 4, "TEXCOORD2D_6" }; // (unused) +static const ElementData texCoord2D7Element = { VERTEX_ELEMENT_TEXCOORD2D_7, 8, 8, 4, "TEXCOORD2D_7" }; // (unused) +static const ElementData texCoord3D0Element = { VERTEX_ELEMENT_TEXCOORD3D_0, 12, 12, 8, "TEXCOORD3D_0" }; // FIXME: used how much? (UNDONE: need vertex shader to take scale, account for clamping) +static const ElementData texCoord3D1Element = { VERTEX_ELEMENT_TEXCOORD3D_1, 12, 12, 8, "TEXCOORD3D_1" }; // FIXME: used how much? (UNDONE: need vertex shader to take scale, account for clamping) +static const ElementData texCoord3D2Element = { VERTEX_ELEMENT_TEXCOORD3D_2, 12, 12, 8, "TEXCOORD3D_2" }; // FIXME: used how much? (UNDONE: need vertex shader to take scale, account for clamping) +static const ElementData texCoord3D3Element = { VERTEX_ELEMENT_TEXCOORD3D_3, 12, 12, 8, "TEXCOORD3D_3" }; // FIXME: used how much? (UNDONE: need vertex shader to take scale, account for clamping) +static const ElementData texCoord3D4Element = { VERTEX_ELEMENT_TEXCOORD3D_4, 12, 12, 8, "TEXCOORD3D_4" }; // FIXME: used how much? (UNDONE: need vertex shader to take scale, account for clamping) +static const ElementData texCoord3D5Element = { VERTEX_ELEMENT_TEXCOORD3D_5, 12, 12, 8, "TEXCOORD3D_5" }; // FIXME: used how much? (UNDONE: need vertex shader to take scale, account for clamping) +static const ElementData texCoord3D6Element = { VERTEX_ELEMENT_TEXCOORD3D_6, 12, 12, 8, "TEXCOORD3D_6" }; // FIXME: used how much? (UNDONE: need vertex shader to take scale, account for clamping) +static const ElementData texCoord3D7Element = { VERTEX_ELEMENT_TEXCOORD3D_7, 12, 12, 8, "TEXCOORD3D_7" }; // FIXME: used how much? (UNDONE: need vertex shader to take scale, account for clamping) +static const ElementData texCoord4D0Element = { VERTEX_ELEMENT_TEXCOORD4D_0, 16, 16, 8, "TEXCOORD4D_0" }; // FIXME: used how much? (UNDONE: need vertex shader to take scale, account for clamping) +static const ElementData texCoord4D1Element = { VERTEX_ELEMENT_TEXCOORD4D_1, 16, 16, 8, "TEXCOORD4D_1" }; // FIXME: used how much? (UNDONE: need vertex shader to take scale, account for clamping) +static const ElementData texCoord4D2Element = { VERTEX_ELEMENT_TEXCOORD4D_2, 16, 16, 8, "TEXCOORD4D_2" }; // FIXME: used how much? (UNDONE: need vertex shader to take scale, account for clamping) +static const ElementData texCoord4D3Element = { VERTEX_ELEMENT_TEXCOORD4D_3, 16, 16, 8, "TEXCOORD4D_3" }; // FIXME: used how much? (UNDONE: need vertex shader to take scale, account for clamping) +static const ElementData texCoord4D4Element = { VERTEX_ELEMENT_TEXCOORD4D_4, 16, 16, 8, "TEXCOORD4D_4" }; // FIXME: used how much? (UNDONE: need vertex shader to take scale, account for clamping) +static const ElementData texCoord4D5Element = { VERTEX_ELEMENT_TEXCOORD4D_5, 16, 16, 8, "TEXCOORD4D_5" }; // FIXME: used how much? (UNDONE: need vertex shader to take scale, account for clamping) +static const ElementData texCoord4D6Element = { VERTEX_ELEMENT_TEXCOORD4D_6, 16, 16, 8, "TEXCOORD4D_6" }; // FIXME: used how much? (UNDONE: need vertex shader to take scale, account for clamping) +static const ElementData texCoord4D7Element = { VERTEX_ELEMENT_TEXCOORD4D_7, 16, 16, 8, "TEXCOORD4D_7" }; // FIXME: used how much? (UNDONE: need vertex shader to take scale, account for clamping) +static const ElementData elementTable[ VERTEX_ELEMENT_NUMELEMENTS ] = { positionElement, + normalElement, + colorElement, + specularElement, + tangentSElement, + tangentTElement, + wrinkleElement, + boneIndexElement, + boneWeight1Element, boneWeight2Element, boneWeight3Element, boneWeight4Element, + userData1Element, userData2Element, userData3Element, userData4Element, + texCoord1D0Element, texCoord1D1Element, texCoord1D2Element, texCoord1D3Element, texCoord1D4Element, texCoord1D5Element, texCoord1D6Element, texCoord1D7Element, + texCoord2D0Element, texCoord2D1Element, texCoord2D2Element, texCoord2D3Element, texCoord2D4Element, texCoord2D5Element, texCoord2D6Element, texCoord2D7Element, + texCoord3D0Element, texCoord3D1Element, texCoord3D2Element, texCoord3D3Element, texCoord3D4Element, texCoord3D5Element, texCoord3D6Element, texCoord3D7Element, + texCoord4D0Element, texCoord4D1Element, texCoord4D2Element, texCoord4D3Element, texCoord4D4Element, texCoord4D5Element, texCoord4D6Element, texCoord4D7Element, + }; + +static ConVar mem_vballocspew( "mem_vballocspew", "0", FCVAR_CHEAT, "How often to spew vertex buffer allocation stats - 1: every alloc, 2+: every 2+ allocs, 0: off" ); + +#endif // ENABLE_VB_ALLOC_TRACKER + +//----------------------------------------------------------------------------- +// Singleton instance exposed to the engine +//----------------------------------------------------------------------------- + +CVBAllocTracker g_VBAllocTrackerShaderAPI; +EXPOSE_SINGLE_INTERFACE_GLOBALVAR( CVBAllocTracker, IVBAllocTracker, + VB_ALLOC_TRACKER_INTERFACE_VERSION, g_VBAllocTrackerShaderAPI ); + +//----------------------------------------------------------------------------- +// +// VB alloc-tracking code starts here +// +//----------------------------------------------------------------------------- + +#if ENABLE_VB_ALLOC_TRACKER + +UtlHashFixedHandle_t CVBAllocTracker::TrackAlloc( void * buffer, int bufferSize, VertexFormat_t fmt, int numVerts, short allocatorHash ) +{ + AllocData newData( buffer, bufferSize, fmt, numVerts, allocatorHash ); + UtlHashFixedHandle_t handle = m_VBAllocTable.Insert( (int)buffer, newData ); + if ( handle == m_VBAllocTable.InvalidHandle() ) + { + Warning( "[VBMEM] VBMemAllocTable hash collision (grow table).\n" ); + } + return handle; +} + +bool CVBAllocTracker::KillAlloc( void * buffer, int & bufferSize, VertexFormat_t & fmt, int & numVerts, short & allocatorHash ) +{ + UtlHashFixedHandle_t handle = m_VBAllocTable.Find( (int)buffer ); + if ( handle != m_VBAllocTable.InvalidHandle() ) + { + AllocData & data = m_VBAllocTable.Element( handle ); + bufferSize = data.m_bufferSize; + fmt = data.m_fmt; + numVerts = data.m_numVerts; + allocatorHash = data.m_allocatorHash; + m_VBAllocTable.Remove( handle ); + return true; + } + Warning( "[VBMEM] VBMemAllocTable failed to find alloc entry...\n" ); + return false; +} + +UtlHashFixedHandle_t CVBAllocTracker::GetCounterHandle( const char * allocatorName, short allocatorHash ) +{ + UtlHashFixedHandle_t handle = m_VBCountTable.Find( allocatorHash ); + if ( handle == m_VBCountTable.InvalidHandle() ) + { + CounterData newData; + Assert( ( allocatorName != NULL ) && ( allocatorName[0] != 0 ) ); + V_strncpy( newData.m_AllocatorName, allocatorName, CounterData::MAX_NAME_SIZE ); + handle = m_VBCountTable.Insert( allocatorHash, newData ); + m_VBTableNameHashes.Push( allocatorHash ); + } + if ( handle == m_VBCountTable.InvalidHandle() ) + { + Warning( "[VBMEM] CounterData hash collision (grow table).\n" ); + } + return handle; +} + +void CheckForElementTableUpdates( const ElementData & element ) +{ + // Ensure that 'elementTable' gets updated if VertexElement_t ever changes: + int tableIndex = &element - &( elementTable[0] ); + Assert( tableIndex == element.element ); + if ( tableIndex != element.element ) + { + static int timesToSpew = 20; + if ( timesToSpew > 0 ) + { + Warning( "VertexElement_t structure has changed, ElementData table in cvballoctracker needs updating!\n" ); + timesToSpew--; + } + } +} + +void CVBAllocTracker::SpewElements( const char * allocatorName, short nameHash ) +{ + short allocatorHash = allocatorName ? HashString( allocatorName ) : nameHash; + UtlHashFixedHandle_t handle = GetCounterHandle( allocatorName, allocatorHash ); + if ( handle != m_VBCountTable.InvalidHandle() ) + { + CounterData & data = m_VBCountTable.Element( handle ); + int originalSum = 0, currentSum = 0, idealSum = 0; + for (int i = 0;i < VERTEX_ELEMENT_NUMELEMENTS;i++) + { + CheckForElementTableUpdates( elementTable[ i ] ); + int numCompressed = data.m_elementsCompressed[ i ]; + int numUncompressed = data.m_elementsUncompressed[ i ]; + int numVerts = numCompressed + numUncompressed; + originalSum += numVerts*elementTable[ i ].uncompressed; + currentSum += numCompressed*elementTable[ i ].currentCompressed + numUncompressed*elementTable[ i ].uncompressed; + idealSum += numVerts*elementTable[ i ].idealCompressed; + } + + if ( originalSum > 0 ) + { + Msg( "[VBMEM] ----elements (%s)----:\n", data.m_AllocatorName); + for (int i = 0;i < VERTEX_ELEMENT_NUMELEMENTS;i++) + { + // We count vertices (converted to bytes via elementTable) + int numCompressed = data.m_elementsCompressed[ i ]; + int numUncompressed = data.m_elementsUncompressed[ i ]; + int numVerts = numCompressed + numUncompressed; + const ElementData & elementData = elementTable[ i ]; + if ( numVerts > 0 ) + { + Msg( " element: %5.2f MB 'U', %5.2f MB 'C', %5.2f MB 'I', %6.2f MB 'D', %s\n", + numVerts*elementData.uncompressed / ( 1024.0f*1024.0f ), + ( numCompressed*elementData.currentCompressed + numUncompressed*elementData.uncompressed ) / ( 1024.0f*1024.0f ), + numVerts*elementData.idealCompressed / ( 1024.0f*1024.0f ), + -( numCompressed*elementData.currentCompressed + numUncompressed*elementData.uncompressed - numVerts*elementData.idealCompressed ) / ( 1024.0f*1024.0f ), + elementData.name ); + } + } + Msg( "[VBMEM] total: %5.2f MB 'U', %5.2f MB 'C', %5.2f MB 'I', %6.2f MB 'D'\n", + originalSum / ( 1024.0f*1024.0f ), + currentSum / ( 1024.0f*1024.0f ), + idealSum / ( 1024.0f*1024.0f ), + -( currentSum - idealSum ) / ( 1024.0f*1024.0f ) ); + Msg( "[VBMEM] ----elements (%s)----:\n", data.m_AllocatorName); + } + } +} + +int CVBAllocTracker::ComputeVertexSize( VertexElementMap_t map, VertexFormat_t fmt, bool compressed ) +{ + int vertexSize = 0; + for ( int i = 0;i < VERTEX_ELEMENT_NUMELEMENTS;i++ ) + { + const ElementData & element = elementTable[ i ]; + CheckForElementTableUpdates( element ); + VertexElementMap_t LSB = 1; + if ( map & ( LSB << i ) ) + { + vertexSize += compressed ? element.currentCompressed : element.uncompressed; + } + } + + // On PC (see CVertexBufferBase::ComputeVertexDescription() in meshbase.cpp) + // vertex strides are aligned to 16 bytes: + bool bCacheAlign = ( fmt & VERTEX_FORMAT_USE_EXACT_FORMAT ) == 0; + if ( bCacheAlign && ( vertexSize > 16 ) && IsPC() ) + { + vertexSize = (vertexSize + 0xF) & (~0xF); + } + + return vertexSize; +} + +VertexElementMap_t CVBAllocTracker::ComputeElementMap( VertexFormat_t fmt, int vertexSize, bool isDynamic ) +{ + VertexElementMap_t map = 0, LSB = 1; + if ( fmt & VERTEX_POSITION ) map |= LSB << VERTEX_ELEMENT_POSITION; + if ( fmt & VERTEX_NORMAL ) map |= LSB << VERTEX_ELEMENT_NORMAL; + if ( fmt & VERTEX_COLOR ) map |= LSB << VERTEX_ELEMENT_COLOR; + if ( fmt & VERTEX_SPECULAR ) map |= LSB << VERTEX_ELEMENT_SPECULAR; + if ( fmt & VERTEX_TANGENT_S ) map |= LSB << VERTEX_ELEMENT_TANGENT_S; + if ( fmt & VERTEX_TANGENT_T ) map |= LSB << VERTEX_ELEMENT_TANGENT_T; + if ( fmt & VERTEX_WRINKLE ) map |= LSB << VERTEX_ELEMENT_WRINKLE; + if ( fmt & VERTEX_BONE_INDEX) map |= LSB << VERTEX_ELEMENT_BONEINDEX; + int numBones = NumBoneWeights( fmt ); + if ( numBones > 0 ) map |= LSB << ( VERTEX_ELEMENT_BONEWEIGHTS1 + numBones - 1 ); + int userDataSize = UserDataSize( fmt ); + if ( userDataSize > 0 ) map |= LSB << ( VERTEX_ELEMENT_USERDATA1 + userDataSize - 1 ); + for ( int i = 0; i < VERTEX_MAX_TEXTURE_COORDINATES; ++i ) + { + VertexElement_t texCoordElements[4] = { VERTEX_ELEMENT_TEXCOORD1D_0, VERTEX_ELEMENT_TEXCOORD2D_0, VERTEX_ELEMENT_TEXCOORD3D_0, VERTEX_ELEMENT_TEXCOORD4D_0 }; + int nCoordSize = TexCoordSize( i, fmt ); + if ( nCoordSize > 0 ) + { + Assert( i < 4 ); + if ( i < 4 ) + { + map |= LSB << ( texCoordElements[ nCoordSize - 1 ] + i ); + } + } + } + + if ( map == 0 ) + { + if ( !isDynamic ) + { + // We expect all (non-dynamic) VB allocs to specify a vertex format + // Warning("[VBMEM] unknown vertex format\n"); + return 0; + } + } + else + { + if ( vertexSize != 0 ) + { + // Make sure elementTable above matches external computations of vertex size + // FIXME: make this assert dependent on whether the current VB is compressed or not + VertexCompressionType_t compressionType = CompressionType( fmt ); + bool isCompressedAlloc = ( compressionType == VERTEX_COMPRESSION_ON ); + // FIXME: once we've finalised which elements we're compressing for ship, update + // elementTable to reflect that and re-enable this assert for compressed verts + if ( !isCompressedAlloc ) + { + Assert( vertexSize == ComputeVertexSize( map, fmt, isCompressedAlloc ) ); + } + } + } + + return map; +} + +void CVBAllocTracker::UpdateElements( CounterData & data, VertexFormat_t fmt, int numVerts, int vertexSize, + bool isDynamic, bool isCompressed ) +{ + VertexElementMap_t map = ComputeElementMap( fmt, vertexSize, isDynamic ); + if ( map != 0 ) + { + for (int i = 0;i < VERTEX_ELEMENT_NUMELEMENTS;i++) + { + // Count vertices (get bytes from our elements table) + VertexElementMap_t LSB = 1; + if ( map & ( LSB << i ) ) + { + if ( isCompressed ) + data.m_elementsCompressed[ i ] += numVerts; + else + data.m_elementsUncompressed[ i ] += numVerts; + } + } + } +} + +int CVBAllocTracker::ComputeAlignmentWastage( int bufferSize ) +{ + if ( !IsX360() ) + return 0; + + // VBs are 4KB-aligned on 360, so we waste thiiiiiis much: + return ( ( 4096 - (bufferSize & 4095)) & 4095 ); +} + +void CVBAllocTracker::AddSaving( int & alreadySaved, int & yetToSave, const char *allocatorName, VertexElement_t element, Saving_t savingType ) +{ + UtlHashFixedHandle_t handle = GetCounterHandle( allocatorName, HashString( allocatorName ) ); + if ( handle != m_VBCountTable.InvalidHandle() ) + { + CheckForElementTableUpdates( elementTable[ element ] ); + CounterData & counterData = m_VBCountTable.Element( handle ); + const ElementData & elementData = elementTable[ element ]; + int numVerts = counterData.m_vertCount; + int numCompressed = counterData.m_elementsCompressed[ element ]; + int numUncompressed = counterData.m_elementsUncompressed[ element ]; + switch( savingType ) + { + case SAVING_COMPRESSION: + alreadySaved += numCompressed*( elementData.uncompressed - elementData.currentCompressed ); + yetToSave += numUncompressed*( elementData.uncompressed - elementData.currentCompressed ); + break; + case SAVING_REMOVAL: + alreadySaved += elementData.uncompressed*( numVerts - ( numUncompressed + numCompressed ) ); + yetToSave += numUncompressed*elementData.uncompressed + numCompressed*elementData.uncompressed; + break; + case SAVING_ALIGNMENT: + yetToSave += counterData.m_paddingCount; + break; + default: + Assert(0); + break; + } + } +} + +void CVBAllocTracker::SpewExpectedSavings( void ) +{ + int alreadySaved = 0, yetToSave = 0; + + // We have removed bone weights+indices from static props + AddSaving( alreadySaved, yetToSave, "R_StudioCreateStaticMeshes (prop_static)", VERTEX_ELEMENT_BONEWEIGHTS2, SAVING_REMOVAL ); + AddSaving( alreadySaved, yetToSave, "R_StudioCreateStaticMeshes (prop_static)", VERTEX_ELEMENT_BONEINDEX, SAVING_REMOVAL ); + // We have removed vertex colors from all models (color should only ever be in stream1, for static vertex lighting) + AddSaving( alreadySaved, yetToSave, "R_StudioCreateStaticMeshes (prop_dynamic)", VERTEX_ELEMENT_COLOR, SAVING_REMOVAL ); + AddSaving( alreadySaved, yetToSave, "R_StudioCreateStaticMeshes (prop_static)", VERTEX_ELEMENT_COLOR, SAVING_REMOVAL ); + AddSaving( alreadySaved, yetToSave, "R_StudioCreateStaticMeshes (character)", VERTEX_ELEMENT_COLOR, SAVING_REMOVAL ); + + // We expect to compress texcoords (DONE: normals+tangents, boneweights) for all studiomdls + AddSaving( alreadySaved, yetToSave, "R_StudioCreateStaticMeshes (prop_dynamic)", VERTEX_ELEMENT_NORMAL, SAVING_COMPRESSION ); + AddSaving( alreadySaved, yetToSave, "R_StudioCreateStaticMeshes (prop_static)", VERTEX_ELEMENT_NORMAL, SAVING_COMPRESSION ); + AddSaving( alreadySaved, yetToSave, "R_StudioCreateStaticMeshes (character)", VERTEX_ELEMENT_NORMAL, SAVING_COMPRESSION ); + AddSaving( alreadySaved, yetToSave, "R_StudioCreateStaticMeshes (prop_dynamic)", VERTEX_ELEMENT_USERDATA4, SAVING_COMPRESSION ); + AddSaving( alreadySaved, yetToSave, "R_StudioCreateStaticMeshes (prop_static)", VERTEX_ELEMENT_USERDATA4, SAVING_COMPRESSION ); + AddSaving( alreadySaved, yetToSave, "R_StudioCreateStaticMeshes (character)", VERTEX_ELEMENT_USERDATA4, SAVING_COMPRESSION ); + AddSaving( alreadySaved, yetToSave, "R_StudioCreateStaticMeshes (prop_dynamic)", VERTEX_ELEMENT_TEXCOORD2D_0, SAVING_COMPRESSION ); + AddSaving( alreadySaved, yetToSave, "R_StudioCreateStaticMeshes (prop_static)", VERTEX_ELEMENT_TEXCOORD2D_0, SAVING_COMPRESSION ); + AddSaving( alreadySaved, yetToSave, "R_StudioCreateStaticMeshes (character)", VERTEX_ELEMENT_TEXCOORD2D_0, SAVING_COMPRESSION ); + AddSaving( alreadySaved, yetToSave, "R_StudioCreateStaticMeshes (character)", VERTEX_ELEMENT_BONEWEIGHTS1, SAVING_COMPRESSION ); + AddSaving( alreadySaved, yetToSave, "R_StudioCreateStaticMeshes (character)", VERTEX_ELEMENT_BONEWEIGHTS2, SAVING_COMPRESSION ); + AddSaving( alreadySaved, yetToSave, "R_StudioCreateStaticMeshes (prop_dynamic)", VERTEX_ELEMENT_BONEWEIGHTS1, SAVING_COMPRESSION ); + AddSaving( alreadySaved, yetToSave, "R_StudioCreateStaticMeshes (prop_dynamic)", VERTEX_ELEMENT_BONEWEIGHTS2, SAVING_COMPRESSION ); + + // UNDONE: compress bone weights for studiomdls? (issue: possible flex artifacts, but 2xSHORTN probably ok) + // UNDONE: compress positions (+wrinkle) for studiomdls? (issue: possible flex artifacts) + // UNDONE: disable tangents for non-bumped models (issue: forcedmaterialoverride support... don't think that needs tangents, though + // however, if we use UBYTE4 normal+tangent encoding, removing tangents saves nothing) + + if ( IsX360() ) + { + // We expect to avoid 4-KB-alignment wastage for color meshes, by allocating them + // out of a single, shared VB and adding per-mesh offsets in vertex shaders + AddSaving( alreadySaved, yetToSave, "CColorMeshData::CreateResource", VERTEX_ELEMENT_USERDATA4, SAVING_ALIGNMENT ); + } + + Msg("[VBMEM]\n"); + Msg("[VBMEM] Total expected memory saving by disabling/compressing vertex elements: %6.2f MB\n", yetToSave / ( 1024.0f*1024.0f ) ); + Msg("[VBMEM] ( total memory already saved: %6.2f MB)\n", alreadySaved / ( 1024.0f*1024.0f ) ); + Msg("[VBMEM] - compression of model texcoords, [DONE: normals+tangents, bone weights]\n" ); + Msg("[VBMEM] - avoidance of 4-KB alignment wastage for color meshes (on 360)\n" ); + Msg("[VBMEM] - [DONE: removal of unneeded bone weights+indices on models]\n" ); + Msg("[VBMEM]\n"); +} + +void CVBAllocTracker::UpdateData( const char * allocatorName, short allocatorKey, int bufferSize, VertexFormat_t fmt, + int numVerts, int vertexSize, bool isDynamic, bool isCompressed ) +{ + UtlHashFixedHandle_t handle = GetCounterHandle( allocatorName, allocatorKey ); + if ( handle != m_VBCountTable.InvalidHandle() ) + { + CounterData & data = m_VBCountTable.Element( handle ); + data.m_memCount += bufferSize; + Assert( data.m_memCount >= 0 ); + data.m_vertCount += numVerts; + Assert( data.m_vertCount >= 0 ); + data.m_paddingCount += ( bufferSize < 0 ? -1 : +1 )*ComputeAlignmentWastage( abs( bufferSize ) ); + UpdateElements( data, fmt, numVerts, vertexSize, isDynamic, isCompressed ); + } +} + +const char * CVBAllocTracker::GetNameString( int allocatorKey ) +{ + UtlHashFixedHandle_t handle = GetCounterHandle( NULL, allocatorKey ); + if ( handle != m_VBCountTable.InvalidHandle() ) + { + CounterData & data = m_VBCountTable.Element( handle ); + return data.m_AllocatorName; + } + return "null"; +} + +void CVBAllocTracker::SpewData( const char * allocatorName, short nameHash ) +{ + short allocatorHash = allocatorName ? HashString( allocatorName ) : nameHash; + UtlHashFixedHandle_t handle = GetCounterHandle( allocatorName, allocatorHash ); + if ( handle != m_VBCountTable.InvalidHandle() ) + { + CounterData & data = m_VBCountTable.Element( handle ); + if ( data.m_memCount > 0 ) + { + Msg("[VBMEM] running mem usage: (%5.2f M-verts) %6.2f MB | '%s'\n", + data.m_vertCount / ( 1024.0f*1024.0f ), + data.m_memCount / ( 1024.0f*1024.0f ), + data.m_AllocatorName ); + } + if ( data.m_paddingCount > 0 ) + { + Msg("[VBMEM] 4KB VB alignment wastage: %6.2f MB | '%s'\n", + data.m_paddingCount / ( 1024.0f*1024.0f ), + data.m_AllocatorName ); + } + } +} + +void CVBAllocTracker::SpewDataSometimes( int inc ) +{ + static int count = 0; + + if ( inc < 0 ) count += inc; + Assert( count >= 0 ); + + int period = mem_vballocspew.GetInt(); + if ( period >= 1 ) + { + if ( ( count % period ) == 0 ) + { + Msg( "[VBMEM] Status after %d VB allocs:\n", count ); + //#define ROUND_UP( _x_ ) ( ( ( _x_ ) + 31 ) & 31 ) + //Msg( "[VBMEM] Conservative estimate of mem used to track allocs: %d\n", 4096*ROUND_UP( 4 + sizeof( CUtlPtrLinkedList ) ) + count*ROUND_UP( sizeof( AllocData ) + 8 ) ); + SpewData( "total_static" ); + SpewData( "unknown" ); + } + } + + if ( inc > 0 ) count += inc; +} + +void CVBAllocTracker::DumpVBAllocs() +{ + m_VBAllocMutex.Lock(); + + Msg("[VBMEM] ----running totals----\n" ); + for ( int i = ( m_VBTableNameHashes.Count() - 1 ); i >= 0; i-- ) + { + short nameHash = m_VBTableNameHashes.Element( i ); + SpewElements( NULL, nameHash ); + } + + Msg("[VBMEM]\n"); + Msg("[VBMEM] 'U' - original memory usage (all vertices uncompressed)\n" ); + Msg("[VBMEM] 'C' - current memory usage (some compression)\n" ); + Msg("[VBMEM] 'I' - ideal memory usage (all verts maximally compressed)\n" ); + Msg("[VBMEM] 'D' - difference between C and I (-> how much more compression could save)\n" ); + Msg("[VBMEM] 'W' - memory wasted due to 4-KB vertex buffer alignment\n" ); + Msg("[VBMEM]\n"); + for ( int i = ( m_VBTableNameHashes.Count() - 1 ); i >= 0; i-- ) + { + short nameHash = m_VBTableNameHashes.Element( i ); + SpewData( NULL, nameHash ); + } + SpewExpectedSavings(); + Msg("[VBMEM] ----running totals----\n" ); + + m_VBAllocMutex.Unlock(); +} + +#endif // ENABLE_VB_ALLOC_TRACKER + +void CVBAllocTracker::CountVB( void * buffer, bool isDynamic, int bufferSize, int vertexSize, VertexFormat_t fmt ) +{ +#if ENABLE_VB_ALLOC_TRACKER + m_VBAllocMutex.Lock(); + + // Update VB memory counts for the relevant allocation type + // (NOTE: we have 'unknown', 'dynamic' and 'total' counts) + const char * allocatorName = ( m_MeshAllocatorName[0] == 0 ) ? "unknown" : m_MeshAllocatorName; + if ( isDynamic ) allocatorName = "total_dynamic"; + int numVerts = ( vertexSize > 0 ) ? ( bufferSize / vertexSize ) : 0; + short totalStaticKey = HashString( "total_static" ); + short key = HashString( allocatorName ); + bool isCompressed = ( VERTEX_COMPRESSION_NONE != CompressionType( fmt ) ); + + if ( m_MeshAllocatorName[0] == 0 ) + { + Warning("[VBMEM] unknown allocation!\n"); + } + + // Add to the VB memory counters + TrackAlloc( buffer, bufferSize, fmt, numVerts, key ); + if ( !isDynamic ) + { + // Keep dynamic allocs out of the total (dynamic VBs don't get compressed) + UpdateData( "total_static", totalStaticKey, bufferSize, fmt, numVerts, vertexSize, isDynamic, isCompressed ); + } + UpdateData( allocatorName, key, bufferSize, fmt, numVerts, vertexSize, isDynamic, isCompressed ); + + if ( m_bSuperSpew ) + { + // Spew every alloc + Msg( "[VBMEM] VB-alloc | %6.2f MB | %s | %s\n", bufferSize / ( 1024.0f*1024.0f ), ( isDynamic ? "DYNamic" : " STAtic" ), allocatorName ); + SpewData( allocatorName ); + } + SpewDataSometimes( +1 ); + + m_VBAllocMutex.Unlock(); +#endif // ENABLE_VB_ALLOC_TRACKER +} + +void CVBAllocTracker::UnCountVB( void * buffer ) +{ +#if ENABLE_VB_ALLOC_TRACKER + m_VBAllocMutex.Lock(); + + short totalKey = HashString( "total_static" ); + short dynamicKey = HashString( "total_dynamic" ); + int bufferSize; + VertexFormat_t fmt; + int numVerts; + short key; + + // We have to store allocation data because the caller often doesn't know what it alloc'd :o/ + if ( KillAlloc( buffer, bufferSize, fmt, numVerts, key ) ) + { + bool isCompressed = ( VERTEX_COMPRESSION_NONE != CompressionType( fmt ) ); + bool isDynamic = ( key == dynamicKey ); + + // Subtract from the VB memory counters + if ( !isDynamic ) + { + UpdateData( NULL, totalKey, -bufferSize, fmt, -numVerts, 0, isDynamic, isCompressed ); + } + UpdateData( NULL, key, -bufferSize, fmt, -numVerts, 0, isDynamic, isCompressed ); + + const char * nameString = GetNameString( key ); + + if ( m_bSuperSpew ) + { + Msg( "[VBMEM] VB-free | %6.2f MB | %s | %s\n", bufferSize / ( 1024.0f*1024.0f ), ( isDynamic ? "DYNamic" : " STAtic" ), nameString ); + SpewData( nameString ); + } + SpewDataSometimes( -1 ); + } + + m_VBAllocMutex.Unlock(); +#endif // ENABLE_VB_ALLOC_TRACKER +} + +bool CVBAllocTracker::TrackMeshAllocations( const char * allocatorName ) +{ +#if ENABLE_VB_ALLOC_TRACKER + // Tracks mesh allocations by name (set this before an alloc, clear it after) + + if ( m_MeshAllocatorName[ 0 ] ) + { + return true; + } + + m_VBAllocMutex.Lock(); + + if ( allocatorName ) + { + Assert( m_MeshAllocatorName[0] == 0 ); + V_strncpy( m_MeshAllocatorName, allocatorName, MAX_ALLOCATOR_NAME_SIZE ); + } + else + { + m_MeshAllocatorName[0] = 0; + } + + m_VBAllocMutex.Unlock(); +#endif // ENABLE_VB_ALLOC_TRACKER + + return false; +} + +#ifndef RETAIL + +static void CC_DumpVBMemAllocs() +{ +#if ( ENABLE_VB_ALLOC_TRACKER == 0 ) + Warning( "ENABLE_VB_ALLOC_TRACKER must be 1 to enable VB mem alloc tracking\n"); +#else + g_VBAllocTrackerShaderAPI.DumpVBAllocs(); +#endif +} + +static ConCommand mem_dumpvballocs( "mem_dumpvballocs", CC_DumpVBMemAllocs, "Dump VB memory allocation stats.", FCVAR_CHEAT ); + +#endif // RETAIL diff --git a/materialsystem/shaderapidx9/d3d_async.cpp b/materialsystem/shaderapidx9/d3d_async.cpp new file mode 100644 index 0000000..6c9cf8b --- /dev/null +++ b/materialsystem/shaderapidx9/d3d_async.cpp @@ -0,0 +1,825 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// methods for muti-core dx9 threading +//===========================================================================// + +#ifdef D3D_ASYNC_SUPPORTED + +#include "glmgr/dxabstract.h" +#include "utlsymbol.h" +#include "utlvector.h" +#include "utldict.h" +#include "utlbuffer.h" +#include "UtlStringMap.h" +#include "locald3dtypes.h" +#include "shaderapidx8_global.h" +#include "recording.h" +#include "tier0/vprof.h" +#include "materialsystem/imaterialsystem.h" +#include "materialsystem/imaterialsystemhardwareconfig.h" +#include "shaderapidx8.h" +#include "materialsystem/IShader.h" +#include "utllinkedlist.h" +#include "IShaderSystem.h" +#include "tier0/fasttimer.h" +#include +#include "convar.h" +#include "materialsystem/shader_vcs_version.h" +#include "datacache/idatacache.h" +#include "winutils.h" + +#include "tier0/memdbgon.h" + +#if SHADERAPI_USE_SMP + +// Set this to 1 to get vprof nodes for playing back the command stream. This is good for counting calls in a frame, etc. +#define SHADERAPI_VPROF_BUFFER_PLAYBACK 1 + +#if SHADERAPI_VPROF_BUFFER_PLAYBACK && SHADERAPI_BUFFER_D3DCALLS +#define VPROF_BUFFER_PLAYBACK(name) VPROF(name) +#else +#define VPROF_BUFFER_PLAYBACK(name) ((void)0) +#endif + +template class FixedWorkQueue +{ + T Data[QSIZE]; + char pad0[256]; + volatile int n_added; + int write_index; + char pad1[256]; // make sure these don't share cache lines + volatile int n_removed; + int read_index; + +public: + FixedWorkQueue(void) + { + read_index=write_index=0; + n_added=n_removed=0; + } + + int IsEmpty(void) + { + return (n_added==n_removed); + } + + int IsFull(void) + { + return (n_added-n_removed)>=QSIZE; + } + + T GetWorkUnit(void) + { + if (IsEmpty()) + return 0; + return Data[read_index]; + } + + void MarkUnitDone(void) + { + n_removed++; + read_index=(read_index+1) % QSIZE; + } + + void AddWorkUnit(T unit) + { +#if SHADERAPI_BUFFER_D3DCALLS + Assert( !IsFull() ); +#else + while (IsFull()) + Sleep(0); +#endif + Data[write_index]=unit; + n_added++; + write_index=(write_index+1) % QSIZE; + } +}; + + +#if SHADERAPI_BUFFER_D3DCALLS +#define N_PUSH_BUFFERS 5000 +#else +#define N_PUSH_BUFFERS 500 +#endif +static volatile PushBuffer *PushBuffers[N_PUSH_BUFFERS]; +FixedWorkQueue PBQueue; + + + +#ifdef WIN32 +void __cdecl OurThreadInit( void * ourthis ) +#else +unsigned int OurThreadInit( void * ourthis ) +#endif +{ + (( D3DDeviceWrapper *) ourthis )->RunThread(); +#ifndef WIN32 + return 0; +#endif +} + +void D3DDeviceWrapper::RunThread( void ) +{ + SetThreadAffinityMask(GetCurrentThread(), 2); + for(;;) + { + PushBuffer *Pbuf=PBQueue.GetWorkUnit(); + if (! Pbuf) + { + ; //Sleep(0); + } + else + { + ExecutePushBuffer( Pbuf ); + PBQueue.MarkUnitDone(); + Pbuf->m_State = PUSHBUFFER_AVAILABLE; + } + } +} + +#if SHADERAPI_BUFFER_D3DCALLS +void D3DDeviceWrapper::ExecuteAllWork( void ) +{ + if( !m_bBufferingD3DCalls ) + return; + VPROF_BUDGET( "ExecuteAllWork", "ExecuteAllWork" ); + SubmitPushBufferAndGetANewOne(); + PushBuffer *Pbuf; + while( ( Pbuf = PBQueue.GetWorkUnit() ) != NULL ) + { + ExecutePushBuffer( Pbuf ); + PBQueue.MarkUnitDone(); + Pbuf->m_State = PUSHBUFFER_AVAILABLE; + } + m_bBufferingD3DCalls = false; +} +#endif + +#if SHADERAPI_BUFFER_D3DCALLS +#define MAXIMUM_NUMBER_OF_BUFFERS_LOCKED_AT_ONCE 1600 +#else +#define MAXIMUM_NUMBER_OF_BUFFERS_LOCKED_AT_ONCE 16 +#endif + +struct RememberedPointer +{ + void *m_pKey; + void *m_pRememberedPtr; +} RememberedPointerHistory[MAXIMUM_NUMBER_OF_BUFFERS_LOCKED_AT_ONCE]; + +void D3DDeviceWrapper::SetASyncMode( bool onoff ) +{ +#if SHADERAPI_BUFFER_D3DCALLS + if ( onoff ) + { + m_bBufferingD3DCalls = true; + // allocate push buffers if we need to + if ( PushBuffers[0] == NULL ) + { + for(int i=0; im_State == PUSHBUFFER_AVAILABLE ) + { + PushBuffers[i]->m_State = newstate; + return (PushBuffer *) PushBuffers[i]; + } + } + // hmm, out of push buffers. better sleep and try again later + SubmitPushBufferAndGetANewOne(); + Sleep(0); + } +} + +void D3DDeviceWrapper::GetPushBuffer( void ) +{ + VPROF_BUFFER_PLAYBACK( "D3DDeviceWrapper::GetPushBuffer" ); + m_pCurPushBuffer = FindFreePushBuffer( PUSHBUFFER_BEING_FILLED ); + m_pOutputPtr = m_pCurPushBuffer->m_BufferData; + m_PushBufferFreeSlots = PUSHBUFFER_NELEMS - 1; // leave room for end marker +} + +void D3DDeviceWrapper::SubmitPushBufferAndGetANewOne( void ) +{ + VPROF_BUFFER_PLAYBACK( "D3DDeviceWrapper::SubmitPushBufferAndGetANewOne" ); + + // submit the current push buffer + if ( m_pCurPushBuffer ) + { + if (m_pOutputPtr == m_pCurPushBuffer->m_BufferData) // haven't done anyting, don't bother + return; + *(m_pOutputPtr) = PBCMD_END; // mark end + m_pCurPushBuffer->m_State = PUSHBUFFER_SUBMITTED; + // here, enqueue for task + PBQueue.AddWorkUnit( m_pCurPushBuffer ); + } + GetPushBuffer(); +} + +void D3DDeviceWrapper::SubmitIfNotBusy( void ) +{ + VPROF_BUFFER_PLAYBACK( "D3DDeviceWrapper::SubmitIfNotBusy" ); + if ( PBQueue.IsEmpty() ) + SubmitPushBufferAndGetANewOne(); +} + + +void D3DDeviceWrapper::Synchronize( void ) +{ +#if SHADERAPI_BUFFER_D3DCALLS + if( m_bBufferingD3DCalls ) + { + Assert( 0 ); + Error( "Synchronize not supported with SHADERAPI_BUFFER_D3DCALLS" ); + } + return; +#endif + if ( ASyncMode()) + { + SubmitPushBufferAndGetANewOne(); + // here, wait for queue to become empty + while (! PBQueue.IsEmpty() ) + { + // Sleep(1); + } + } +} + +void D3DDeviceWrapper::AsynchronousLock( IDirect3DIndexBuffer9* ib, + size_t offset, size_t size, void **ptr, + DWORD flags, + LockedBufferContext *lb) +{ + VPROF_BUFFER_PLAYBACK( "D3DDeviceWrapper::AsynchronousLock index" ); + + if ( size <= sizeof( PushBuffers[0]->m_BufferData )) + { + // can use one of our pushbuffers for this + lb->m_pPushBuffer = FindFreePushBuffer( PUSHBUFFER_BEING_USED_FOR_LOCKEDDATA ); + *(ptr) = lb->m_pPushBuffer->m_BufferData; + Assert( *ptr ); + lb->m_pMallocedMemory = NULL; + } + else // out of buffer space or size too big + { + lb->m_pPushBuffer = NULL; + lb->m_pMallocedMemory = new uint8 [ size ]; + *(ptr) = lb->m_pMallocedMemory; + } + // now, push lock commands + AllocatePushBufferSpace( 1+N_DWORDS_IN_PTR+3 ); + *(m_pOutputPtr++)=PBCMD_ASYNC_LOCK_IB; + *((LPDIRECT3DINDEXBUFFER *) m_pOutputPtr)=ib; + m_pOutputPtr+=N_DWORDS_IN_PTR; + *(m_pOutputPtr++)=offset; + *(m_pOutputPtr++)=size; + *(m_pOutputPtr++)=flags; +} + +void D3DDeviceWrapper::AsynchronousLock( IDirect3DVertexBuffer9* vb, + size_t offset, size_t size, void **ptr, + DWORD flags, + LockedBufferContext *lb) +{ + VPROF_BUFFER_PLAYBACK( "D3DDeviceWrapper::AsynchronousLock vertex" ); + // we have commands in flight. Need to use temporary memory for this lock. + // if the size needed is < the amount of space in a push buffer, we can use + // a push buffer for the buffer. Otherwise, we're going to malloc one. + if ( size <= sizeof( PushBuffers[0]->m_BufferData )) + { + // can use one of our pushbuffers for this + lb->m_pPushBuffer = FindFreePushBuffer( PUSHBUFFER_BEING_USED_FOR_LOCKEDDATA ); + *(ptr) = lb->m_pPushBuffer->m_BufferData; + Assert( *ptr ); + lb->m_pMallocedMemory = NULL; + } + else // out of buffer space or size too big + { + lb->m_pPushBuffer = NULL; + lb->m_pMallocedMemory = new uint8 [ size ]; + *(ptr) = lb->m_pMallocedMemory; + } + // now, push lock commands + AllocatePushBufferSpace( 1+N_DWORDS_IN_PTR+3 ); + *(m_pOutputPtr++)=PBCMD_ASYNC_LOCK_VB; + *((LPDIRECT3DVERTEXBUFFER *) m_pOutputPtr)=vb; + m_pOutputPtr+=N_DWORDS_IN_PTR; + *(m_pOutputPtr++)=offset; + *(m_pOutputPtr++)=size; + *(m_pOutputPtr++)=flags; +} + + + +inline void RememberLockedPointer( void *key, void *value ) +{ + VPROF_BUFFER_PLAYBACK( "RememberLockedPointer" ); + int repl=-1; + int i; + for(i=0;iLock( offset, size, &locked_ptr, flags ); + RememberLockedPointer( vb, locked_ptr ); +} + +void D3DDeviceWrapper::HandleAsynchronousUnLockVBCommand( uint32 const *dptr ) +{ + dptr++; + LPDIRECT3DVERTEXBUFFER vb=*((LPDIRECT3DVERTEXBUFFER *) dptr); + dptr+=N_DWORDS_IN_PTR; + LockedBufferContext lb=*((LockedBufferContext *) dptr); + dptr+=N_DWORDS( LockedBufferContext ); + size_t unlock_size=*( dptr++ ); + void *locked_data=RecallLockedPointer( vb ); + Assert( locked_data ); + if (lb.m_pPushBuffer) + { + Assert( ! lb.m_pMallocedMemory ); + if ( locked_data ) + memcpy( locked_data, lb.m_pPushBuffer->m_BufferData, unlock_size ); + lb.m_pPushBuffer->m_State = PUSHBUFFER_AVAILABLE; + } + else if ( lb.m_pMallocedMemory ) + { + Assert( ! lb.m_pPushBuffer ); + if ( locked_data ) + memcpy( locked_data, lb.m_pMallocedMemory, unlock_size ); + delete[] ((uint8 *) lb.m_pMallocedMemory); + } + // now, actually unlock + RememberLockedPointer( vb, NULL ); + vb->Unlock(); +} + +void D3DDeviceWrapper::HandleAsynchronousLockIBCommand( uint32 const *dptr ) +{ + dptr++; + LPDIRECT3DINDEXBUFFER ib=*((LPDIRECT3DINDEXBUFFER *) dptr); + Assert( ib ); + dptr+=N_DWORDS_IN_PTR; + uint32 offset=*(dptr++); + uint32 size=*(dptr++); + uint32 flags=*(dptr++); + void *locked_ptr=0; + ib->Lock( offset, size, &locked_ptr, flags ); + RememberLockedPointer( ib, locked_ptr ); +} + +void D3DDeviceWrapper::HandleAsynchronousUnLockIBCommand( uint32 const *dptr ) +{ + dptr++; + LPDIRECT3DINDEXBUFFER ib=*((LPDIRECT3DINDEXBUFFER *) dptr); + dptr+=N_DWORDS_IN_PTR; + LockedBufferContext lb=*((LockedBufferContext *) dptr); + dptr+=N_DWORDS( LockedBufferContext ); + size_t unlock_size=*( dptr++ ); + void *locked_data=RecallLockedPointer( ib ); + Assert( locked_data ); + if (lb.m_pPushBuffer) + { + Assert( ! lb.m_pMallocedMemory ); + if ( locked_data ) + memcpy( locked_data, lb.m_pPushBuffer->m_BufferData, unlock_size ); + lb.m_pPushBuffer->m_State = PUSHBUFFER_AVAILABLE; + } + else if ( lb.m_pMallocedMemory ) + { + Assert( ! lb.m_pPushBuffer ); + if ( locked_data ) + memcpy( locked_data, lb.m_pMallocedMemory, unlock_size ); + delete[] ((uint8 *) lb.m_pMallocedMemory); + } + // now, actually unlock + RememberLockedPointer( ib, NULL ); + ib->Unlock(); + +} + + +static inline void *FetchPtr( uint32 const *mem) +{ + void **p=(void **) mem; + return *p; +} + +#define CALC_STATS 1 +#if CALC_STATS +int n_commands_executed=0; +int n_pbs_executed=0; +#endif + +void D3DDeviceWrapper::ExecutePushBuffer( PushBuffer const* pb) +{ + VPROF_BUFFER_PLAYBACK( "D3DDeviceWrapper::ExecutePushBuffer" ); + uint32 const *dptr=pb->m_BufferData; + n_pbs_executed++; + for(;;) + { + n_commands_executed++; + switch( dptr[0] ) + { + case PBCMD_END: + { + VPROF_BUFFER_PLAYBACK( "END" ); + n_commands_executed--; // doesn't count + return; + } + + case PBCMD_SET_RENDERSTATE: + { + VPROF_BUFFER_PLAYBACK( "SET_RENDERSTATE" ); + Dx9Device()->SetRenderState((D3DRENDERSTATETYPE) dptr[1],dptr[2]); + dptr+=3; + break; + } + + case PBCMD_SET_SAMPLER_STATE: + { + VPROF_BUFFER_PLAYBACK( "SET_SAMPLER_STATE" ); + Dx9Device()->SetSamplerState(dptr[1], (D3DSAMPLERSTATETYPE) dptr[2], dptr[3]); + dptr+=4; + break; + } + + case PBCMD_DRAWPRIM: + { + VPROF_BUFFER_PLAYBACK( "DRAWPRIM" ); + + tmZone( TELEMETRY_LEVEL2, TMZF_NONE, "Dx9Device()->DrawPrimitive" ); + + Dx9Device()->DrawPrimitive( (D3DPRIMITIVETYPE) dptr[1], dptr[2], dptr[3] ); + dptr+=4; + break; + } + + case PBCMD_DRAWINDEXEDPRIM: + { + VPROF_BUFFER_PLAYBACK( "DRAWINDEXEDPRIM" ); + + tmZone( TELEMETRY_LEVEL2, TMZF_NONE, "Dx9Device()->DrawIndexedPrimitive" ); + + Dx9Device()->DrawIndexedPrimitive( (D3DPRIMITIVETYPE) dptr[1], dptr[2], dptr[3], + dptr[4], dptr[5], dptr[6]); + dptr+=7; + break; + } + + case PBCMD_SET_STREAM_SOURCE: + { + VPROF_BUFFER_PLAYBACK( "SET_STREAM_SOURCE" ); + Dx9Device()->SetStreamSource( dptr[1],(IDirect3DVertexBuffer9 *) FetchPtr(dptr+2), + dptr[3],dptr[4] ); + dptr += 4+N_DWORDS( IDirect3DVertexBuffer9 * ); + break; + } + + case PBCMD_SET_TEXTURE: + { + VPROF_BUFFER_PLAYBACK( "SET_TEXTURE" ); + Dx9Device()->SetTexture( dptr[1],(IDirect3DBaseTexture *) FetchPtr(dptr+2)); + dptr += 2+N_DWORDS_IN_PTR; + break; + } + + case PBCMD_SET_RENDER_TARGET: + { + VPROF_BUFFER_PLAYBACK( "SET_RENDER_TARGET" ); + Dx9Device()->SetRenderTarget( dptr[1],(IDirect3DSurface *) FetchPtr(dptr+2)); + dptr += 2+N_DWORDS_IN_PTR; + break; + } + + case PBCMD_SET_PIXEL_SHADER: + { + VPROF_BUFFER_PLAYBACK( "SET_PIXEL_SHADER" ); + Dx9Device()->SetPixelShader( (IDirect3DPixelShader9 *) FetchPtr(dptr+1)); + dptr += 1+N_DWORDS_IN_PTR; + break; + } + + case PBCMD_SET_INDICES: + { + VPROF_BUFFER_PLAYBACK( "SET_INDICES" ); + Dx9Device()->SetIndices( (IDirect3DIndexBuffer9*) FetchPtr(dptr+1)); + dptr += 1+N_DWORDS_IN_PTR; + break; + } + + case PBCMD_SET_DEPTH_STENCIL_SURFACE: + { + VPROF_BUFFER_PLAYBACK( "SET_DEPTH_STENCIL_SURFACE" ); + Dx9Device()->SetDepthStencilSurface( (IDirect3DSurface9*) FetchPtr(dptr+1)); + dptr += 1+N_DWORDS_IN_PTR; + break; + } + + case PBCMD_SETVIEWPORT: + { + VPROF_BUFFER_PLAYBACK( "SETVIEWPORT" ); + Dx9Device()->SetViewport( (D3DVIEWPORT9 const *) (dptr+1) ); + dptr += 1+N_DWORDS(D3DVIEWPORT9); + break; + } + + case PBCMD_SET_VERTEX_SHADER: + { + VPROF_BUFFER_PLAYBACK( "SET_VERTEX_SHADER" ); + Dx9Device()->SetVertexShader( (IDirect3DVertexShader9 *) FetchPtr(dptr+1)); + dptr += 1+N_DWORDS_IN_PTR; + break; + } + + case PBCMD_ASYNC_LOCK_VB: + { + VPROF_BUFFER_PLAYBACK( "ASYNC_LOCK_VB" ); + HandleAsynchronousLockVBCommand(dptr); + dptr+=1+N_DWORDS_IN_PTR+3; + break; + } + + case PBCMD_ASYNC_UNLOCK_VB: + { + VPROF_BUFFER_PLAYBACK( "ASYNC_UNLOCK_VB" ); + HandleAsynchronousUnLockVBCommand( dptr ); + dptr+=1+N_DWORDS_IN_PTR+N_DWORDS( LockedBufferContext )+1; + break; + } + + case PBCMD_ASYNC_LOCK_IB: + { + VPROF_BUFFER_PLAYBACK( "ASYNC_LOCK_IB" ); + HandleAsynchronousLockIBCommand(dptr); + dptr+=1+N_DWORDS_IN_PTR+3; + break; + } + + case PBCMD_ASYNC_UNLOCK_IB: + { + VPROF_BUFFER_PLAYBACK( "ASYNC_UNLOCK_IB" ); + HandleAsynchronousUnLockIBCommand( dptr ); + dptr+=1+N_DWORDS_IN_PTR+N_DWORDS( LockedBufferContext )+1; + break; + } + + case PBCMD_UNLOCK_VB: + { + VPROF_BUFFER_PLAYBACK( "UNLOCK_VB" ); + IDirect3DVertexBuffer9 *p=(IDirect3DVertexBuffer9 *) FetchPtr(dptr+1); + p->Unlock(); + dptr += 1+N_DWORDS_IN_PTR; + break; + } + case PBCMD_UNLOCK_IB: + { + VPROF_BUFFER_PLAYBACK( "UNLOCK_IB" ); + IDirect3DIndexBuffer9 *p=(IDirect3DIndexBuffer9 *) FetchPtr(dptr+1); + p->Unlock(); + dptr += 1+N_DWORDS_IN_PTR; + break; + } + case PBCMD_SET_VERTEX_SHADER_CONSTANT: + { + VPROF_BUFFER_PLAYBACK( "SET_VERTEX_SHADER_CONSTANT" ); + Dx9Device()->SetVertexShaderConstantF( dptr[1], (float const *) dptr+3, dptr[2]); + dptr += 3+4*dptr[2]; + break; + } + case PBCMD_SET_BOOLEAN_VERTEX_SHADER_CONSTANT: + { + VPROF_BUFFER_PLAYBACK( "SET_BOOLEAN_VERTEX_SHADER_CONSTANT" ); + Dx9Device()->SetVertexShaderConstantB( dptr[1], (int const *) dptr+3, dptr[2]); + dptr += 3+dptr[2]; + break; + } + + case PBCMD_SET_INTEGER_VERTEX_SHADER_CONSTANT: + { + VPROF_BUFFER_PLAYBACK( "SET_INTEGER_VERTEX_SHADER_CONSTANT" ); + Dx9Device()->SetVertexShaderConstantI( dptr[1], (int const *) dptr+3, dptr[2]); + dptr += 3+4*dptr[2]; + break; + } + + case PBCMD_SET_PIXEL_SHADER_CONSTANT: + { + VPROF_BUFFER_PLAYBACK( "SET_PIXEL_SHADER_CONSTANT" ); + Dx9Device()->SetPixelShaderConstantF( dptr[1], (float const *) dptr+3, dptr[2]); + dptr += 3+4*dptr[2]; + break; + } + case PBCMD_SET_BOOLEAN_PIXEL_SHADER_CONSTANT: + { + VPROF_BUFFER_PLAYBACK( "SET_BOOLEAN_PIXEL_SHADER_CONSTANT" ); + Dx9Device()->SetPixelShaderConstantB( dptr[1], (int const *) dptr+3, dptr[2]); + dptr += 3+dptr[2]; + break; + } + + case PBCMD_SET_INTEGER_PIXEL_SHADER_CONSTANT: + { + VPROF_BUFFER_PLAYBACK( "SET_INTEGER_PIXEL_SHADER_CONSTANT" ); + Dx9Device()->SetPixelShaderConstantI( dptr[1], (int const *) dptr+3, dptr[2]); + dptr += 3+4*dptr[2]; + break; + } + + case PBCMD_BEGIN_SCENE: + { + VPROF_BUFFER_PLAYBACK( "BEGIN_SCENE" ); + Dx9Device()->BeginScene(); + dptr++; + break; + } + + case PBCMD_END_SCENE: + { + VPROF_BUFFER_PLAYBACK( "END_SCENE" ); + Dx9Device()->EndScene(); + dptr++; + break; + } + + case PBCMD_CLEAR: + { + VPROF_BUFFER_PLAYBACK( "CLEAR" ); + dptr++; + int count=*(dptr++); + D3DRECT const *pRects=0; + if (count) + { + pRects=(D3DRECT const *) dptr; + dptr+=count*N_DWORDS( D3DRECT ); + } + int flags=*(dptr++); + D3DCOLOR color=*((D3DCOLOR const *) (dptr++)); + float z=*((float const *) (dptr++)); + int stencil=*(dptr++); + Dx9Device()->Clear( count, pRects, flags, color, z, stencil ); + break; + } + + case PBCMD_SET_VERTEXDECLARATION: + { + VPROF_BUFFER_PLAYBACK( "SET_VERTEXDECLARATION" ); + Dx9Device()->SetVertexDeclaration( (IDirect3DVertexDeclaration9 *) FetchPtr(dptr+1)); + dptr += 1+N_DWORDS_IN_PTR; + break; + } + + case PBCMD_SETCLIPPLANE: + { + VPROF_BUFFER_PLAYBACK( "SETCLIPPLANE" ); + Dx9Device()->SetClipPlane( dptr[1], (float const *) dptr+2 ); + dptr+=6; + } + break; + + case PBCMD_STRETCHRECT: + { + VPROF_BUFFER_PLAYBACK( "STRETCHRECT" ); + dptr++; + IDirect3DSurface9 *pSourceSurface=(IDirect3DSurface9 *) FetchPtr(dptr); + dptr+=N_DWORDS_IN_PTR; + RECT const *pSourceRect=0; + if (*(dptr++)) + pSourceRect=(RECT const *) dptr; + dptr += N_DWORDS( RECT ); + IDirect3DSurface9 *pDestSurface= (IDirect3DSurface9 *) FetchPtr( dptr ); + dptr += N_DWORDS_IN_PTR; + RECT const *pDestRect=0; + if (*(dptr++)) + pDestRect=(RECT const *) dptr; + dptr += N_DWORDS( RECT ); + D3DTEXTUREFILTERTYPE Filter = (D3DTEXTUREFILTERTYPE) *(dptr++); + Dx9Device()->StretchRect( pSourceSurface, pSourceRect, + pDestSurface, pDestRect, + Filter ); + } + break; + + + case PBCMD_PRESENT: + { + VPROF_BUFFER_PLAYBACK( "PRESENT" ); + dptr++; + RECT const *pSourceRect=0; + if (* (dptr++) ) + pSourceRect=(RECT const *) dptr; + dptr+=N_DWORDS( RECT ); + RECT const *pDestRect = 0; + if (* (dptr++) ) + pDestRect=(RECT const *) dptr; + dptr+=N_DWORDS( RECT ); + VD3DHWND hDestWindowOverride = (VD3DHWND) *(dptr++); + RGNDATA const *pDirtyRegion=0; + if ( *(dptr++) ) + pDirtyRegion= (RGNDATA const *) dptr; + dptr+=N_DWORDS( RGNDATA ); + + tmZone( TELEMETRY_LEVEL1, TMZF_NONE, "!D3DPresent" ); + + Dx9Device()->Present( pSourceRect, pDestRect, hDestWindowOverride, pDirtyRegion ); + break; + } + + case PBCMD_SET_SCISSOR_RECT: + { + VPROF_BUFFER_PLAYBACK( "SET_SCISSOR_RECT" ); + dptr++; + const RECT *pRect = ( RECT * )FetchPtr( dptr ); + dptr += sizeof( RECT ); + Dx9Device()->SetScissorRect( pRect ); + } + } + } +} + +#endif + +#endif // D3D_ASYNC_SUPPORTED diff --git a/materialsystem/shaderapidx9/d3d_async.h b/materialsystem/shaderapidx9/d3d_async.h new file mode 100644 index 0000000..7deb2f3 --- /dev/null +++ b/materialsystem/shaderapidx9/d3d_async.h @@ -0,0 +1,1567 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//===========================================================================// + +#ifdef D3D_ASYNC_SUPPORTED + +#ifndef D3DASYNC_H +#define D3DASYNC_H + +#ifdef _WIN32 +#pragma once +#endif + +// Set this to 1 to allow d3d calls to be buffered and played back on another thread +// Slamming this off - it's causing very hot D3D9 function calls to not be inlined and contain a bunch of unused code. (Does this code even work/add real value any more?) +#define SHADERAPI_USE_SMP 0 + +// Set this to 1 to allow buffering of the whole frame to memory and then playback (singlethreaded). +// This is for debugging only and is used to test the performance of just calling D3D and rendering without other CPU overhead. +#define SHADERAPI_BUFFER_D3DCALLS 0 + +#if SHADERAPI_BUFFER_D3DCALLS && !SHADERAPI_USE_SMP +# error "SHADERAPI_USE_SMP must be 1 for SHADERAPI_BUFFER_D3DCALLS to work!" +#endif + +#include "recording.h" +#include "strtools.h" +#include "glmgr/dxabstract.h" + +#ifdef NDEBUG +#define DO_D3D(x) Dx9Device()->x +#else +#define DO_D3D(x) Dx9Device()->x +//#define DO_D3D(x) { HRESULT hr=Dx9Device()->x; Assert( !FAILED(hr) ); } +#endif + +#define PUSHBUFFER_NELEMS 4096 + +enum PushBufferState +{ + PUSHBUFFER_AVAILABLE, + PUSHBUFFER_BEING_FILLED, + PUSHBUFFER_SUBMITTED, + PUSHBUFFER_BEING_USED_FOR_LOCKEDDATA, +}; + +class PushBuffer +{ + friend class D3DDeviceWrapper; + + volatile PushBufferState m_State; + uint32 m_BufferData[PUSHBUFFER_NELEMS]; +public: + PushBuffer(void) + { + m_State = PUSHBUFFER_AVAILABLE; + } +}; + +// When running multithreaded, lock for write calls actually return a pointer to temporary memory +// buffer. When the buffer is later unlocked by the caller, data must be queued with the Unlock() +// that lets the d3d thread know how much data to copy from where. One possible optimization for +// things which write a lot of data into lock buffers woudl be to proviude a way for the caller to +// occasionally check if the Lock() has been dequeued. If so, the the data pushed so far could be +// copied asynchronously into the buffer, while the caller would be told to switch to writing +// directly to the vertex buffer. +// +// another possibility would be lock()ing in advance for large ones, such as the world renderer, +// or keeping multiple locked vb's open for meshbuilder. + +struct LockedBufferContext +{ + PushBuffer *m_pPushBuffer; // if a push buffer was used to hold + // the temporary data, this will be non-null + void *m_pMallocedMemory; // if memory had to be malloc'd, this will be set. + + size_t m_MallocSize; // # of bytes malloced if mallocedmem ptr non-null + + LockedBufferContext( void ) + { + m_pPushBuffer = NULL; + m_pMallocedMemory = NULL; + } + +}; + + + +// push buffer commands follow +enum PushBufferCommand +{ + PBCMD_END, // at end of push buffer + PBCMD_SET_RENDERSTATE, // state, val + PBCMD_SET_TEXTURE, // stage, txtr + PBCMD_DRAWPRIM, // prim type, start v, nprims + PBCMD_DRAWINDEXEDPRIM, // prim type, baseidx, minidx, numv, starti, pcount + PBCMD_SET_PIXEL_SHADER, // shaderptr + PBCMD_SET_VERTEX_SHADER, // shaderptr + PBCMD_SET_PIXEL_SHADER_CONSTANT, // startreg, nregs, data... + PBCMD_SET_BOOLEAN_PIXEL_SHADER_CONSTANT, // startreg, nregs, data... + PBCMD_SET_INTEGER_PIXEL_SHADER_CONSTANT, // startreg, nregs, data... + PBCMD_SET_VERTEX_SHADER_CONSTANT, // startreg, nregs, data... + PBCMD_SET_BOOLEAN_VERTEX_SHADER_CONSTANT, // startreg, nregs, data... + PBCMD_SET_INTEGER_VERTEX_SHADER_CONSTANT, // startreg, nregs, data... + PBCMD_SET_RENDER_TARGET, // idx, targetptr + PBCMD_SET_DEPTH_STENCIL_SURFACE, // surfptr + PBCMD_SET_STREAM_SOURCE, // idx, sptr, ofs, stride + PBCMD_SET_INDICES, // idxbuffer + PBCMD_SET_SAMPLER_STATE, // stage, state, val + PBCMD_UNLOCK_VB, // vptr + PBCMD_UNLOCK_IB, // idxbufptr + PBCMD_SETVIEWPORT, // vp_struct + PBCMD_CLEAR, // count, n rect structs, flags, color, z, stencil + PBCMD_SET_VERTEXDECLARATION, // vdeclptr + PBCMD_BEGIN_SCENE, // + PBCMD_END_SCENE, // + PBCMD_PRESENT, // complicated..see code + PBCMD_SETCLIPPLANE, // idx, 4 floats + PBCMD_STRETCHRECT, // see code + PBCMD_ASYNC_LOCK_VB, // see code + PBCMD_ASYNC_UNLOCK_VB, + PBCMD_ASYNC_LOCK_IB, // see code + PBCMD_ASYNC_UNLOCK_IB, + PBCMD_SET_SCISSOR_RECT, // RECT +}; + + + +#define N_DWORDS( x ) (( sizeof(x)+3)/sizeof( DWORD )) +#define N_DWORDS_IN_PTR (N_DWORDS( void * )) + +class D3DDeviceWrapper +{ +private: + IDirect3DDevice9 *m_pD3DDevice; + bool m_bSupportsTessellation; + int m_nCurrentTessLevel; + TessellationMode_t m_nTessellationMode; + +#if SHADERAPI_USE_SMP + uintptr_t m_pASyncThreadHandle; + PushBuffer *m_pCurPushBuffer; + uint32 *m_pOutputPtr; + size_t m_PushBufferFreeSlots; +#endif + +#if SHADERAPI_BUFFER_D3DCALLS + bool m_bBufferingD3DCalls; +# define SHADERAPI_BUFFER_MAXRENDERTARGETS 4 + IDirect3DSurface9 *m_StoredRenderTargets[SHADERAPI_BUFFER_MAXRENDERTARGETS]; +#endif + + PushBuffer *FindFreePushBuffer( PushBufferState newstate ); // find a free push buffer and change its state + + void GetPushBuffer(void); // set us up to point at a new push buffer + void SubmitPushBufferAndGetANewOne(void); // submit the current push buffer + void ExecutePushBuffer( PushBuffer const *pb); + +#if SHADERAPI_USE_SMP + void Synchronize(void); // wait for all commands to be done +#else + FORCEINLINE void Synchronize(void) + { + } +#endif + + + void SubmitIfNotBusy(void); + +#if SHADERAPI_USE_SMP + template FORCEINLINE void PushStruct( PushBufferCommand cmd, T const *str ) + { + int nwords=N_DWORDS( T ); + AllocatePushBufferSpace( 1+ nwords ); + m_pOutputPtr[0]=cmd; + memcpy( m_pOutputPtr+1, str, sizeof( T ) ); + m_pOutputPtr += 1+nwords; + } + + FORCEINLINE void AllocatePushBufferSpace(size_t nSlots) + { + // check for N slots of space, and decrement amount of space left + if ( nSlots>m_PushBufferFreeSlots ) // out of room? + { + SubmitPushBufferAndGetANewOne(); + } + m_PushBufferFreeSlots -= nSlots; + } + + // simple methods for pushing a few words into output buffer + FORCEINLINE void Push( PushBufferCommand cmd ) + { + AllocatePushBufferSpace(1); + m_pOutputPtr[0]=cmd; + m_pOutputPtr++; + } + + FORCEINLINE void Push( PushBufferCommand cmd, int arg1) + { + AllocatePushBufferSpace(2); + m_pOutputPtr[0]=cmd; + m_pOutputPtr[1]=arg1; + m_pOutputPtr += 2; + } + + FORCEINLINE void Push( PushBufferCommand cmd, void *ptr ) + { + AllocatePushBufferSpace(1+N_DWORDS_IN_PTR); + *(m_pOutputPtr++)=cmd; + *((void **) m_pOutputPtr)=ptr; + m_pOutputPtr+=N_DWORDS_IN_PTR; + } + + FORCEINLINE void Push( PushBufferCommand cmd, void *ptr, void *ptr1 ) + { + AllocatePushBufferSpace(1+2*N_DWORDS_IN_PTR); + *(m_pOutputPtr++)=cmd; + *((void **) m_pOutputPtr)=ptr; + m_pOutputPtr+=N_DWORDS_IN_PTR; + *((void **) m_pOutputPtr)=ptr1; + m_pOutputPtr+=N_DWORDS_IN_PTR; + } + + FORCEINLINE void Push( PushBufferCommand cmd, void *arg1, uint32 arg2, uint32 arg3, uint32 arg4, + void *arg5) + { + AllocatePushBufferSpace(1+N_DWORDS_IN_PTR+1+1+1+N_DWORDS_IN_PTR); + *(m_pOutputPtr++)=cmd; + *((void **) m_pOutputPtr)=arg1; + m_pOutputPtr+=N_DWORDS_IN_PTR; + *(m_pOutputPtr++)=arg2; + *(m_pOutputPtr++)=arg3; + *(m_pOutputPtr++)=arg4; + *((void **) m_pOutputPtr)=arg5; + m_pOutputPtr+=N_DWORDS_IN_PTR; + + } + + FORCEINLINE void Push( PushBufferCommand cmd, uint32 arg1, void *ptr ) + { + AllocatePushBufferSpace(2+N_DWORDS_IN_PTR); + *(m_pOutputPtr++)=cmd; + *(m_pOutputPtr++)=arg1; + *((void **) m_pOutputPtr)=ptr; + m_pOutputPtr+=N_DWORDS_IN_PTR; + } + + FORCEINLINE void Push( PushBufferCommand cmd, uint32 arg1, void *ptr, int arg2, int arg3 ) + { + AllocatePushBufferSpace( 4+N_DWORDS_IN_PTR ); + *(m_pOutputPtr++)=cmd; + *(m_pOutputPtr++)=arg1; + *((void **) m_pOutputPtr)=ptr; + m_pOutputPtr+=N_DWORDS_IN_PTR; + m_pOutputPtr[0]=arg2; + m_pOutputPtr[1]=arg3; + m_pOutputPtr += 2; + } + + FORCEINLINE void Push( PushBufferCommand cmd, int arg1, int arg2) + { + AllocatePushBufferSpace(3); + m_pOutputPtr[0]=cmd; + m_pOutputPtr[1]=arg1; + m_pOutputPtr[2]=arg2; + m_pOutputPtr += 3; + } + + FORCEINLINE void Push( PushBufferCommand cmd, int arg1, int arg2, int arg3) + { + AllocatePushBufferSpace(4); + m_pOutputPtr[0]=cmd; + m_pOutputPtr[1]=arg1; + m_pOutputPtr[2]=arg2; + m_pOutputPtr[3]=arg3; + m_pOutputPtr += 4; + } + + FORCEINLINE void Push( PushBufferCommand cmd, int arg1, int arg2, int arg3, int arg4, int arg5, int arg6 ) + { + AllocatePushBufferSpace(7); + m_pOutputPtr[0]=cmd; + m_pOutputPtr[1]=arg1; + m_pOutputPtr[2]=arg2; + m_pOutputPtr[3]=arg3; + m_pOutputPtr[4]=arg4; + m_pOutputPtr[5]=arg5; + m_pOutputPtr[6]=arg6; + m_pOutputPtr += 7; + } + +#else + template FORCEINLINE void PushStruct( PushBufferCommand cmd, T const *str ) + { + } + + FORCEINLINE void AllocatePushBufferSpace(size_t nSlots) + { + } + + // simple methods for pushing a few words into output buffer + FORCEINLINE void Push( PushBufferCommand cmd ) + { + } + + FORCEINLINE void Push( PushBufferCommand cmd, int arg1) + { + } + + FORCEINLINE void Push( PushBufferCommand cmd, void *ptr ) + { + } + + FORCEINLINE void Push( PushBufferCommand cmd, void *ptr, void *ptr1 ) + { + } + + FORCEINLINE void Push( PushBufferCommand cmd, void *arg1, uint32 arg2, uint32 arg3, uint32 arg4, + void *arg5) + { + } + + FORCEINLINE void Push( PushBufferCommand cmd, uint32 arg1, void *ptr ) + { + } + + FORCEINLINE void Push( PushBufferCommand cmd, uint32 arg1, void *ptr, int arg2, int arg3 ) + { + } + + FORCEINLINE void Push( PushBufferCommand cmd, int arg1, int arg2) + { + } + + FORCEINLINE void Push( PushBufferCommand cmd, int arg1, int arg2, int arg3) + { + } + + FORCEINLINE void Push( PushBufferCommand cmd, int arg1, int arg2, int arg3, int arg4, int arg5, int arg6 ) + { + } + +#endif + + FORCEINLINE bool ASyncMode(void) const + { +#if SHADERAPI_USE_SMP +# if SHADERAPI_BUFFER_D3DCALLS + return m_bBufferingD3DCalls; +# else + return (m_pASyncThreadHandle != 0 ); +# endif +#else + return false; +#endif + } + + FORCEINLINE IDirect3DDevice9* Dx9Device(void) const + { + return m_pD3DDevice; + } + + void AsynchronousLock( IDirect3DVertexBuffer9* vb, size_t offset, size_t size, void **ptr, + DWORD flags, + LockedBufferContext *lb); + + void AsynchronousLock( IDirect3DIndexBuffer9* ib, size_t offset, size_t size, void **ptr, + DWORD flags, + LockedBufferContext *lb); + + // handlers for push buffer contexts + void HandleAsynchronousLockVBCommand( uint32 const *dptr ); + void HandleAsynchronousUnLockVBCommand( uint32 const *dptr ); + void HandleAsynchronousLockIBCommand( uint32 const *dptr ); + void HandleAsynchronousUnLockIBCommand( uint32 const *dptr ); + +public: + +#if SHADERAPI_BUFFER_D3DCALLS + void ExecuteAllWork( void ); +#endif + void RunThread( void ); // this is what the worker thread runs + + void SetASyncMode( bool onoff ); + + + bool IsActive( void )const + { + return m_pD3DDevice != NULL; + } + + void D3DeviceWrapper(void) + { + m_pD3DDevice = 0; +#if SHADERAPI_USE_SMP + m_pASyncThreadHandle = 0; +#endif +#if SHADERAPI_BUFFER_D3DCALLS + m_bBufferingD3DCalls = false; +#endif + } + + void SetDevicePtr(IDirect3DDevice9 *pD3DDev ) + { + m_pD3DDevice = pD3DDev; + } + + void SetSupportsTessellation( bool bSupportsTessellation ) + { + m_bSupportsTessellation = bSupportsTessellation; + } + + void ShutDownDevice(void) + { + if ( ASyncMode() ) + { + // sync w/ thread + } + m_pD3DDevice = 0; + } + + void FORCEINLINE SetDepthStencilSurface( IDirect3DSurface9 *new_stencil ) + { + if ( ASyncMode() ) + Push( PBCMD_SET_DEPTH_STENCIL_SURFACE, new_stencil ); + else + DO_D3D( SetDepthStencilSurface( new_stencil ) ); + } + + HRESULT CreateCubeTexture( + UINT EdgeLength, + UINT Levels, + DWORD Usage, + D3DFORMAT Format, + D3DPOOL Pool, + IDirect3DCubeTexture9 ** ppCubeTexture, + HANDLE* pSharedHandle, + char *debugLabel = NULL // <-- OK to not pass this arg, only passed through on DX_TO_GL_ABSTRACTION + ) + { + Synchronize(); + return m_pD3DDevice->CreateCubeTexture( EdgeLength, Levels, Usage, Format, Pool, + ppCubeTexture, pSharedHandle + #if defined( DX_TO_GL_ABSTRACTION ) + ,debugLabel + #endif + ); + } + + HRESULT CreateVolumeTexture( + UINT Width, + UINT Height, + UINT Depth, + UINT Levels, + DWORD Usage, + D3DFORMAT Format, + D3DPOOL Pool, + IDirect3DVolumeTexture9** ppVolumeTexture, + HANDLE* pSharedHandle, + char *debugLabel = NULL // <-- OK to not pass this arg, only passed through on DX_TO_GL_ABSTRACTION + ) + { + Synchronize(); + return m_pD3DDevice->CreateVolumeTexture( Width, Height, Depth, Levels, + Usage, Format, Pool, ppVolumeTexture, + pSharedHandle + #if defined( DX_TO_GL_ABSTRACTION ) + ,debugLabel + #endif + ); + } + + HRESULT CreateOffscreenPlainSurface( UINT Width, + UINT Height, + D3DFORMAT Format, + D3DPOOL Pool, + IDirect3DSurface9** ppSurface, + HANDLE* pSharedHandle) + { + Synchronize(); + return m_pD3DDevice->CreateOffscreenPlainSurface( Width, Height, Format, Pool, + ppSurface, pSharedHandle); + } + + HRESULT CreateTexture( + UINT Width, + UINT Height, + UINT Levels, + DWORD Usage, + D3DFORMAT Format, + D3DPOOL Pool, + IDirect3DTexture9** ppTexture, + HANDLE* pSharedHandle, + char *debugLabel = NULL // <-- OK to not pass this arg, only passed through on DX_TO_GL_ABSTRACTION + ) + { + Synchronize(); + return m_pD3DDevice->CreateTexture( Width, Height, Levels, Usage, + Format, Pool, ppTexture, pSharedHandle + #if defined( DX_TO_GL_ABSTRACTION ) + ,debugLabel + #endif + ); + } + + HRESULT GetRenderTargetData( + IDirect3DSurface9* pRenderTarget, + IDirect3DSurface9* pDestSurface + ) + { + Synchronize(); + return m_pD3DDevice->GetRenderTargetData( pRenderTarget, pDestSurface ); + } + + + void GetDeviceCaps( D3DCAPS9 * pCaps ) + { + Synchronize(); + m_pD3DDevice->GetDeviceCaps( pCaps ); + } + + LPCSTR GetPixelShaderProfile( void ) + { + Synchronize(); + return D3DXGetPixelShaderProfile( m_pD3DDevice ); + } + + HRESULT TestCooperativeLevel( void ) + { + // hack! We are going to assume that calling this immediately when in buffered mode isn't going to cause problems. +#if !SHADERAPI_BUFFER_D3DCALLS + Synchronize(); +#endif + return m_pD3DDevice->TestCooperativeLevel(); + } + + HRESULT GetFrontBufferData( UINT iSwapChain, IDirect3DSurface9 * pDestSurface ) + { + Synchronize(); + return m_pD3DDevice->GetFrontBufferData( iSwapChain, pDestSurface ); + } + + void SetGammaRamp( int swapchain, int flags, D3DGAMMARAMP const *pRamp) + { + Synchronize(); + m_pD3DDevice->SetGammaRamp( swapchain, flags, pRamp); + } + + HRESULT GetTexture( DWORD Stage, IDirect3DBaseTexture9 ** ppTexture ) + { + Synchronize(); + return m_pD3DDevice->GetTexture( Stage, ppTexture ); + } + + HRESULT GetFVF( DWORD * pFVF ) + { + Synchronize(); + return m_pD3DDevice->GetFVF( pFVF ); + } + + HRESULT GetDepthStencilSurface( + IDirect3DSurface9 ** ppZStencilSurface + ) + { + Synchronize(); + return m_pD3DDevice->GetDepthStencilSurface( ppZStencilSurface ); + } + + FORCEINLINE void SetClipPlane( int idx, float const * pplane) + { + RECORD_COMMAND( DX8_SET_CLIP_PLANE, 5 ); + RECORD_INT( idx ); + RECORD_FLOAT( pplane[0] ); + RECORD_FLOAT( pplane[1] ); + RECORD_FLOAT( pplane[2] ); + RECORD_FLOAT( pplane[3] ); +#if SHADERAPI_USE_SMP + if ( ASyncMode() ) + { + AllocatePushBufferSpace( 6 ); + m_pOutputPtr[0]=PBCMD_SETCLIPPLANE; + m_pOutputPtr[1]=idx; + memcpy(m_pOutputPtr+2,pplane, 4*sizeof(float) ); + m_pOutputPtr += 6; + } + else +#endif + DO_D3D( SetClipPlane( idx, pplane ) ); + } + + FORCEINLINE void SetVertexDeclaration( IDirect3DVertexDeclaration9 *decl ) + { + RECORD_COMMAND( DX8_SET_VERTEX_DECLARATION, 1 ); + RECORD_INT( ( int ) decl ); + +#if SHADERAPI_USE_SMP + if ( ASyncMode() ) + { + Push( PBCMD_SET_VERTEXDECLARATION, decl ); + } + else +#endif + DO_D3D( SetVertexDeclaration( decl ) ); + } + + FORCEINLINE void SetViewport( D3DVIEWPORT9 const *vp ) + { + RECORD_COMMAND( DX8_SET_VIEWPORT, 1 ); + RECORD_STRUCT( vp, sizeof( *vp )); + +#if SHADERAPI_USE_SMP + if ( ASyncMode() ) + PushStruct( PBCMD_SETVIEWPORT, vp ); + else +#endif + DO_D3D( SetViewport( vp ) ); + } + + HRESULT GetRenderTarget( + DWORD RenderTargetIndex, + IDirect3DSurface9 ** ppRenderTarget) + { +#if SHADERAPI_BUFFER_D3DCALLS + if ( ASyncMode() ) + { + Assert( RenderTargetIndex >= 0 && RenderTargetIndex < SHADERAPI_BUFFER_MAXRENDERTARGETS ); + *ppRenderTarget = m_StoredRenderTargets[RenderTargetIndex]; + return D3D_OK; + } +#endif + Synchronize(); + return m_pD3DDevice->GetRenderTarget( RenderTargetIndex, ppRenderTarget ); + } + + HRESULT CreateQuery( D3DQUERYTYPE Type, IDirect3DQuery9** ppQuery ) + { + Synchronize(); + return m_pD3DDevice->CreateQuery( Type, ppQuery ); + } + + HRESULT CreateRenderTarget( + UINT Width, + UINT Height, + D3DFORMAT Format, + D3DMULTISAMPLE_TYPE MultiSample, + DWORD MultisampleQuality, + BOOL Lockable, + IDirect3DSurface9** ppSurface, + HANDLE* pSharedHandle + ) + { + Synchronize(); + return m_pD3DDevice->CreateRenderTarget( Width, Height, Format, MultiSample, + MultisampleQuality, Lockable, ppSurface, + pSharedHandle); + } + + HRESULT CreateDepthStencilSurface( + UINT Width, + UINT Height, + D3DFORMAT Format, + D3DMULTISAMPLE_TYPE MultiSample, + DWORD MultisampleQuality, + BOOL Discard, + IDirect3DSurface9** ppSurface, + HANDLE* pSharedHandle + ) + { + Synchronize(); + return m_pD3DDevice->CreateDepthStencilSurface( Width, Height, Format, MultiSample, + MultisampleQuality, Discard, ppSurface, + pSharedHandle ); + } + + + FORCEINLINE void SetRenderTarget( int idx, IDirect3DSurface9 *new_rt ) + { + if (ASyncMode()) + { + Push( PBCMD_SET_RENDER_TARGET, idx, new_rt ); +#if SHADERAPI_BUFFER_D3DCALLS + m_StoredRenderTargets[idx] = new_rt; +#endif + } + else + { + // NOTE: If the debug runtime breaks here on the shadow depth render target that is normal. dx9 doesn't directly support shadow + // depth texturing so we are forced to initialize this texture without the render target flagr + DO_D3D( SetRenderTarget( idx, new_rt) ); + } + } + + FORCEINLINE void LightEnable( int lidx, bool onoff ) + { + RECORD_COMMAND( DX8_LIGHT_ENABLE, 2 ); + RECORD_INT( lidx ); + RECORD_INT( onoff ); + + Synchronize(); + DO_D3D( LightEnable( lidx, onoff ) ); + } + + FORCEINLINE void SetRenderState( D3DRENDERSTATETYPE state, DWORD val ) + { +// Assert( state >= 0 && state < MAX_NUM_RENDERSTATES ); + RECORD_RENDER_STATE( state, val ); + if (ASyncMode()) + { + Push( PBCMD_SET_RENDERSTATE, state, val ); + } + else + DO_D3D( SetRenderState( state, val ) ); + } + + FORCEINLINE void SetRenderStateInline( D3DRENDERSTATETYPE state, DWORD val ) + { + // Assert( state >= 0 && state < MAX_NUM_RENDERSTATES ); + RECORD_RENDER_STATE( state, val ); + if (ASyncMode()) + { + SetRenderState( state, val ); + } + else + { +#ifdef DX_TO_GL_ABSTRACTION + DO_D3D( SetRenderStateInline( state, val ) ); +#else + DO_D3D( SetRenderState( state, val ) ); +#endif + } + } + + FORCEINLINE void SetScissorRect( const RECT *pScissorRect ) + { + RECORD_COMMAND( DX8_SET_SCISSOR_RECT, 1 ); + RECORD_STRUCT( pScissorRect, 4 * sizeof(LONG) ); +#if SHADERAPI_USE_SMP + if ( ASyncMode() ) + { + AllocatePushBufferSpace( 5 ); + m_pOutputPtr[0] = PBCMD_SET_SCISSOR_RECT; + memcpy( m_pOutputPtr + 1, pScissorRect, sizeof( *pScissorRect ) ); + } + else +#endif + DO_D3D( SetScissorRect( pScissorRect ) ); + } + + FORCEINLINE void SetVertexShaderConstantF( UINT StartRegister, CONST float * pConstantData, + UINT Vector4fCount) + { + RECORD_COMMAND( DX8_SET_VERTEX_SHADER_CONSTANT, 3 ); + RECORD_INT( StartRegister ); + RECORD_INT( Vector4fCount ); + RECORD_STRUCT( pConstantData, Vector4fCount * 4 * sizeof(float) ); +#if SHADERAPI_USE_SMP + if ( ASyncMode() ) + { + AllocatePushBufferSpace(3+4*Vector4fCount); + m_pOutputPtr[0]=PBCMD_SET_VERTEX_SHADER_CONSTANT; + m_pOutputPtr[1]=StartRegister; + m_pOutputPtr[2]=Vector4fCount; + memcpy(m_pOutputPtr+3,pConstantData,sizeof(float)*4*Vector4fCount); + m_pOutputPtr+=3+4*Vector4fCount; + } + else +#endif + DO_D3D( SetVertexShaderConstantF( StartRegister, pConstantData, Vector4fCount ) ); + } + + FORCEINLINE void SetVertexShaderConstantB( UINT StartRegister, CONST int * pConstantData, + UINT BoolCount) + { + RECORD_COMMAND( DX8_SET_VERTEX_SHADER_CONSTANT, 3 ); + RECORD_INT( StartRegister ); + RECORD_INT( BoolCount ); + RECORD_STRUCT( pConstantData, BoolCount * sizeof(int) ); +#if SHADERAPI_USE_SMP + if ( ASyncMode() ) + { + AllocatePushBufferSpace(3+BoolCount); + m_pOutputPtr[0]=PBCMD_SET_BOOLEAN_VERTEX_SHADER_CONSTANT; + m_pOutputPtr[1]=StartRegister; + m_pOutputPtr[2]=BoolCount; + memcpy(m_pOutputPtr+3,pConstantData,sizeof(int)*BoolCount); + m_pOutputPtr+=3+BoolCount; + } + else +#endif + DO_D3D( SetVertexShaderConstantB( StartRegister, pConstantData, BoolCount ) ); + } + + FORCEINLINE void SetVertexShaderConstantI( UINT StartRegister, CONST int * pConstantData, + UINT Vector4IntCount) + { + RECORD_COMMAND( DX8_SET_VERTEX_SHADER_CONSTANT, 3 ); + RECORD_INT( StartRegister ); + RECORD_INT( Vector4IntCount ); + RECORD_STRUCT( pConstantData, Vector4IntCount * 4 * sizeof(int) ); +#if SHADERAPI_USE_SMP + if ( ASyncMode() ) + { + AllocatePushBufferSpace(3+4*Vector4IntCount); + m_pOutputPtr[0]=PBCMD_SET_INTEGER_VERTEX_SHADER_CONSTANT; + m_pOutputPtr[1]=StartRegister; + m_pOutputPtr[2]=Vector4IntCount; + memcpy(m_pOutputPtr+3,pConstantData,sizeof(int)*4*Vector4IntCount); + m_pOutputPtr+=3+4*Vector4IntCount; + } + else +#endif + DO_D3D( SetVertexShaderConstantI( StartRegister, pConstantData, Vector4IntCount ) ); + } + + FORCEINLINE void SetPixelShaderConstantF( UINT StartRegister, CONST float * pConstantData, + UINT Vector4fCount) + { + RECORD_COMMAND( DX8_SET_PIXEL_SHADER_CONSTANT, 3 ); + RECORD_INT( StartRegister ); + RECORD_INT( Vector4fCount ); + RECORD_STRUCT( pConstantData, Vector4fCount * 4 * sizeof(float) ); +#if SHADERAPI_USE_SMP + if ( ASyncMode() ) + { + AllocatePushBufferSpace(3+4*Vector4fCount); + m_pOutputPtr[0]=PBCMD_SET_PIXEL_SHADER_CONSTANT; + m_pOutputPtr[1]=StartRegister; + m_pOutputPtr[2]=Vector4fCount; + memcpy(m_pOutputPtr+3,pConstantData,sizeof(float)*4*Vector4fCount); + m_pOutputPtr+=3+4*Vector4fCount; + } + else +#endif + DO_D3D( SetPixelShaderConstantF( StartRegister, pConstantData, Vector4fCount ) ); + } + + FORCEINLINE void SetPixelShaderConstantB( UINT StartRegister, CONST int * pConstantData, + UINT BoolCount) + { + RECORD_COMMAND( DX8_SET_PIXEL_SHADER_CONSTANT, 3 ); + RECORD_INT( StartRegister ); + RECORD_INT( BoolCount ); + RECORD_STRUCT( pConstantData, BoolCount * sizeof(int) ); +#if SHADERAPI_USE_SMP + if ( ASyncMode() ) + { + AllocatePushBufferSpace(3+BoolCount); + m_pOutputPtr[0]=PBCMD_SET_BOOLEAN_PIXEL_SHADER_CONSTANT; + m_pOutputPtr[1]=StartRegister; + m_pOutputPtr[2]=BoolCount; + memcpy(m_pOutputPtr+3,pConstantData,sizeof(int)*BoolCount); + m_pOutputPtr+=3+BoolCount; + } + else +#endif + DO_D3D( SetPixelShaderConstantB( StartRegister, pConstantData, BoolCount ) ); + } + + FORCEINLINE void SetPixelShaderConstantI( UINT StartRegister, CONST int * pConstantData, + UINT Vector4IntCount) + { + RECORD_COMMAND( DX8_SET_PIXEL_SHADER_CONSTANT, 3 ); + RECORD_INT( StartRegister ); + RECORD_INT( Vector4IntCount ); + RECORD_STRUCT( pConstantData, Vector4IntCount * 4 * sizeof(int) ); +#if SHADERAPI_USE_SMP + if ( ASyncMode() ) + { + AllocatePushBufferSpace(3+4*Vector4IntCount); + m_pOutputPtr[0]=PBCMD_SET_INTEGER_PIXEL_SHADER_CONSTANT; + m_pOutputPtr[1]=StartRegister; + m_pOutputPtr[2]=Vector4IntCount; + memcpy(m_pOutputPtr+3,pConstantData,sizeof(int)*4*Vector4IntCount); + m_pOutputPtr+=3+4*Vector4IntCount; + } + else +#endif + DO_D3D( SetPixelShaderConstantI( StartRegister, pConstantData, Vector4IntCount ) ); + } + + HRESULT StretchRect( IDirect3DSurface9 * pSourceSurface, + CONST RECT * pSourceRect, + IDirect3DSurface9 * pDestSurface, + CONST RECT * pDestRect, + D3DTEXTUREFILTERTYPE Filter ) + { +#if SHADERAPI_USE_SMP + if ( ASyncMode() ) + { + AllocatePushBufferSpace(1+1+1+N_DWORDS( RECT )+1+1+N_DWORDS( RECT ) + 1); + *(m_pOutputPtr++)=PBCMD_STRETCHRECT; + *(m_pOutputPtr++)=(int) pSourceSurface; + *(m_pOutputPtr++)=(pSourceRect != NULL); + if (pSourceRect) + { + memcpy(m_pOutputPtr,pSourceRect,sizeof(RECT)); + } + m_pOutputPtr+=N_DWORDS(RECT); + *(m_pOutputPtr++)=(int) pDestSurface; + *(m_pOutputPtr++)=(pDestRect != NULL); + if (pDestRect) + memcpy(m_pOutputPtr,pDestRect,sizeof(RECT)); + m_pOutputPtr+=N_DWORDS(RECT); + *(m_pOutputPtr++)=Filter; + return S_OK; // !bug! + } + else +#endif + return m_pD3DDevice-> + StretchRect( pSourceSurface, pSourceRect, pDestSurface, pDestRect, Filter ); + } + + + FORCEINLINE void BeginScene(void) + { + RECORD_COMMAND( DX8_BEGIN_SCENE, 0 ); + if ( ASyncMode() ) + Push( PBCMD_BEGIN_SCENE ); + else + DO_D3D( BeginScene() ); + } + + FORCEINLINE void EndScene(void) + { + RECORD_COMMAND( DX8_END_SCENE, 0 ); + if ( ASyncMode() ) + Push( PBCMD_END_SCENE ); + else + DO_D3D( EndScene() ); + } + + FORCEINLINE HRESULT Lock( IDirect3DVertexBuffer9* vb, size_t offset, size_t size, void **ptr, DWORD flags ) + { + Assert( size ); // lock size of 0 = unknown entire size of buffer = bad + Synchronize(); + + HRESULT hr = vb->Lock(offset, size, ptr, flags); + switch (hr) + { + case D3DERR_INVALIDCALL: + Warning( "D3DERR_INVALIDCALL - Vertex Buffer Lock Failed in %s on line %d(offset %d, size %d, flags 0x%x)\n", V_UnqualifiedFileName(__FILE__), __LINE__, offset, size, flags ); + break; + case D3DERR_DRIVERINTERNALERROR: + Warning( "D3DERR_DRIVERINTERNALERROR - Vertex Buffer Lock Failed in %s on line %d (offset %d, size %d, flags 0x%x)\n", V_UnqualifiedFileName(__FILE__), __LINE__, offset, size, flags ); + break; + case D3DERR_OUTOFVIDEOMEMORY: + Warning( "D3DERR_OUTOFVIDEOMEMORY - Vertex Buffer Lock Failed in %s on line %d (offset %d, size %d, flags 0x%x)\n", V_UnqualifiedFileName(__FILE__), __LINE__, offset, size, flags ); + break; + } + + return hr; + } + + + FORCEINLINE HRESULT Lock( IDirect3DVertexBuffer9* vb, size_t offset, size_t size, void **ptr, + DWORD flags, + LockedBufferContext *lb) + { + + HRESULT hr = D3D_OK; + + // asynchronous write-only dynamic vb lock + if ( ASyncMode() ) + { + AsynchronousLock( vb, offset, size, ptr, flags, lb ); + } + else + { + hr = vb->Lock(offset, size, ptr, flags); + switch (hr) + { + case D3DERR_INVALIDCALL: + Warning( "D3DERR_INVALIDCALL - Vertex Buffer Lock Failed in %s on line %d(offset %d, size %d, flags 0x%x)\n", V_UnqualifiedFileName(__FILE__), __LINE__, offset, size, flags ); + break; + case D3DERR_DRIVERINTERNALERROR: + Warning( "D3DERR_DRIVERINTERNALERROR - Vertex Buffer Lock Failed in %s on line %d (offset %d, size %d, flags 0x%x)\n", V_UnqualifiedFileName(__FILE__), __LINE__, offset, size, flags ); + break; + case D3DERR_OUTOFVIDEOMEMORY: + Warning( "D3DERR_OUTOFVIDEOMEMORY - Vertex Buffer Lock Failed in %s on line %d (offset %d, size %d, flags 0x%x)\n", V_UnqualifiedFileName(__FILE__), __LINE__, offset, size, flags ); + break; + } + } + + return hr; + } + + FORCEINLINE HRESULT Lock( IDirect3DIndexBuffer9* ib, size_t offset, size_t size, void **ptr, DWORD flags) + { + HRESULT hr = D3D_OK; + + Synchronize(); + + hr = ib->Lock(offset, size, ptr, flags); + switch (hr) + { + case D3DERR_INVALIDCALL: + Warning( "D3DERR_INVALIDCALL - Index Buffer Lock Failed in %s on line %d(offset %d, size %d, flags 0x%x)\n", V_UnqualifiedFileName(__FILE__), __LINE__, offset, size, flags ); + break; + case D3DERR_DRIVERINTERNALERROR: + Warning( "D3DERR_DRIVERINTERNALERROR - Index Buffer Lock Failed in %s on line %d (offset %d, size %d, flags 0x%x)\n", V_UnqualifiedFileName(__FILE__), __LINE__, offset, size, flags ); + break; + case D3DERR_OUTOFVIDEOMEMORY: + Warning( "D3DERR_OUTOFVIDEOMEMORY - Index Buffer Lock Failed in %s on line %d (offset %d, size %d, flags 0x%x)\n", V_UnqualifiedFileName(__FILE__), __LINE__, offset, size, flags ); + break; + } + + return hr; + } + + // asycnhronous lock of index buffer + FORCEINLINE HRESULT Lock( IDirect3DIndexBuffer9* ib, size_t offset, size_t size, void **ptr, DWORD flags, + LockedBufferContext * lb) + { + HRESULT hr = D3D_OK; + + if ( ASyncMode() ) + AsynchronousLock( ib, offset, size, ptr, flags, lb ); + else + { + hr = ib->Lock(offset, size, ptr, flags); + switch (hr) + { + case D3DERR_INVALIDCALL: + Warning( "D3DERR_INVALIDCALL - Index Buffer Lock Failed in %s on line %d(offset %d, size %d, flags 0x%x)\n", V_UnqualifiedFileName(__FILE__), __LINE__, offset, size, flags ); + break; + case D3DERR_DRIVERINTERNALERROR: + Warning( "D3DERR_DRIVERINTERNALERROR - Index Buffer Lock Failed in %s on line %d (offset %d, size %d, flags 0x%x)\n", V_UnqualifiedFileName(__FILE__), __LINE__, offset, size, flags ); + break; + case D3DERR_OUTOFVIDEOMEMORY: + Warning( "D3DERR_OUTOFVIDEOMEMORY - Index Buffer Lock Failed in %s on line %d (offset %d, size %d, flags 0x%x)\n", V_UnqualifiedFileName(__FILE__), __LINE__, offset, size, flags ); + break; + } + } + + return hr; + } + +#ifndef DX_TO_GL_ABSTRACTION + FORCEINLINE HRESULT UpdateSurface( IDirect3DSurface9* pSourceSurface, CONST RECT* pSourceRect, IDirect3DSurface9* pDestSurface, CONST POINT* pDestPoint ) + { + return m_pD3DDevice->UpdateSurface( pSourceSurface, pSourceRect, pDestSurface, pDestPoint ); + } +#endif + + void Release( IDirect3DIndexBuffer9* ib ) + { + Synchronize(); + ib->Release(); + } + + void Release( IDirect3DVertexBuffer9* vb ) + { + Synchronize(); + vb->Release(); + } + + FORCEINLINE void Unlock( IDirect3DVertexBuffer9* vb ) + { + // needed for d3d on pc only + if ( ASyncMode() ) + Push(PBCMD_UNLOCK_VB, vb); + else + { + HRESULT hr = vb->Unlock( ); + + if ( FAILED(hr) ) + { + Warning( "Vertex Buffer Unlock Failed in %s on line %d\n", V_UnqualifiedFileName(__FILE__), __LINE__ ); + } + } + } + + FORCEINLINE void Unlock( IDirect3DVertexBuffer9* vb, LockedBufferContext *lb, size_t unlock_size) + { + // needed for d3d on pc only +#if SHADERAPI_USE_SMP + if ( ASyncMode() ) + { + AllocatePushBufferSpace( 1+N_DWORDS_IN_PTR+N_DWORDS( LockedBufferContext )+1 ); + *(m_pOutputPtr++)=PBCMD_ASYNC_UNLOCK_VB; + *((IDirect3DVertexBuffer9* *) m_pOutputPtr)=vb; + m_pOutputPtr+=N_DWORDS_IN_PTR; + *((LockedBufferContext *) m_pOutputPtr)=*lb; + m_pOutputPtr+=N_DWORDS( LockedBufferContext ); + *(m_pOutputPtr++)=unlock_size; + } + else +#endif + { + HRESULT hr = vb->Unlock(); + + if ( FAILED(hr) ) + { + Warning( "Vertex Buffer Unlock Failed in %s on line %d\n", V_UnqualifiedFileName(__FILE__), __LINE__ ); + } + } + } + + FORCEINLINE void Unlock( IDirect3DIndexBuffer9* ib ) + { + // needed for d3d on pc only + if ( ASyncMode() ) + Push(PBCMD_UNLOCK_IB, ib); + else + { + HRESULT hr = ib->Unlock(); + + if ( FAILED(hr) ) + { + Warning( "Index Buffer Unlock Failed in %s on line %d\n", V_UnqualifiedFileName(__FILE__), __LINE__ ); + } + } + } + + FORCEINLINE void Unlock( IDirect3DIndexBuffer9* ib, LockedBufferContext *lb, size_t unlock_size) + { + // needed for d3d on pc only +#if SHADERAPI_USE_SMP + if ( ASyncMode() ) + { + AllocatePushBufferSpace( 1+N_DWORDS_IN_PTR+N_DWORDS( LockedBufferContext )+1 ); + *(m_pOutputPtr++)=PBCMD_ASYNC_UNLOCK_IB; + *((IDirect3DIndexBuffer9* *) m_pOutputPtr)=ib; + m_pOutputPtr+=N_DWORDS_IN_PTR; + *((LockedBufferContext *) m_pOutputPtr)=*lb; + m_pOutputPtr+=N_DWORDS( LockedBufferContext ); + *(m_pOutputPtr++)=unlock_size; + } + else +#endif + { + HRESULT hr = ib->Unlock( ); + + if ( FAILED(hr) ) + { + Warning( "Index Buffer Unlock Failed in %s on line %d\n", V_UnqualifiedFileName(__FILE__), __LINE__ ); + } + } + } + + void ShowCursor( bool onoff) + { + Synchronize(); + DO_D3D( ShowCursor(onoff) ); + } + + FORCEINLINE void Clear( int count, D3DRECT const *pRects, int Flags, D3DCOLOR color, float Z, int stencil) + { +#if SHADERAPI_USE_SMP + if ( ASyncMode() ) + { + int n_rects_words = count * N_DWORDS( D3DRECT ); + AllocatePushBufferSpace( 2 + n_rects_words + 4 ); + *(m_pOutputPtr++) = PBCMD_CLEAR; + *(m_pOutputPtr++) = count; + if ( count ) + { + memcpy( m_pOutputPtr, pRects, count * sizeof( D3DRECT ) ); + m_pOutputPtr += n_rects_words; + } + *(m_pOutputPtr++) = Flags; + *( (D3DCOLOR *) m_pOutputPtr ) = color; + m_pOutputPtr++; + *( (float *) m_pOutputPtr ) = Z; + m_pOutputPtr++; + *(m_pOutputPtr++) = stencil; + } + else +#endif + DO_D3D( Clear(count, pRects, Flags, color, Z, stencil) ); + } + + HRESULT Reset( D3DPRESENT_PARAMETERS *parms) + { + RECORD_COMMAND( DX8_RESET, 1 ); + RECORD_STRUCT( parms, sizeof(*parms) ); + Synchronize(); + return m_pD3DDevice->Reset( parms ); + } + + void Release( void ) + { + Synchronize(); + DO_D3D( Release() ); + } + + FORCEINLINE void SetTexture(int stage, IDirect3DBaseTexture9 *txtr) + { + RECORD_COMMAND( DX8_SET_TEXTURE, 3 ); + RECORD_INT( stage ); + RECORD_INT( -1 ); + RECORD_INT( -1 ); + if (ASyncMode()) + { + Push( PBCMD_SET_TEXTURE, stage, txtr ); + } + else + DO_D3D( SetTexture( stage, txtr) ); + } + void SetTransform( D3DTRANSFORMSTATETYPE mtrx_id, D3DXMATRIX const *mt) + { + RECORD_COMMAND( DX8_SET_TRANSFORM, 2 ); + RECORD_INT( mtrx_id ); + RECORD_STRUCT( mt, sizeof(D3DXMATRIX) ); + Synchronize(); + DO_D3D( SetTransform( mtrx_id, mt) ); + } + + FORCEINLINE void SetSamplerState( int stage, D3DSAMPLERSTATETYPE state, DWORD val) + { + RECORD_SAMPLER_STATE( stage, state, val ); + if ( ASyncMode() ) + Push( PBCMD_SET_SAMPLER_STATE, stage, state, val ); + else + DO_D3D( SetSamplerState( stage, state, val) ); + } + + void SetFVF( int fvf) + { + Synchronize(); + DO_D3D( SetFVF( fvf) ); + } + + FORCEINLINE void SetTextureStageState( int stage, D3DTEXTURESTAGESTATETYPE state, DWORD val ) + { + RECORD_TEXTURE_STAGE_STATE( stage, state, val ); + Synchronize(); + DO_D3D( SetTextureStageState( stage, state, val) ); + } + + FORCEINLINE void DrawPrimitive( + D3DPRIMITIVETYPE PrimitiveType, + UINT StartVertex, + UINT PrimitiveCount + ) + { + RECORD_COMMAND( DX8_DRAW_PRIMITIVE, 3 ); + RECORD_INT( PrimitiveType ); + RECORD_INT( StartVertex ); + RECORD_INT( PrimitiveCount ); + if ( ASyncMode() ) + { + Push( PBCMD_DRAWPRIM, PrimitiveType, StartVertex, PrimitiveCount ); + SubmitIfNotBusy(); + } + else + DO_D3D( DrawPrimitive( PrimitiveType, StartVertex, PrimitiveCount ) ); + + } + + HRESULT CreateVertexDeclaration( + CONST D3DVERTEXELEMENT9* pVertexElements, + IDirect3DVertexDeclaration9** ppDecl + ) + { + Synchronize(); + return m_pD3DDevice->CreateVertexDeclaration( pVertexElements, ppDecl ); + } + + + HRESULT ValidateDevice( DWORD * pNumPasses ) + { + Synchronize(); + return m_pD3DDevice->ValidateDevice( pNumPasses ); + } + + + + HRESULT CreateVertexShader( + CONST DWORD * pFunction, + IDirect3DVertexShader9** ppShader, + const char *pShaderName, + char *debugLabel = NULL + ) + { + Synchronize(); + #ifdef DX_TO_GL_ABSTRACTION + return m_pD3DDevice->CreateVertexShader( pFunction, ppShader, pShaderName, debugLabel ); + #else + return m_pD3DDevice->CreateVertexShader( pFunction, ppShader ); + #endif + } + + HRESULT CreatePixelShader( + CONST DWORD * pFunction, + IDirect3DPixelShader9** ppShader, + const char *pShaderName, + char *debugLabel = NULL + ) + { + Synchronize(); + #ifdef DX_TO_GL_ABSTRACTION + return m_pD3DDevice->CreatePixelShader( pFunction, ppShader, pShaderName, debugLabel ); + #else + return m_pD3DDevice->CreatePixelShader( pFunction, ppShader ); + #endif + } + + + FORCEINLINE void SetIndices( + IDirect3DIndexBuffer9 * pIndexData + ) + { + if ( ASyncMode() ) + Push( PBCMD_SET_INDICES, pIndexData ); + else + DO_D3D( SetIndices( pIndexData ) ); + } + + FORCEINLINE void SetStreamSource( + UINT StreamNumber, + IDirect3DVertexBuffer9 * pStreamData, + UINT OffsetInBytes, + UINT Stride + ) + { + if ( ASyncMode() ) + Push( PBCMD_SET_STREAM_SOURCE, StreamNumber, pStreamData, OffsetInBytes, Stride ); + else + DO_D3D( SetStreamSource( StreamNumber, pStreamData, OffsetInBytes, Stride ) ); + } + + + HRESULT CreateVertexBuffer( + UINT Length, + DWORD Usage, + DWORD FVF, + D3DPOOL Pool, + IDirect3DVertexBuffer9** ppVertexBuffer, + HANDLE* pSharedHandle + ) + { + Synchronize(); + return m_pD3DDevice->CreateVertexBuffer( Length, Usage, FVF, + Pool, ppVertexBuffer, pSharedHandle ); + } + + HRESULT CreateIndexBuffer( + UINT Length, + DWORD Usage, + D3DFORMAT Format, + D3DPOOL Pool, + IDirect3DIndexBuffer9** ppIndexBuffer, + HANDLE* pSharedHandle + ) + { + Synchronize(); + return m_pD3DDevice->CreateIndexBuffer( Length, Usage, Format, Pool, ppIndexBuffer, + pSharedHandle ); + } + + + FORCEINLINE void DrawIndexedPrimitive( + D3DPRIMITIVETYPE Type, + INT BaseVertexIndex, + UINT MinIndex, + UINT NumVertices, + UINT StartIndex, + UINT PrimitiveCount ) + { + RECORD_COMMAND( DX8_DRAW_INDEXED_PRIMITIVE, 6 ); + RECORD_INT( Type ); + RECORD_INT( BaseVertexIndex ); + RECORD_INT( MinIndex ); + RECORD_INT( NumVertices ); + RECORD_INT( StartIndex ); + RECORD_INT( PrimitiveCount ); + if ( ASyncMode() ) + { + Push(PBCMD_DRAWINDEXEDPRIM, + Type, BaseVertexIndex, MinIndex, NumVertices, StartIndex, PrimitiveCount ); +// SubmitIfNotBusy(); + } + else + { + DO_D3D( DrawIndexedPrimitive( Type, BaseVertexIndex, MinIndex, NumVertices, StartIndex, PrimitiveCount ) ); + } + } + +#ifndef DX_TO_GL_ABSTRACTION + FORCEINLINE void DrawTessellatedIndexedPrimitive( INT BaseVertexIndex, UINT MinIndex, UINT NumVertices, + UINT StartIndex, UINT PrimitiveCount ) + { + // Setup our stream-source frequencies + DO_D3D( SetStreamSourceFreq( 0, D3DSTREAMSOURCE_INDEXEDDATA | PrimitiveCount ) ); + DO_D3D( SetStreamSourceFreq( VertexStreamSpec_t::STREAM_MORPH, D3DSTREAMSOURCE_INSTANCEDATA | 1ul ) ); + DO_D3D( SetStreamSourceFreq( VertexStreamSpec_t::STREAM_SUBDQUADS, D3DSTREAMSOURCE_INSTANCEDATA | 1ul ) ); + + int nIndicesPerPatch = ( ( ( m_nCurrentTessLevel + 1 ) * 2 + 2 ) * m_nCurrentTessLevel ) - 2; + int nVerticesPerPatch = m_nCurrentTessLevel + 1; + nVerticesPerPatch *= nVerticesPerPatch; + int nPrimitiveCount = nIndicesPerPatch - 2; + DO_D3D( DrawIndexedPrimitive( D3DPT_TRIANGLESTRIP, 0, 0, nVerticesPerPatch, 0, nPrimitiveCount ) ); + + // Disable instancing + DO_D3D( SetStreamSourceFreq( 0, 1ul ) ); + DO_D3D( SetStreamSourceFreq( VertexStreamSpec_t::STREAM_MORPH, 1ul ) ); + DO_D3D( SetStreamSourceFreq( VertexStreamSpec_t::STREAM_SUBDQUADS, 1ul ) ); + } + + FORCEINLINE void DrawTessellatedPrimitive( UINT StartVertex, UINT PrimitiveCount ) + { + + // Setup our stream-source frequencies + DO_D3D( SetStreamSourceFreq( 0, D3DSTREAMSOURCE_INDEXEDDATA | PrimitiveCount ) ); + DO_D3D( SetStreamSourceFreq( VertexStreamSpec_t::STREAM_MORPH, D3DSTREAMSOURCE_INSTANCEDATA | 1ul ) ); + DO_D3D( SetStreamSourceFreq( VertexStreamSpec_t::STREAM_SUBDQUADS, D3DSTREAMSOURCE_INSTANCEDATA | 1ul ) ); + + int nIndicesPerPatch = ( ( ( m_nCurrentTessLevel + 1 ) * 2 + 2 ) * m_nCurrentTessLevel ) - 2; + int nVerticesPerPatch = m_nCurrentTessLevel + 1; + nVerticesPerPatch *= nVerticesPerPatch; + int nPrimitiveCount = nIndicesPerPatch - 2; + DO_D3D( DrawIndexedPrimitive( D3DPT_TRIANGLESTRIP, 0, 0, nVerticesPerPatch, 0, nPrimitiveCount ) ); + + // Disable instancing + DO_D3D( SetStreamSourceFreq( 0, 1ul ) ); + DO_D3D( SetStreamSourceFreq( VertexStreamSpec_t::STREAM_MORPH, 1ul ) ); + DO_D3D( SetStreamSourceFreq( VertexStreamSpec_t::STREAM_SUBDQUADS, 1ul ) ); + } + + FORCEINLINE void SetTessellationLevel( float level ) + { + // Track our current tessellation level + m_nCurrentTessLevel = (int)ceil( level ); + } +#endif + + void SetMaterial( D3DMATERIAL9 const *mat) + { + RECORD_COMMAND( DX8_SET_MATERIAL, 1 ); + RECORD_STRUCT( &mat, sizeof(mat) ); + Synchronize(); + DO_D3D( SetMaterial( mat ) ); + } + + FORCEINLINE void SetPixelShader( IDirect3DPixelShader9 *pShader ) + { + RECORD_COMMAND( DX8_SET_PIXEL_SHADER, 1 ); + RECORD_INT( ( int ) pShader ); + if ( ASyncMode() ) + Push( PBCMD_SET_PIXEL_SHADER, pShader ); + else + DO_D3D( SetPixelShader( pShader ) ); + } + + FORCEINLINE void SetVertexShader( IDirect3DVertexShader9 *pShader ) + { + if ( ASyncMode() ) + Push( PBCMD_SET_VERTEX_SHADER, pShader ); + else + DO_D3D( SetVertexShader( pShader ) ); + } + +#ifdef DX_TO_GL_ABSTRACTION + FORCEINLINE HRESULT LinkShaderPair( IDirect3DVertexShader9* vs, IDirect3DPixelShader9* ps ) + { + Assert ( !ASyncMode() ); + return DO_D3D( LinkShaderPair( vs, ps ) ); + } + + HRESULT QueryShaderPair( int index, GLMShaderPairInfo *infoOut ) + { + Assert ( !ASyncMode() ); + return DO_D3D( QueryShaderPair( index, infoOut ) ); + } + + void SetMaxUsedVertexShaderConstantsHint( uint nMaxReg ) + { + Assert( !ASyncMode() ); + DO_D3D( SetMaxUsedVertexShaderConstantsHint( nMaxReg ) ); + } + +#endif + + void EvictManagedResources( void ) + { + if (m_pD3DDevice) // people call this before creating the device + { + Synchronize(); + DO_D3D( EvictManagedResources() ); + } + } + + void SetLight( int i, D3DLIGHT9 const *l) + { + RECORD_COMMAND( DX8_SET_LIGHT, 2 ); + RECORD_INT( i ); + RECORD_STRUCT( l, sizeof(*l) ); + + Synchronize(); + DO_D3D( SetLight(i, l) ); + } + + void DrawIndexedPrimitiveUP( D3DPRIMITIVETYPE PrimitiveType, + UINT MinVertexIndex, + UINT NumVertices, + UINT PrimitiveCount, + CONST void * pIndexData, + D3DFORMAT IndexDataFormat, + CONST void* pVertexStreamZeroData, + UINT VertexStreamZeroStride ) + { + Synchronize(); + DO_D3D( DrawIndexedPrimitiveUP( PrimitiveType, MinVertexIndex, NumVertices, PrimitiveCount, + pIndexData, IndexDataFormat, pVertexStreamZeroData, + VertexStreamZeroStride ) ); + } + + HRESULT Present( + CONST RECT * pSourceRect, + CONST RECT * pDestRect, + VD3DHWND hDestWindowOverride, + CONST RGNDATA * pDirtyRegion) + { + RECORD_COMMAND( DX8_PRESENT, 0 ); +#if SHADERAPI_USE_SMP + if ( ASyncMode() ) + { + // need to deal with ret code here + AllocatePushBufferSpace(1+1+ + N_DWORDS( RECT )+1+N_DWORDS( RECT )+1+1+N_DWORDS( RGNDATA )); + *(m_pOutputPtr++)=PBCMD_PRESENT; + *(m_pOutputPtr++)=( pSourceRect != NULL ); + if (pSourceRect) + memcpy(m_pOutputPtr, pSourceRect, sizeof( RECT ) ); + m_pOutputPtr+=N_DWORDS( RECT ); + *(m_pOutputPtr++)=( pDestRect != NULL ); + if (pDestRect) + memcpy(m_pOutputPtr, pDestRect, sizeof( RECT ) ); + m_pOutputPtr+=N_DWORDS( RECT ); + *(m_pOutputPtr++)=(uint32) hDestWindowOverride; + *(m_pOutputPtr++)=( pDirtyRegion != NULL ); + if (pDirtyRegion) + memcpy(m_pOutputPtr, pDirtyRegion, sizeof( RGNDATA )); + m_pOutputPtr+=N_DWORDS( RGNDATA ); + return S_OK; // not good - caller wants to here about lost devices + } + else +#endif + return m_pD3DDevice->Present( pSourceRect, pDestRect, + hDestWindowOverride, pDirtyRegion ); + } + + +#if defined( DX_TO_GL_ABSTRACTION ) + + void AcquireThreadOwnership( ) + { + m_pD3DDevice->AcquireThreadOwnership(); + } + + void ReleaseThreadOwnership( ) + { + m_pD3DDevice->ReleaseThreadOwnership(); + } + +#endif + +}; + +#endif // D3DASYNC_H + +#endif // #if D3D_ASYNC_SUPPORTED diff --git a/materialsystem/shaderapidx9/dx9hook.h b/materialsystem/shaderapidx9/dx9hook.h new file mode 100644 index 0000000..a117f3e --- /dev/null +++ b/materialsystem/shaderapidx9/dx9hook.h @@ -0,0 +1,1289 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +/* +TODO: add option to null out drawprim calls. + +Also maybe hook the PIX_ENABLE stuff to Telemetry + + In imaterialsystem.h: + #define PIX_ENABLE 1 // set this to 1 and build engine/studiorender to enable pix events in the engine + + And in shaderapidx8.h: + #define PIX_ENABLE 1 // set this to 1 and build engine/studiorender to enable pix events in the engine + +Might be interesting to make it so dx9hook.h paid attention to the PIX_ENABLE names + and allowed you to filter drawprim calls based on those? +*/ + +#ifndef _DX9HOOK_H_ +#define _DX9HOOK_H_ + +#if D3D_BATCH_PERF_ANALYSIS + #include "../../thirdparty/miniz/miniz.c" + #include "../../thirdparty/miniz/simple_bitmap.h" + + extern ConVar d3d_batch_vis, d3d_batch_vis_abs_scale, d3d_batch_vis_y_scale, d3d_present_vis_abs_scale; + extern uint64 g_nTotalD3DCalls, g_nTotalD3DCycles; + + class CD3DCallTimer + { + public: + inline CD3DCallTimer() { g_nTotalD3DCalls++; g_nTotalD3DCycles -= tmFastTime(); } + inline ~CD3DCallTimer() { g_nTotalD3DCycles += tmFastTime(); } + }; + + #define D3D_BATCH_PERF(...) __VA_ARGS__ +#else + #define D3D_BATCH_PERF(...) +#endif + +#if D3D_BATCH_PERF_ANALYSIS + #define XXX \ + tmZone( TELEMETRY_LEVEL3, TMZF_NONE, "D3D9: %s", __FUNCTION__ ); \ + CD3DCallTimer scopedCallTimer; +#else + #define XXX \ + if( ThreadInMainThread() ) \ + { \ + tmMessage( TELEMETRY_LEVEL0, TMMF_ICON_NOTE | TMMF_SEVERITY_WARNING, "(source/d3d)%s", __FUNCTION__ ); \ + tmZoneFiltered( TELEMETRY_LEVEL0, 50, TMZF_NONE, "%s", __FUNCTION__ ); \ + } +#endif + +// Hooks for routines which return values. +#define _DOCALL0( _member ) ( m_Data.pHWObj->_member )() +#define _DOCALL( _member, ... ) ( m_Data.pHWObj->_member )( __VA_ARGS__ ) +// And hooks for routines which return squatola. +#define _DOCALL0_NORET( _member ) ( m_Data.pHWObj->_member )() +#define _DOCALL_NORET( _member, ... ) ( m_Data.pHWObj->_member )( __VA_ARGS__) + +#define DEF_HOOKCLASSES( X ) \ + template<> class CDx9HookBase< struct I ## X > \ + { \ + public: \ + typedef I ## X _D3DINTERFACE; \ + typedef class C ## X ## Hook _HOOKCLASS; \ + \ + public: \ + CDx9HookBase() { memset( &m_Data, 0, sizeof( m_Data ) ); } \ + virtual ~CDx9HookBase() {} \ + \ + public: \ + struct DATA \ + { \ + class CDirect3DDevice9Hook *pDevice; \ + _D3DINTERFACE *pHWObj; \ + }; \ + \ + DATA m_Data; \ + } + +template < class T > class CDx9HookBase; +DEF_HOOKCLASSES( Direct3DVertexDeclaration9 ); +DEF_HOOKCLASSES( Direct3DPixelShader9 ); +DEF_HOOKCLASSES( Direct3DVertexShader9 ); +DEF_HOOKCLASSES( Direct3DVertexBuffer9 ); +DEF_HOOKCLASSES( Direct3DIndexBuffer9 ); +DEF_HOOKCLASSES( Direct3DQuery9 ); +DEF_HOOKCLASSES( Direct3DStateBlock9 ); +DEF_HOOKCLASSES( Direct3DSurface9 ); +DEF_HOOKCLASSES( Direct3DBaseTexture9 ); +DEF_HOOKCLASSES( Direct3DTexture9 ); +DEF_HOOKCLASSES( Direct3DCubeTexture9 ); +DEF_HOOKCLASSES( Direct3DVolume9 ); +DEF_HOOKCLASSES( Direct3DVolumeTexture9 ); +DEF_HOOKCLASSES( Direct3DSwapChain9 ); +DEF_HOOKCLASSES( Direct3D9 ); +DEF_HOOKCLASSES( Direct3DDevice9 ); + +template < class _D3DINTERFACE > HRESULT AllocOverride( HRESULT *hr, class CDirect3DDevice9Hook *pDevice, _D3DINTERFACE **ppHWObj ) +{ + if( SUCCEEDED(*hr) && ppHWObj && *ppHWObj ) + { + CDx9HookBase< _D3DINTERFACE >::_HOOKCLASS *pClass = new CDx9HookBase< _D3DINTERFACE >::_HOOKCLASS; + + if(!pClass) + { + ( *ppHWObj )->Release(); + *hr = E_OUTOFMEMORY; + return NULL; + } + pClass->m_Data.pDevice = pDevice; + pClass->m_Data.pHWObj = *ppHWObj; + *ppHWObj = pClass; + return *hr; + } + + return *hr; +} + +template _D3DINTERFACE *GetHWPtr( _D3DINTERFACE *pD3DInterface ) +{ + if( pD3DInterface ) + { + CDx9HookBase< _D3DINTERFACE >::_HOOKCLASS *pClass = ( CDx9HookBase< _D3DINTERFACE >::_HOOKCLASS * )pD3DInterface; + return pClass->m_Data.pHWObj; + } + return NULL; +} + +template CDirect3DDevice9Hook *GetHookDevice( _D3DINTERFACE *pD3DInterface ) +{ + if( pD3DInterface ) + { + CDx9HookBase< _D3DINTERFACE >::_HOOKCLASS *pClass = ( CDx9HookBase< _D3DINTERFACE >::_HOOKCLASS * )pD3DInterface; + return pClass->m_Data.pDevice; + } + return NULL; +} + +//$ TODO: if(riid == IID_IDirect3DDevice9Ex, IID_IDirect3DDevice9, etc. +#define IMPL_QUERYINTERFACE() \ + STDMETHOD(QueryInterface)(THIS_ REFIID riid, void** ppvObj) \ + { \ + __debugbreak(); \ + XXX; return _DOCALL(QueryInterface, riid, ppvObj); \ + } + +#define IMPL_IUNKOWN() \ + IMPL_QUERYINTERFACE(); \ + STDMETHOD_(ULONG,AddRef)(THIS) \ + { XXX; return _DOCALL0(AddRef); } \ + STDMETHOD_(ULONG,Release)(THIS) \ + { \ + XXX; \ + ULONG retval = _DOCALL0(Release); \ + if(retval == 0) \ + delete this; \ + return retval; \ + } + +#define IMPL_GETDEVICE() \ + STDMETHOD(GetDevice)(THIS_ IDirect3DDevice9** ppDevice) \ + { \ + XXX; \ + HRESULT hr = _DOCALL( GetDevice, ppDevice ); \ + return AllocOverride( &hr, m_Data.pDevice, ppDevice ); \ + } + +#define IMPL_SETPRIVATEDATA() \ + STDMETHOD(SetPrivateData)(THIS_ REFGUID refguid,CONST void* pData,DWORD SizeOfData,DWORD Flags) \ + { XXX; return _DOCALL(SetPrivateData, refguid, pData, SizeOfData, Flags); } + +#define IMPL_GETPRIVATEDATA() \ + STDMETHOD(GetPrivateData)(THIS_ REFGUID refguid,void* pData,DWORD* pSizeOfData) \ + { XXX; return _DOCALL(GetPrivateData, refguid, pData, pSizeOfData); } + +#define IMPL_FREEPRIVATEDATA() \ + STDMETHOD(FreePrivateData)(THIS_ REFGUID refguid) \ + { XXX; return _DOCALL(FreePrivateData, refguid); } + +#define IMPL_IDIRECT3DRESOURCE9() \ + IMPL_GETDEVICE(); \ + IMPL_SETPRIVATEDATA(); \ + IMPL_GETPRIVATEDATA(); \ + IMPL_FREEPRIVATEDATA(); \ + STDMETHOD_(DWORD, SetPriority)(THIS_ DWORD PriorityNew) \ + { XXX; return _DOCALL(SetPriority, PriorityNew); } \ + STDMETHOD_(DWORD, GetPriority)(THIS) \ + { XXX; return _DOCALL0(GetPriority); } \ + STDMETHOD_(void, PreLoad)(THIS) \ + { XXX; _DOCALL0_NORET(PreLoad); } \ + STDMETHOD_(D3DRESOURCETYPE, GetType)(THIS) \ + { XXX; return _DOCALL0(GetType); } + +class CDirect3DSwapChain9Hook : public CDx9HookBase, public IDirect3DSwapChain9 +{ +public: + /*** IUnknown methods ***/ + IMPL_IUNKOWN(); + + /*** IDirect3DSwapChain9 methods ***/ + STDMETHOD(Present)(THIS_ CONST RECT* pSourceRect,CONST RECT* pDestRect,HWND hDestWindowOverride,CONST RGNDATA* pDirtyRegion,DWORD dwFlags) + { XXX; return _DOCALL(Present, pSourceRect, pDestRect, hDestWindowOverride, pDirtyRegion, dwFlags); } + + STDMETHOD(GetFrontBufferData)(THIS_ IDirect3DSurface9* pDestSurface) + { + XXX; return _DOCALL(GetFrontBufferData, GetHWPtr(pDestSurface)); + } + STDMETHOD(GetBackBuffer)(THIS_ UINT iBackBuffer,D3DBACKBUFFER_TYPE Type,IDirect3DSurface9** ppBackBuffer) + { + XXX; + HRESULT hr = _DOCALL(GetBackBuffer, iBackBuffer, Type, ppBackBuffer); + return AllocOverride( &hr, m_Data.pDevice, ppBackBuffer ); + } + + STDMETHOD(GetRasterStatus)(THIS_ D3DRASTER_STATUS* pRasterStatus) + { XXX; return _DOCALL(GetRasterStatus, pRasterStatus); } + STDMETHOD(GetDisplayMode)(THIS_ D3DDISPLAYMODE* pMode) + { XXX; return _DOCALL(GetDisplayMode, pMode); } + + IMPL_GETDEVICE(); + + STDMETHOD(GetPresentParameters)(THIS_ D3DPRESENT_PARAMETERS* pPresentationParameters) + { XXX; return _DOCALL(GetPresentParameters, pPresentationParameters); } +}; + +class CDirect3DVertexDeclaration9Hook : public CDx9HookBase, public IDirect3DVertexDeclaration9 +{ +public: + /*** IUnknown methods ***/ + IMPL_IUNKOWN(); + + // IDirect3DVertexDeclaration9 methods + IMPL_GETDEVICE(); + + STDMETHOD(GetDeclaration)(THIS_ D3DVERTEXELEMENT9 *pVertElem, UINT* pNumElements) + { XXX; return _DOCALL(GetDeclaration, pVertElem, pNumElements); } +}; + +class CDirect3DPixelShader9Hook : public CDx9HookBase, public IDirect3DPixelShader9 +{ +public: + /*** IUnknown methods ***/ + IMPL_IUNKOWN(); + + // IDirect3DPixelShader9 methods + IMPL_GETDEVICE(); + + STDMETHOD(GetFunction)(THIS_ void *pData,UINT* pSizeOfData) + { XXX; return _DOCALL(GetFunction, pData, pSizeOfData); } +}; + +class CDirect3DVertexShader9Hook : public CDx9HookBase, public IDirect3DVertexShader9 +{ +public: + /*** IUnknown methods ***/ + IMPL_IUNKOWN(); + + // IDirect3DVertexDeclaration9 methods + IMPL_GETDEVICE(); + + STDMETHOD(GetFunction)(THIS_ void *pData,UINT* pSizeOfData) + { XXX; return _DOCALL(GetFunction, pData, pSizeOfData); } +}; + +class CDirect3DVertexBuffer9Hook : public CDx9HookBase, public IDirect3DVertexBuffer9 +{ +public: + /*** IUnknown methods ***/ + IMPL_IUNKOWN(); + + // IDirect3DResource9 methods + IMPL_IDIRECT3DRESOURCE9(); + + // IDirect3DVertexBuffer9 + STDMETHOD(Lock)(THIS_ UINT OffsetToLock,UINT SizeToLock,void** ppbData,DWORD Flags) + { XXX; return _DOCALL(Lock, OffsetToLock, SizeToLock, ppbData, Flags); } + STDMETHOD(Unlock)(THIS) + { XXX; return _DOCALL0(Unlock); } + + STDMETHOD(GetDesc)(THIS_ D3DVERTEXBUFFER_DESC *pDesc) + { XXX; return _DOCALL(GetDesc, pDesc); } +}; + +class CDirect3DIndexBuffer9Hook : public CDx9HookBase, public IDirect3DIndexBuffer9 +{ +public: + /*** IUnknown methods ***/ + IMPL_IUNKOWN(); + + // IDirect3DResource9 methods + IMPL_IDIRECT3DRESOURCE9(); + + // IDirect3DIndexBuffer9 + STDMETHOD(Lock)(THIS_ UINT OffsetToLock,UINT SizeToLock,void** ppbData,DWORD Flags) + { XXX; return _DOCALL(Lock, OffsetToLock, SizeToLock, ppbData, Flags); } + STDMETHOD(Unlock)(THIS) + { XXX; return _DOCALL0(Unlock); } + + STDMETHOD(GetDesc)(THIS_ D3DINDEXBUFFER_DESC *pDesc) + { XXX; return _DOCALL(GetDesc, pDesc); } +}; + +class CDirect3DQuery9Hook : public CDx9HookBase, public IDirect3DQuery9 +{ +public: + /*** IUnknown methods ***/ + IMPL_IUNKOWN(); + + /*** IDirect3DQuery9 methods ***/ + IMPL_GETDEVICE(); + + // IDirect3DQuery9 + STDMETHOD_(D3DQUERYTYPE, GetType)(THIS) + { XXX; return _DOCALL0(GetType); } + STDMETHOD_(DWORD, GetDataSize)(THIS) + { XXX; return _DOCALL0(GetDataSize); } + STDMETHOD(Issue)(THIS_ DWORD dwIssueFlags) + { XXX; return _DOCALL(Issue, dwIssueFlags); } + STDMETHOD(GetData)(THIS_ void* pData,DWORD dwSize,DWORD dwGetDataFlags) + { XXX; return _DOCALL(GetData, pData, dwSize, dwGetDataFlags); } +}; + +class CDirect3DStateBlock9Hook : public CDx9HookBase, public IDirect3DStateBlock9 +{ +public: + CDirect3DStateBlock9Hook() {} + virtual ~CDirect3DStateBlock9Hook() {} + + /*** IUnknown methods ***/ + IMPL_IUNKOWN(); + + // IDirect3DStateBlock9 methods + IMPL_GETDEVICE(); + + STDMETHOD(Capture)(THIS) + { XXX; return _DOCALL0(Capture); } + STDMETHOD(Apply)(THIS) + { XXX; return _DOCALL0(Apply); } +}; + +class CDirect3DSurface9Hook : public CDx9HookBase, public IDirect3DSurface9 +{ +public: + /*** IUnknown methods ***/ + IMPL_IUNKOWN(); + + // IDirect3DResource9 methods + IMPL_IDIRECT3DRESOURCE9(); + + STDMETHOD(GetContainer)(THIS_ REFIID riid,void** ppContainer) + { + //$ TODO: do the call, check riid, and wrap the returned ppContainer + __debugbreak(); + XXX; return _DOCALL(GetContainer, riid, ppContainer); + } + STDMETHOD(GetDesc)(THIS_ D3DSURFACE_DESC *pDesc) + { XXX; return _DOCALL(GetDesc, pDesc); } + + STDMETHOD(LockRect)(THIS_ D3DLOCKED_RECT* pLockedRect,CONST RECT* pRect,DWORD Flags) + { XXX; return _DOCALL(LockRect, pLockedRect, pRect, Flags); } + STDMETHOD(UnlockRect)(THIS) + { XXX; return _DOCALL0(UnlockRect); } + + STDMETHOD(GetDC)(THIS_ HDC *phdc) + { XXX; return _DOCALL(GetDC, phdc); } + STDMETHOD(ReleaseDC)(THIS_ HDC hdc) + { XXX; return _DOCALL(ReleaseDC, hdc); } +}; + +class CDirect3DBaseTexture9Hook : public CDx9HookBase, public IDirect3DBaseTexture9 +{ +public: + /*** IUnknown methods ***/ + IMPL_IUNKOWN(); + + /*** IDirect3DBaseTexture9 methods ***/ + IMPL_IDIRECT3DRESOURCE9(); + + STDMETHOD_(DWORD, SetLOD)(THIS_ DWORD LODNew) + { XXX; return _DOCALL(SetLOD, LODNew); } + STDMETHOD_(DWORD, GetLOD)(THIS) + { XXX; return _DOCALL0(GetLOD); } + STDMETHOD_(DWORD, GetLevelCount)(THIS) + { XXX; return _DOCALL0(GetLevelCount); } + STDMETHOD(SetAutoGenFilterType)(THIS_ D3DTEXTUREFILTERTYPE FilterType) + { XXX; return _DOCALL(SetAutoGenFilterType, FilterType); } + STDMETHOD_(D3DTEXTUREFILTERTYPE, GetAutoGenFilterType)(THIS) + { XXX; return _DOCALL0(GetAutoGenFilterType); } + STDMETHOD_(void, GenerateMipSubLevels)(THIS) + { XXX; return _DOCALL0(GenerateMipSubLevels); } +}; + +class CDirect3DTexture9Hook : public CDx9HookBase, public IDirect3DTexture9 +{ +public: + /*** IUnknown methods ***/ + IMPL_IUNKOWN(); + + /*** IDirect3DBaseTexture9 methods ***/ + IMPL_IDIRECT3DRESOURCE9(); + + STDMETHOD_(DWORD, SetLOD)(THIS_ DWORD LODNew) + { XXX; return _DOCALL(SetLOD, LODNew); } + STDMETHOD_(DWORD, GetLOD)(THIS) + { XXX; return _DOCALL0(GetLOD); } + STDMETHOD_(DWORD, GetLevelCount)(THIS) + { XXX; return _DOCALL0(GetLevelCount); } + STDMETHOD(SetAutoGenFilterType)(THIS_ D3DTEXTUREFILTERTYPE FilterType) + { XXX; return _DOCALL(SetAutoGenFilterType, FilterType); } + STDMETHOD_(D3DTEXTUREFILTERTYPE, GetAutoGenFilterType)(THIS) + { XXX; return _DOCALL0(GetAutoGenFilterType); } + + STDMETHOD_(void, GenerateMipSubLevels)(THIS) + { XXX; return _DOCALL0(GenerateMipSubLevels); } + + STDMETHOD(GetLevelDesc)(THIS_ UINT Level,D3DSURFACE_DESC *pDesc) + { XXX; return _DOCALL(GetLevelDesc, Level, pDesc); } + + STDMETHOD(GetSurfaceLevel)(THIS_ UINT Level,IDirect3DSurface9** ppSurfaceLevel) + { + XXX; + HRESULT hr = _DOCALL(GetSurfaceLevel, Level, ppSurfaceLevel); + return AllocOverride( &hr, m_Data.pDevice, ppSurfaceLevel ); + } + STDMETHOD(LockRect)(THIS_ UINT Level,D3DLOCKED_RECT* pLockedRect,CONST RECT* pRect,DWORD Flags) + { XXX; return _DOCALL(LockRect, Level, pLockedRect, pRect, Flags); } + STDMETHOD(UnlockRect)(THIS_ UINT Level) + { XXX; return _DOCALL(UnlockRect, Level); } + + STDMETHOD(AddDirtyRect)(THIS_ CONST RECT* pDirtyRect) + { XXX; return _DOCALL(AddDirtyRect, pDirtyRect); } +}; + +class CDirect3DCubeTexture9Hook : public CDx9HookBase, public IDirect3DCubeTexture9 +{ +public: + /*** IUnknown methods ***/ + IMPL_IUNKOWN(); + + /*** IDirect3DBaseTexture9 methods ***/ + IMPL_IDIRECT3DRESOURCE9(); + + STDMETHOD_(DWORD, SetLOD)(THIS_ DWORD LODNew) + { XXX; return _DOCALL(SetLOD, LODNew); } + STDMETHOD_(DWORD, GetLOD)(THIS) + { XXX; return _DOCALL0(GetLOD); } + STDMETHOD_(DWORD, GetLevelCount)(THIS) + { XXX; return _DOCALL0(GetLevelCount); } + STDMETHOD(SetAutoGenFilterType)(THIS_ D3DTEXTUREFILTERTYPE FilterType) + { XXX; return _DOCALL(SetAutoGenFilterType, FilterType); } + STDMETHOD_(D3DTEXTUREFILTERTYPE, GetAutoGenFilterType)(THIS) + { XXX; return _DOCALL0(GetAutoGenFilterType); } + + STDMETHOD_(void, GenerateMipSubLevels)(THIS) + { XXX; return _DOCALL0(GenerateMipSubLevels); } + + STDMETHOD(GetLevelDesc)(THIS_ UINT Level,D3DSURFACE_DESC *pDesc) + { XXX; return _DOCALL(GetLevelDesc, Level, pDesc); } + + STDMETHOD(GetCubeMapSurface)(THIS_ D3DCUBEMAP_FACES FaceType,UINT Level,IDirect3DSurface9** ppCubeMapSurface) + { + XXX; + HRESULT hr = _DOCALL(GetCubeMapSurface, FaceType, Level, ppCubeMapSurface); + return AllocOverride(&hr, m_Data.pDevice, ppCubeMapSurface); + } + STDMETHOD(LockRect)(THIS_ D3DCUBEMAP_FACES FaceType,UINT Level,D3DLOCKED_RECT* pLockedRect,CONST RECT* pRect,DWORD Flags) + { XXX; return _DOCALL(LockRect, FaceType, Level, pLockedRect, pRect, Flags); } + STDMETHOD(UnlockRect)(THIS_ D3DCUBEMAP_FACES FaceType,UINT Level) + { XXX; return _DOCALL(UnlockRect, FaceType, Level); } + + STDMETHOD(AddDirtyRect)(THIS_ D3DCUBEMAP_FACES FaceType,CONST RECT* pDirtyRect) + { XXX; return _DOCALL(AddDirtyRect, FaceType, pDirtyRect); } +}; + +class CDirect3DVolume9Hook : public CDx9HookBase, public IDirect3DVolume9 +{ +public: + /*** IUnknown methods ***/ + IMPL_IUNKOWN(); + + /*** IDirect3DVolume9 methods ***/ + IMPL_GETDEVICE(); + + IMPL_SETPRIVATEDATA(); + IMPL_GETPRIVATEDATA(); + IMPL_FREEPRIVATEDATA() + + STDMETHOD(GetContainer)(THIS_ REFIID riid,void** ppContainer) + { + //$ TODO: do the call, check riid, and wrap the returned ppContainer + __debugbreak(); + XXX; return _DOCALL(GetContainer, riid, ppContainer); + } + + STDMETHOD(GetDesc)(THIS_ D3DVOLUME_DESC *pDesc) + { XXX; return _DOCALL(GetDesc, pDesc); } + + STDMETHOD(LockBox)(THIS_ D3DLOCKED_BOX * pLockedVolume,CONST D3DBOX* pBox,DWORD Flags) + { XXX; return _DOCALL(LockBox, pLockedVolume, pBox, Flags); } + STDMETHOD(UnlockBox)(THIS) + { XXX; return _DOCALL0(UnlockBox); } +}; + +class CDirect3DVolumeTexture9Hook : public CDx9HookBase, public IDirect3DVolumeTexture9 +{ +public: + /*** IUnknown methods ***/ + IMPL_IUNKOWN(); + + /*** IDirect3DBaseTexture9 methods ***/ + IMPL_IDIRECT3DRESOURCE9(); + + STDMETHOD_(DWORD, SetLOD)(THIS_ DWORD LODNew) + { XXX; return _DOCALL(SetLOD, LODNew); } + STDMETHOD_(DWORD, GetLOD)(THIS) + { XXX; return _DOCALL0(GetLOD); } + STDMETHOD_(DWORD, GetLevelCount)(THIS) + { XXX; return _DOCALL0(GetLevelCount); } + STDMETHOD(SetAutoGenFilterType)(THIS_ D3DTEXTUREFILTERTYPE FilterType) + { XXX; return _DOCALL(SetAutoGenFilterType, FilterType); } + STDMETHOD_(D3DTEXTUREFILTERTYPE, GetAutoGenFilterType)(THIS) + { XXX; return _DOCALL0(GetAutoGenFilterType); } + + STDMETHOD_(void, GenerateMipSubLevels)(THIS) + { XXX; return _DOCALL0(GenerateMipSubLevels); } + + STDMETHOD(GetLevelDesc)(THIS_ UINT Level,D3DVOLUME_DESC *pDesc) + { XXX; return _DOCALL(GetLevelDesc, Level, pDesc); } + + STDMETHOD(GetVolumeLevel)(THIS_ UINT Level,IDirect3DVolume9** ppVolumeLevel) + { + XXX; + HRESULT hr = _DOCALL(GetVolumeLevel, Level, ppVolumeLevel); + return AllocOverride( &hr, m_Data.pDevice, ppVolumeLevel ); + } + STDMETHOD(LockBox)(THIS_ UINT Level,D3DLOCKED_BOX* pLockedVolume,CONST D3DBOX* pBox,DWORD Flags) + { XXX; return _DOCALL(LockBox, Level, pLockedVolume, pBox, Flags); } + STDMETHOD(UnlockBox)(THIS_ UINT Level) + { XXX; return _DOCALL(UnlockBox, Level); } + + STDMETHOD(AddDirtyBox)(THIS_ CONST D3DBOX* pDirtyBox) + { XXX; return _DOCALL(AddDirtyBox, pDirtyBox); } +}; + +class CDirect3DDevice9Hook : public CDx9HookBase, public IDirect3DDevice9 +{ +public: + /*** IUnknown methods ***/ + IMPL_IUNKOWN(); + + CDirect3DDevice9Hook() : CDx9HookBase(), IDirect3DDevice9() + { + D3D_BATCH_PERF( g_nTotalD3DCycles = 0; g_nTotalD3DCalls = 0; m_batch_state.Clear(); m_nTotalDraws = 0; m_nTotalPrims = 0; m_nTotalD3DCalls = 0; m_flTotalD3DTime = 0; m_nOverallDraws = 0; m_nOverallPrims = 0; m_nOverallD3DCalls = 0; m_flOverallD3DTime = 0; m_nTotalFrames = 0; m_pPrevRenderTarget0 = NULL; ) + } + + // IDirect3DDevice9 methods + STDMETHOD(TestCooperativeLevel)(THIS) + { XXX; return _DOCALL0(TestCooperativeLevel); } + STDMETHOD_(UINT, GetAvailableTextureMem)(THIS) + { XXX; return _DOCALL0(GetAvailableTextureMem); } + STDMETHOD(EvictManagedResources)(THIS) + { XXX; return _DOCALL0(EvictManagedResources); } + STDMETHOD(GetDirect3D)(THIS_ IDirect3D9** ppD3D9) + { + XXX; + HRESULT hr = _DOCALL(GetDirect3D, ppD3D9); + return AllocOverride(&hr, this, ppD3D9); + } + STDMETHOD(GetDeviceCaps)(THIS_ D3DCAPS9* pCaps) + { XXX; return _DOCALL(GetDeviceCaps, pCaps); } + STDMETHOD(GetDisplayMode)(THIS_ UINT iSwapChain,D3DDISPLAYMODE* pMode) + { XXX; return _DOCALL(GetDisplayMode, iSwapChain, pMode); } + STDMETHOD(GetCreationParameters)(THIS_ D3DDEVICE_CREATION_PARAMETERS *pParameters) + { XXX; return _DOCALL(GetCreationParameters, pParameters); } + STDMETHOD(SetCursorProperties)(THIS_ UINT XHotSpot,UINT YHotSpot,IDirect3DSurface9* pCursorBitmap) + { XXX; return _DOCALL(SetCursorProperties, XHotSpot, YHotSpot, GetHWPtr(pCursorBitmap)); } + STDMETHOD_(void, SetCursorPosition)(THIS_ int X,int Y,DWORD Flags) + { XXX; _DOCALL_NORET(SetCursorPosition, X, Y, Flags); } + STDMETHOD_(BOOL, ShowCursor)(THIS_ BOOL bShow) + { XXX; return _DOCALL(ShowCursor, bShow); } + STDMETHOD(CreateAdditionalSwapChain)(THIS_ D3DPRESENT_PARAMETERS* pPresentationParameters,IDirect3DSwapChain9** ppSwapChain) + { + XXX; + HRESULT hr = _DOCALL(CreateAdditionalSwapChain, pPresentationParameters, ppSwapChain); + return AllocOverride(&hr, this, ppSwapChain); + } + STDMETHOD(GetSwapChain)(THIS_ UINT iSwapChain,IDirect3DSwapChain9** ppSwapChain) + { + XXX; + HRESULT hr = _DOCALL(GetSwapChain, iSwapChain, ppSwapChain); + return AllocOverride(&hr, this, ppSwapChain); + } + STDMETHOD_(UINT, GetNumberOfSwapChains)(THIS) + { XXX; return _DOCALL0(GetNumberOfSwapChains); } + STDMETHOD(Reset)(THIS_ D3DPRESENT_PARAMETERS* pPresentationParameters) + { XXX; return _DOCALL( Reset, pPresentationParameters ); } + STDMETHOD(Present)(THIS_ CONST RECT* pSourceRect,CONST RECT* pDestRect,HWND hDestWindowOverride,CONST RGNDATA* pDirtyRegion) + { + HRESULT hres; + +#if D3D_BATCH_PERF_ANALYSIS + uint64 nStartCycles = g_nTotalD3DCycles; + + CFastTimer tm; + tm.Start(); + + g_nTotalD3DCalls++; +#endif + + { + XXX; + hres = _DOCALL(Present, pSourceRect, pDestRect, hDestWindowOverride, pDirtyRegion); + } + +#if D3D_BATCH_PERF_ANALYSIS + const uint nFrameIndex = m_nTotalFrames; + m_nTotalFrames++; + + if (nFrameIndex >= 5) + { + double flPresentTime = tm.GetDurationInProgress().GetMillisecondsF(); + uint64 nEndCycles = g_nTotalD3DCycles; + + double flTotalPresentTime = ( nEndCycles - nStartCycles ) * s_rdtsc_to_ms; + + m_nTotalD3DCalls += g_nTotalD3DCalls; + m_flTotalD3DTime += g_nTotalD3DCycles * s_rdtsc_to_ms; + + static int bPrevBatchVis = -1; + + if ((bPrevBatchVis == 1) && m_batch_vis_bitmap.is_valid()) + { + m_nOverallDraws += m_nTotalDraws; + m_nOverallPrims += m_nTotalPrims; + m_nOverallD3DCalls += m_nTotalD3DCalls; + m_flOverallD3DTime += m_flTotalD3DTime; + + m_batch_vis_bitmap.fill_box(0, m_nBatchVisY, (uint)(.5f + flPresentTime / d3d_present_vis_abs_scale.GetFloat() * m_batch_vis_bitmap.width()), 10, 255, 16, 128); + m_batch_vis_bitmap.additive_fill_box(0, m_nBatchVisY, (uint)(.5f + flTotalPresentTime / d3d_present_vis_abs_scale.GetFloat() * m_batch_vis_bitmap.width()), 10, 0, 255, 128); + m_nBatchVisY += 10; + + uint y = MAX( 600, m_nBatchVisY + 20 ), l = 0; + m_batch_vis_bitmap.draw_formatted_text(0, y+8*(l++), 1, 255, 255, 255, "Frame: %u, Batches: %u, Prims: %u", nFrameIndex, m_nTotalDraws, m_nTotalPrims ); + m_batch_vis_bitmap.draw_formatted_text(0, y+8*(l++), 1, 255, 255, 255, "Frame: D3D Calls: %u, D3D Time: %3.3fms", m_nTotalD3DCalls, m_flTotalD3DTime); + l++; + m_batch_vis_bitmap.draw_formatted_text(0, y+8*(l++), 1, 255, 255, 255, "Overall: Batches: %u, Prims: %u", m_nOverallDraws, m_nOverallPrims ); + m_batch_vis_bitmap.draw_formatted_text(0, y+8*(l++), 1, 255, 255, 255, "Overall: D3D Calls: %u D3D Time: %4.3fms", m_nOverallD3DCalls, m_flOverallD3DTime ); + + size_t png_size = 0; + void *pPNG_data = tdefl_write_image_to_png_file_in_memory(m_batch_vis_bitmap.get_ptr(), m_batch_vis_bitmap.width(), m_batch_vis_bitmap.height(), 3, &png_size, true); + if (pPNG_data) + { + char filename[256]; + V_snprintf(filename, sizeof(filename), "left4dead2/batchvis_%u_%u.png", m_nBatchVisFileIdx, m_nBatchVisFrameIndex); + FILE* pFile = fopen(filename, "wb"); + if (pFile) + { + fwrite(pPNG_data, png_size, 1, pFile); + fclose(pFile); + } + free(pPNG_data); + } + m_nBatchVisFrameIndex++; + m_nBatchVisY = 0; + m_batch_vis_bitmap.cls(); + } + + if (bPrevBatchVis != (int)d3d_batch_vis.GetBool()) + { + bPrevBatchVis = d3d_batch_vis.GetBool(); + if (!bPrevBatchVis) + { + m_batch_vis_bitmap.clear(); + } + else + { + m_batch_vis_bitmap.init(768, 1024); + } + m_nBatchVisY = 0; + m_nBatchVisFrameIndex = 0; + m_nBatchVisFileIdx = (uint)time(NULL); //rand(); + + m_nOverallDraws = 0; + m_nOverallPrims = 0; + m_nOverallD3DCalls = 0; + m_flOverallD3DTime = 0; + } + } + + m_nTotalD3DCalls = 0; + m_nTotalPrims = 0; + m_flTotalD3DTime = 0; + g_nTotalD3DCycles = 0; + g_nTotalD3DCalls = 0; + m_nTotalDraws = 0; +#else + if ( d3d_batch_vis.GetBool() ) + { + d3d_batch_vis.SetValue( false ); + + ConMsg( "Must define D3D_BATCH_PERF_ANALYSIS to use this feature" ); + } +#endif + + return hres; + } + + STDMETHOD(GetBackBuffer)(THIS_ UINT iSwapChain,UINT iBackBuffer,D3DBACKBUFFER_TYPE Type,IDirect3DSurface9** ppBackBuffer) + { + XXX; + HRESULT hr = _DOCALL(GetBackBuffer, iSwapChain, iBackBuffer, Type, ppBackBuffer); + return AllocOverride(&hr, this, ppBackBuffer); + } + STDMETHOD(GetRasterStatus)(THIS_ UINT iSwapChain,D3DRASTER_STATUS* pRasterStatus) + { XXX; return _DOCALL(GetRasterStatus, iSwapChain, pRasterStatus); } + STDMETHOD(SetDialogBoxMode)(THIS_ BOOL bEnableDialogs) + { XXX; return _DOCALL(SetDialogBoxMode, bEnableDialogs); } + STDMETHOD_(void, SetGammaRamp)(THIS_ UINT iSwapChain,DWORD Flags,CONST D3DGAMMARAMP* pRamp) + { XXX; _DOCALL_NORET(SetGammaRamp, iSwapChain, Flags, pRamp); } + STDMETHOD_(void, GetGammaRamp)(THIS_ UINT iSwapChain,D3DGAMMARAMP* pRamp) + { XXX; _DOCALL_NORET(GetGammaRamp, iSwapChain, pRamp); } + STDMETHOD(CreateTexture)(THIS_ UINT Width,UINT Height,UINT Levels,DWORD Usage,D3DFORMAT Format,D3DPOOL Pool,IDirect3DTexture9** ppTexture,HANDLE* pSharedHandle) + { + XXX; + HRESULT hr = _DOCALL(CreateTexture, Width, Height, Levels, Usage, Format, Pool, ppTexture, pSharedHandle); + return AllocOverride(&hr, this, ppTexture); + } + STDMETHOD(CreateVolumeTexture)(THIS_ UINT Width,UINT Height,UINT Depth,UINT Levels,DWORD Usage,D3DFORMAT Format,D3DPOOL Pool,IDirect3DVolumeTexture9** ppVolumeTexture,HANDLE* pSharedHandle) + { + XXX; + HRESULT hr = _DOCALL(CreateVolumeTexture, Width, Height, Depth, Levels, Usage, Format, Pool, ppVolumeTexture, pSharedHandle); + return AllocOverride(&hr, this, ppVolumeTexture); + } + STDMETHOD(CreateCubeTexture)(THIS_ UINT EdgeLength,UINT Levels,DWORD Usage,D3DFORMAT Format,D3DPOOL Pool,IDirect3DCubeTexture9** ppCubeTexture,HANDLE* pSharedHandle) + { + XXX; + HRESULT hr = _DOCALL(CreateCubeTexture, EdgeLength, Levels, Usage, Format, Pool, ppCubeTexture, pSharedHandle); + return AllocOverride(&hr, this, ppCubeTexture); + } + STDMETHOD(CreateVertexBuffer)(THIS_ UINT Length,DWORD Usage,DWORD FVF,D3DPOOL Pool,IDirect3DVertexBuffer9** ppVertexBuffer,HANDLE* pSharedHandle) + { + XXX; + HRESULT hr = _DOCALL(CreateVertexBuffer, Length, Usage, FVF, Pool, ppVertexBuffer, pSharedHandle); + return AllocOverride(&hr, this, ppVertexBuffer); + } + STDMETHOD(CreateIndexBuffer)(THIS_ UINT Length,DWORD Usage,D3DFORMAT Format,D3DPOOL Pool,IDirect3DIndexBuffer9** ppIndexBuffer,HANDLE* pSharedHandle) + { + XXX; + HRESULT hr = _DOCALL(CreateIndexBuffer, Length, Usage, Format, Pool, ppIndexBuffer, pSharedHandle); + return AllocOverride(&hr, this, ppIndexBuffer); + } + STDMETHOD(CreateRenderTarget)(THIS_ UINT Width,UINT Height,D3DFORMAT Format,D3DMULTISAMPLE_TYPE MultiSample,DWORD MultiSampleQuality,BOOL Lockable,IDirect3DSurface9** ppSurface,HANDLE* pSharedHandle) + { + XXX; + HRESULT hr = _DOCALL(CreateRenderTarget, Width, Height, Format, MultiSample, MultiSampleQuality, Lockable, ppSurface, pSharedHandle); + return AllocOverride(&hr, this, ppSurface); + } + STDMETHOD(CreateDepthStencilSurface)(THIS_ UINT Width,UINT Height,D3DFORMAT Format,D3DMULTISAMPLE_TYPE MultiSample,DWORD MultiSampleQuality,BOOL Discard,IDirect3DSurface9** ppSurface,HANDLE* pSharedHandle) + { + XXX; + HRESULT hr = _DOCALL(CreateDepthStencilSurface, Width, Height, Format, MultiSample, MultiSampleQuality, Discard, ppSurface, pSharedHandle); + return AllocOverride(&hr, this, ppSurface); + } + STDMETHOD(UpdateSurface)(THIS_ IDirect3DSurface9* pSourceSurface,CONST RECT* pSourceRect,IDirect3DSurface9* pDestinationSurface,CONST POINT* pDestPoint) + { XXX; return _DOCALL(UpdateSurface, GetHWPtr(pSourceSurface), pSourceRect, GetHWPtr(pDestinationSurface), pDestPoint); } + STDMETHOD(UpdateTexture)(THIS_ IDirect3DBaseTexture9* pSourceTexture,IDirect3DBaseTexture9* pDestinationTexture) + { XXX; return _DOCALL(UpdateTexture, GetHWPtr(pSourceTexture), GetHWPtr(pDestinationTexture)); } + STDMETHOD(GetRenderTargetData)(THIS_ IDirect3DSurface9* pRenderTarget,IDirect3DSurface9* pDestSurface) + { XXX; return _DOCALL(GetRenderTargetData, GetHWPtr(pRenderTarget), GetHWPtr(pDestSurface)); } + STDMETHOD(GetFrontBufferData)(THIS_ UINT iSwapChain,IDirect3DSurface9* pDestSurface) + { XXX; return _DOCALL(GetFrontBufferData, iSwapChain, GetHWPtr(pDestSurface)); } + STDMETHOD(StretchRect)(THIS_ IDirect3DSurface9* pSourceSurface,CONST RECT* pSourceRect,IDirect3DSurface9* pDestSurface,CONST RECT* pDestRect,D3DTEXTUREFILTERTYPE Filter) + { XXX; return _DOCALL(StretchRect, GetHWPtr(pSourceSurface), pSourceRect, GetHWPtr(pDestSurface), pDestRect, Filter); } + STDMETHOD(ColorFill)(THIS_ IDirect3DSurface9* pSurface,CONST RECT* pRect,D3DCOLOR color) + { XXX; return _DOCALL(ColorFill, GetHWPtr(pSurface), pRect, color); } + STDMETHOD(CreateOffscreenPlainSurface)(THIS_ UINT Width,UINT Height,D3DFORMAT Format,D3DPOOL Pool,IDirect3DSurface9** ppSurface,HANDLE* pSharedHandle) + { + XXX; + HRESULT hr = _DOCALL(CreateOffscreenPlainSurface, Width, Height, Format, Pool, ppSurface, pSharedHandle); + return AllocOverride(&hr, this, ppSurface); + } + + STDMETHOD(SetRenderTarget)(THIS_ DWORD RenderTargetIndex,IDirect3DSurface9* pRenderTarget) + { + HRESULT hres; + { + XXX; + hres = _DOCALL(SetRenderTarget, RenderTargetIndex, GetHWPtr(pRenderTarget)); + } +#if D3D_BATCH_PERF_ANALYSIS + if ( m_batch_vis_bitmap.is_valid() && !RenderTargetIndex ) + { + if ( pRenderTarget != m_pPrevRenderTarget0 ) + { + m_batch_vis_bitmap.fill_box(0, m_nBatchVisY, m_batch_vis_bitmap.width(), 1, 30, 20, 20); + m_nBatchVisY += 1; + } + } + if ( !RenderTargetIndex ) + { + m_pPrevRenderTarget0 = pRenderTarget; + } +#endif + return hres; + } + + STDMETHOD(GetRenderTarget)(THIS_ DWORD RenderTargetIndex,IDirect3DSurface9** ppRenderTarget) + { + XXX; + HRESULT hr = _DOCALL(GetRenderTarget, RenderTargetIndex, ppRenderTarget); + return AllocOverride(&hr, this, ppRenderTarget); + } + STDMETHOD(SetDepthStencilSurface)(THIS_ IDirect3DSurface9* pNewZStencil) + { XXX; return _DOCALL(SetDepthStencilSurface, GetHWPtr(pNewZStencil)); } + STDMETHOD(GetDepthStencilSurface)(THIS_ IDirect3DSurface9** ppZStencilSurface) + { + XXX; + HRESULT hr = _DOCALL(GetDepthStencilSurface, ppZStencilSurface); + return AllocOverride(&hr, this, ppZStencilSurface); + } + STDMETHOD(BeginScene)(THIS) + { XXX; return _DOCALL0(BeginScene); } + STDMETHOD(EndScene)(THIS) + { XXX; return _DOCALL0(EndScene); } + STDMETHOD(Clear)(THIS_ DWORD Count,CONST D3DRECT* pRects,DWORD Flags,D3DCOLOR Color,float Z,DWORD Stencil) + { XXX; return _DOCALL(Clear, Count, pRects, Flags, Color, Z, Stencil); } + STDMETHOD(SetTransform)(THIS_ D3DTRANSFORMSTATETYPE State,CONST D3DMATRIX* pMatrix) + { XXX; return _DOCALL(SetTransform, State, pMatrix); } + STDMETHOD(GetTransform)(THIS_ D3DTRANSFORMSTATETYPE State,D3DMATRIX* pMatrix) + { XXX; return _DOCALL(GetTransform, State, pMatrix); } + STDMETHOD(MultiplyTransform)(THIS_ D3DTRANSFORMSTATETYPE State,CONST D3DMATRIX *pMatrix) + { XXX; return _DOCALL(MultiplyTransform, State, pMatrix); } + STDMETHOD(SetViewport)(THIS_ CONST D3DVIEWPORT9* pViewport) + { XXX; return _DOCALL(SetViewport, pViewport); } + STDMETHOD(GetViewport)(THIS_ D3DVIEWPORT9* pViewport) + { XXX; return _DOCALL(GetViewport, pViewport); } + STDMETHOD(SetMaterial)(THIS_ CONST D3DMATERIAL9* pMaterial) + { XXX; return _DOCALL(SetMaterial, pMaterial); } + STDMETHOD(GetMaterial)(THIS_ D3DMATERIAL9* pMaterial) + { XXX; return _DOCALL(GetMaterial, pMaterial); } + STDMETHOD(SetLight)(THIS_ DWORD Index,CONST D3DLIGHT9* pLight) + { XXX; return _DOCALL(SetLight, Index, pLight); } + STDMETHOD(GetLight)(THIS_ DWORD Index,D3DLIGHT9* pLight) + { XXX; return _DOCALL(GetLight, Index, pLight); } + STDMETHOD(LightEnable)(THIS_ DWORD Index,BOOL Enable) + { XXX; return _DOCALL(LightEnable, Index, Enable); } + STDMETHOD(GetLightEnable)(THIS_ DWORD Index,BOOL* pEnable) + { XXX; return _DOCALL(GetLightEnable, Index, pEnable); } + STDMETHOD(SetClipPlane)(THIS_ DWORD Index,CONST float* pPlane) + { XXX; return _DOCALL(SetClipPlane, Index, pPlane);} + STDMETHOD(GetClipPlane)(THIS_ DWORD Index,float* pPlane) + { XXX; return _DOCALL(GetClipPlane, Index, pPlane); } + STDMETHOD(SetRenderState)(THIS_ D3DRENDERSTATETYPE State,DWORD Value) + { XXX; return _DOCALL(SetRenderState, State, Value); } + STDMETHOD(GetRenderState)(THIS_ D3DRENDERSTATETYPE State,DWORD* pValue) + { XXX; return _DOCALL(GetRenderState, State, pValue); } + STDMETHOD(CreateStateBlock)(THIS_ D3DSTATEBLOCKTYPE Type,IDirect3DStateBlock9** ppSB) + { + XXX; + HRESULT hr = _DOCALL(CreateStateBlock, Type, ppSB); + return AllocOverride(&hr, this, ppSB); + } + STDMETHOD(BeginStateBlock)(THIS) + { XXX; return _DOCALL0(BeginStateBlock); } + STDMETHOD(EndStateBlock)(THIS_ IDirect3DStateBlock9** ppSB) + { + XXX; + HRESULT hr = _DOCALL(EndStateBlock, ppSB); + return AllocOverride(&hr, this, ppSB); + } + STDMETHOD(SetClipStatus)(THIS_ CONST D3DCLIPSTATUS9* pClipStatus) + { XXX; return _DOCALL(SetClipStatus, pClipStatus);} + STDMETHOD(GetClipStatus)(THIS_ D3DCLIPSTATUS9* pClipStatus) + { XXX; return _DOCALL(GetClipStatus, pClipStatus);} + STDMETHOD(GetTexture)(THIS_ DWORD Stage,IDirect3DBaseTexture9** ppTexture) + { + XXX; + HRESULT hr = _DOCALL(GetTexture, Stage, ppTexture); + return AllocOverride(&hr, this, ppTexture); + } + + STDMETHOD(SetTexture)(THIS_ DWORD Stage,IDirect3DBaseTexture9* pTexture) + { + HRESULT hres; + { + XXX; + hres = _DOCALL(SetTexture, Stage, GetHWPtr(pTexture)); + } + D3D_BATCH_PERF( m_batch_state.m_nNumSamplersChanged++; ) + return hres; + } + + STDMETHOD(GetTextureStageState)(THIS_ DWORD Stage,D3DTEXTURESTAGESTATETYPE Type,DWORD* pValue) + { XXX; return _DOCALL(GetTextureStageState, Stage, Type, pValue); } + STDMETHOD(SetTextureStageState)(THIS_ DWORD Stage,D3DTEXTURESTAGESTATETYPE Type,DWORD Value) + { XXX; return _DOCALL(SetTextureStageState, Stage, Type, Value); } + STDMETHOD(GetSamplerState)(THIS_ DWORD Sampler,D3DSAMPLERSTATETYPE Type,DWORD* pValue) + { XXX; return _DOCALL(GetSamplerState, Sampler, Type, pValue); } + STDMETHOD(SetSamplerState)(THIS_ DWORD Sampler,D3DSAMPLERSTATETYPE Type,DWORD Value) + { + HRESULT hres; + { + XXX; + hres = _DOCALL(SetSamplerState, Sampler, Type, Value); + } + D3D_BATCH_PERF( m_batch_state.m_nNumSamplerStatesChanged++; ) + return hres; + } + + STDMETHOD(ValidateDevice)(THIS_ DWORD* pNumPasses) + { XXX; return _DOCALL(ValidateDevice, pNumPasses); } + STDMETHOD(SetPaletteEntries)(THIS_ UINT PaletteNumber,CONST PALETTEENTRY* pEntries) + { XXX; return _DOCALL(SetPaletteEntries, PaletteNumber, pEntries); } + STDMETHOD(GetPaletteEntries)(THIS_ UINT PaletteNumber,PALETTEENTRY* pEntries) + { XXX; return _DOCALL(GetPaletteEntries, PaletteNumber, pEntries); } + STDMETHOD(SetCurrentTexturePalette)(THIS_ UINT PaletteNumber) + { XXX; return _DOCALL(SetCurrentTexturePalette, PaletteNumber);} + STDMETHOD(GetCurrentTexturePalette)(THIS_ UINT *PaletteNumber) + { XXX; return _DOCALL(GetCurrentTexturePalette, PaletteNumber); } + STDMETHOD(SetScissorRect)(THIS_ CONST RECT* pRect) + { XXX; return _DOCALL(SetScissorRect, pRect); } + STDMETHOD(GetScissorRect)(THIS_ RECT* pRect) + { XXX; return _DOCALL(GetScissorRect, pRect); } + STDMETHOD(SetSoftwareVertexProcessing)(THIS_ BOOL bSoftware) + { XXX; return _DOCALL(SetSoftwareVertexProcessing, bSoftware); } + STDMETHOD_(BOOL, GetSoftwareVertexProcessing)(THIS) + { XXX; return _DOCALL0(GetSoftwareVertexProcessing); } + STDMETHOD(SetNPatchMode)(THIS_ float nSegments) + { XXX; return _DOCALL(SetNPatchMode, nSegments); } + STDMETHOD_(float, GetNPatchMode)(THIS) + { XXX; return _DOCALL0(GetNPatchMode);} + STDMETHOD(DrawPrimitive)(THIS_ D3DPRIMITIVETYPE PrimitiveType,UINT StartVertex,UINT PrimitiveCount) + { XXX; m_nTotalDraws++; m_nTotalPrims += PrimitiveCount; return _DOCALL(DrawPrimitive, PrimitiveType, StartVertex, PrimitiveCount); } + STDMETHOD(DrawIndexedPrimitive)(THIS_ D3DPRIMITIVETYPE PrimitiveType,INT BaseVertexIndex,UINT MinVertexIndex,UINT NumVertices,UINT startIndex,UINT primCount) + { + m_nTotalDraws++; + m_nTotalPrims += primCount; + +#if D3D_BATCH_PERF_ANALYSIS + CFastTimer tm; + if ( m_batch_vis_bitmap.is_valid() ) + { + tm.Start(); + } +#endif + + HRESULT hres; + { + XXX; + hres = _DOCALL(DrawIndexedPrimitive, PrimitiveType, BaseVertexIndex, MinVertexIndex, NumVertices, startIndex, primCount); + } + +#if D3D_BATCH_PERF_ANALYSIS + if ( m_batch_vis_bitmap.is_valid() ) + { + double t = tm.GetDurationInProgress().GetMillisecondsF(); + + uint h = 1; + if ( d3d_batch_vis_y_scale.GetFloat() > 0.0f) + { + h = ceil( t / d3d_batch_vis_y_scale.GetFloat() ); + h = MAX(h, 1); + } + + m_batch_vis_bitmap.fill_box(0, m_nBatchVisY, (uint)(.5f + t / d3d_batch_vis_abs_scale.GetFloat() * m_batch_vis_bitmap.width()), h, 64, 64, 64); + + if ( s_rdtsc_to_ms == 0.0f ) + { + TmU64 t0 = tmFastTime(); + double d0 = Plat_FloatTime(); + + ThreadSleep( 1 ); + + TmU64 t1 = tmFastTime(); + double d1 = Plat_FloatTime(); + + s_rdtsc_to_ms = ( 1000.0f * ( d1 - d0 ) ) / ( t1 - t0 ); + } + + double flTotalD3DCallMS = g_nTotalD3DCycles * s_rdtsc_to_ms; + + m_batch_vis_bitmap.additive_fill_box(0, m_nBatchVisY, (uint)(.5f + flTotalD3DCallMS / d3d_batch_vis_abs_scale.GetFloat() * m_batch_vis_bitmap.width()), h, 96, 96, 128); + + if (m_batch_state.m_bVertexShaderChanged) m_batch_vis_bitmap.additive_fill_box(0, m_nBatchVisY, 8, h, 0, 0, 64); + if (m_batch_state.m_bPixelShaderChanged) m_batch_vis_bitmap.additive_fill_box(32, m_nBatchVisY, 8, h, 64, 0, 64); + + int lm = 80; + m_batch_vis_bitmap.fill_box(lm+0+0, m_nBatchVisY, m_batch_state.m_nNumVSConstants, h, 64, 255, 255); + m_batch_vis_bitmap.fill_box(lm+64+256+0, m_nBatchVisY, m_batch_state.m_nNumPSConstants, h, 64, 64, 255); + + m_batch_vis_bitmap.fill_box(lm+64+256+32, m_nBatchVisY, m_batch_state.m_nNumSamplersChanged, h, 255, 255, 255); + m_batch_vis_bitmap.fill_box(lm+64+256+32+16, m_nBatchVisY, m_batch_state.m_nNumSamplerStatesChanged, h, 92, 128, 255); + + if ( m_batch_state.m_bStreamSourceChanged) m_batch_vis_bitmap.fill_box(lm+64+256+32+16+64, m_nBatchVisY, 16, h, 128, 128, 128); + if ( m_batch_state.m_bIndicesChanged ) m_batch_vis_bitmap.fill_box(lm+64+256+32+16+64+16, m_nBatchVisY, 16, h, 128, 128, 255); + + m_nBatchVisY += h; + + m_nTotalD3DCalls += g_nTotalD3DCalls; + m_flTotalD3DTime += flTotalD3DCallMS; + + g_nTotalD3DCycles = 0; + g_nTotalD3DCalls = 0; + + m_batch_state.Clear(); + } +#endif + + return hres; + } + + STDMETHOD(DrawPrimitiveUP)(THIS_ D3DPRIMITIVETYPE PrimitiveType,UINT PrimitiveCount,CONST void* pVertexStreamZeroData,UINT VertexStreamZeroStride) + { XXX; m_nTotalDraws++; m_nTotalPrims += PrimitiveCount; return _DOCALL(DrawPrimitiveUP, PrimitiveType, PrimitiveCount, pVertexStreamZeroData, VertexStreamZeroStride); } + STDMETHOD(DrawIndexedPrimitiveUP)(THIS_ D3DPRIMITIVETYPE PrimitiveType,UINT MinVertexIndex,UINT NumVertices,UINT PrimitiveCount,CONST void* pIndexData,D3DFORMAT IndexDataFormat,CONST void* pVertexStreamZeroData,UINT VertexStreamZeroStride) + { XXX; m_nTotalDraws++; m_nTotalPrims += PrimitiveCount; return _DOCALL(DrawIndexedPrimitiveUP, PrimitiveType, MinVertexIndex, NumVertices, PrimitiveCount, pIndexData, IndexDataFormat, pVertexStreamZeroData, VertexStreamZeroStride); } + STDMETHOD(ProcessVertices)(THIS_ UINT SrcStartIndex,UINT DestIndex,UINT VertexCount,IDirect3DVertexBuffer9* pDestBuffer,IDirect3DVertexDeclaration9* pVertexDecl,DWORD Flags) + { XXX; return _DOCALL(ProcessVertices, SrcStartIndex, DestIndex, VertexCount, GetHWPtr(pDestBuffer), GetHWPtr(pVertexDecl), Flags); } + STDMETHOD(CreateVertexDeclaration)(THIS_ CONST D3DVERTEXELEMENT9* pVertexElements,IDirect3DVertexDeclaration9** ppDecl) + { + XXX; + HRESULT hr = _DOCALL(CreateVertexDeclaration, pVertexElements, ppDecl); + return AllocOverride(&hr, this, ppDecl); + } + STDMETHOD(SetVertexDeclaration)(THIS_ IDirect3DVertexDeclaration9* pDecl) + { XXX; return _DOCALL(SetVertexDeclaration, GetHWPtr(pDecl)); } + STDMETHOD(GetVertexDeclaration)(THIS_ IDirect3DVertexDeclaration9** ppDecl) + { + XXX; + HRESULT hr = _DOCALL(GetVertexDeclaration, ppDecl); + return AllocOverride(&hr, this, ppDecl); + } + STDMETHOD(SetFVF)(THIS_ DWORD FVF) + { XXX; return _DOCALL(SetFVF, FVF); } + STDMETHOD(GetFVF)(THIS_ DWORD* pFVF) + { XXX; return _DOCALL(GetFVF, pFVF); } + STDMETHOD(CreateVertexShader)(THIS_ CONST DWORD* pFunction,IDirect3DVertexShader9** ppShader) + { + XXX; + HRESULT hr = _DOCALL(CreateVertexShader, pFunction, ppShader); + return AllocOverride(&hr, this, ppShader); + } + + STDMETHOD(SetVertexShader)(THIS_ IDirect3DVertexShader9* pShader) + { + HRESULT hres; + { + XXX; + hres = _DOCALL(SetVertexShader, GetHWPtr(pShader)); + } + D3D_BATCH_PERF( m_batch_state.m_bVertexShaderChanged = true; ) + return hres; + } + + STDMETHOD(GetVertexShader)(THIS_ IDirect3DVertexShader9** ppShader) + { + XXX; + HRESULT hr = _DOCALL(GetVertexShader, ppShader); + return AllocOverride(&hr, this, ppShader); + } + + STDMETHOD(SetVertexShaderConstantF)(THIS_ UINT StartRegister,CONST float* pConstantData,UINT Vector4fCount) + { + HRESULT hres; + { + XXX; + hres = _DOCALL(SetVertexShaderConstantF, StartRegister, pConstantData, Vector4fCount); + } + D3D_BATCH_PERF( m_batch_state.m_nNumVSConstants += Vector4fCount; ) + return hres; + } + + STDMETHOD(GetVertexShaderConstantF)(THIS_ UINT StartRegister,float* pConstantData,UINT Vector4fCount) + { XXX; return _DOCALL(GetVertexShaderConstantF, StartRegister, pConstantData, Vector4fCount);} + STDMETHOD(SetVertexShaderConstantI)(THIS_ UINT StartRegister,CONST int* pConstantData,UINT Vector4iCount) + { XXX; return _DOCALL(SetVertexShaderConstantI, StartRegister, pConstantData, Vector4iCount); } + STDMETHOD(GetVertexShaderConstantI)(THIS_ UINT StartRegister,int* pConstantData,UINT Vector4iCount) + { XXX; return _DOCALL(GetVertexShaderConstantI, StartRegister, pConstantData, Vector4iCount); } + STDMETHOD(SetVertexShaderConstantB)(THIS_ UINT StartRegister,CONST BOOL* pConstantData,UINT BoolCount) + { XXX; return _DOCALL(SetVertexShaderConstantB, StartRegister, pConstantData, BoolCount); } + STDMETHOD(GetVertexShaderConstantB)(THIS_ UINT StartRegister,BOOL* pConstantData,UINT BoolCount) + { XXX; return _DOCALL(GetVertexShaderConstantB, StartRegister, pConstantData, BoolCount); } + + STDMETHOD(SetStreamSource)(THIS_ UINT StreamNumber,IDirect3DVertexBuffer9* pStreamData,UINT OffsetInBytes,UINT Stride) + + { + HRESULT hres; + { + XXX; + hres = _DOCALL(SetStreamSource, StreamNumber, GetHWPtr(pStreamData), OffsetInBytes, Stride); + } + + D3D_BATCH_PERF( m_batch_state.m_bStreamSourceChanged = true; ) + + return hres; + } + + STDMETHOD(GetStreamSource)(THIS_ UINT StreamNumber,IDirect3DVertexBuffer9** ppStreamData,UINT* OffsetInBytes,UINT* pStride) + { + XXX; + HRESULT hr = _DOCALL(GetStreamSource, StreamNumber, ppStreamData, OffsetInBytes, pStride); + return AllocOverride(&hr, this, ppStreamData); + } + STDMETHOD(SetStreamSourceFreq)(THIS_ UINT StreamNumber,UINT Divider) + { XXX; return _DOCALL(SetStreamSourceFreq, StreamNumber, Divider); } + + STDMETHOD(GetStreamSourceFreq)(THIS_ UINT StreamNumber,UINT* Divider) + { XXX; return _DOCALL(GetStreamSourceFreq, StreamNumber, Divider); } + + STDMETHOD(SetIndices)(THIS_ IDirect3DIndexBuffer9* pIndexData) + { + HRESULT hres; + + { + XXX; + hres = _DOCALL(SetIndices, GetHWPtr(pIndexData)); + } + + D3D_BATCH_PERF( m_batch_state.m_bIndicesChanged = true; ) + + return hres; + } + + STDMETHOD(GetIndices)(THIS_ IDirect3DIndexBuffer9** ppIndexData) + { + XXX; + HRESULT hr = _DOCALL(GetIndices, ppIndexData); + return AllocOverride(&hr, this, ppIndexData); + } + STDMETHOD(CreatePixelShader)(THIS_ CONST DWORD* pFunction,IDirect3DPixelShader9** ppShader) + { + XXX; + HRESULT hr = _DOCALL(CreatePixelShader, pFunction, ppShader); + return AllocOverride(&hr, this, ppShader); + } + + STDMETHOD(SetPixelShader)(THIS_ IDirect3DPixelShader9* pShader) + { + HRESULT hres; + { + XXX; + hres = _DOCALL(SetPixelShader, GetHWPtr(pShader)); + } + D3D_BATCH_PERF( m_batch_state.m_bPixelShaderChanged = true; ) + return hres; + } + + STDMETHOD(GetPixelShader)(THIS_ IDirect3DPixelShader9** ppShader) + { + XXX; + HRESULT hr = _DOCALL(GetPixelShader, ppShader); + return AllocOverride(&hr, this, ppShader); + } + + STDMETHOD(SetPixelShaderConstantF)(THIS_ UINT StartRegister,CONST float* pConstantData,UINT Vector4fCount) + { + HRESULT hres; + { + XXX; + hres = _DOCALL(SetPixelShaderConstantF, StartRegister, pConstantData, Vector4fCount); + } + D3D_BATCH_PERF( m_batch_state.m_nNumPSConstants += Vector4fCount; ) + return hres; + } + + STDMETHOD(GetPixelShaderConstantF)(THIS_ UINT StartRegister,float* pConstantData,UINT Vector4fCount) + { XXX; return _DOCALL(GetPixelShaderConstantF, StartRegister, pConstantData, Vector4fCount); } + STDMETHOD(SetPixelShaderConstantI)(THIS_ UINT StartRegister,CONST int* pConstantData,UINT Vector4iCount) + { XXX; return _DOCALL(SetPixelShaderConstantI, StartRegister, pConstantData, Vector4iCount); } + STDMETHOD(GetPixelShaderConstantI)(THIS_ UINT StartRegister,int* pConstantData,UINT Vector4iCount) + { XXX; return _DOCALL(GetPixelShaderConstantI, StartRegister, pConstantData, Vector4iCount); } + STDMETHOD(SetPixelShaderConstantB)(THIS_ UINT StartRegister,CONST BOOL* pConstantData,UINT BoolCount) + { XXX; return _DOCALL(SetPixelShaderConstantB, StartRegister, pConstantData, BoolCount); } + STDMETHOD(GetPixelShaderConstantB)(THIS_ UINT StartRegister,BOOL* pConstantData,UINT BoolCount) + { XXX; return _DOCALL(GetPixelShaderConstantB, StartRegister, pConstantData, BoolCount); } + STDMETHOD(DrawRectPatch)(THIS_ UINT Handle,CONST float* pNumSegs,CONST D3DRECTPATCH_INFO* pRectPatchInfo) + { XXX; return _DOCALL(DrawRectPatch, Handle, pNumSegs, pRectPatchInfo);} + STDMETHOD(DrawTriPatch)(THIS_ UINT Handle,CONST float* pNumSegs,CONST D3DTRIPATCH_INFO* pTriPatchInfo) + { XXX; return _DOCALL(DrawTriPatch, Handle, pNumSegs, pTriPatchInfo); } + STDMETHOD(DeletePatch)(THIS_ UINT Handle) + { XXX; return _DOCALL(DeletePatch, Handle);} + STDMETHOD(CreateQuery)(THIS_ D3DQUERYTYPE Type,IDirect3DQuery9** ppQuery) + { + XXX; + HRESULT hr = _DOCALL(CreateQuery, Type, ppQuery); + return AllocOverride(&hr, this, ppQuery); + } + +private: + +#if D3D_BATCH_PERF_ANALYSIS + IDirect3DSurface9* m_pPrevRenderTarget0; + simple_bitmap m_batch_vis_bitmap; + uint m_nBatchVisY; + uint m_nBatchVisFrameIndex, m_nBatchVisFileIdx; + + struct BatchState_t + { + void Clear() { memset(this, 0, sizeof(*this)); } + + bool m_bStreamSourceChanged; + bool m_bIndicesChanged; + bool m_bPixelShaderChanged; + bool m_bVertexShaderChanged; + uint m_nNumPSConstants; + uint m_nNumVSConstants; + uint m_nNumSamplersChanged; + uint m_nNumSamplerStatesChanged; + }; + + BatchState_t m_batch_state; + + uint m_nTotalFrames; + + uint m_nTotalDraws; + uint m_nTotalPrims; + uint m_nTotalD3DCalls; + double m_flTotalD3DTime; + + uint m_nOverallDraws; + uint m_nOverallPrims; + uint m_nOverallD3DCalls; + double m_flOverallD3DTime; +#endif +}; + +class CDirect3D9Hook : public CDx9HookBase, public IDirect3D9 +{ +public: + /*** IUnknown methods ***/ + IMPL_IUNKOWN(); + + // IDirect3D9 methods + STDMETHOD(RegisterSoftwareDevice)(THIS_ void* pInitializeFunction) + { XXX; return _DOCALL(RegisterSoftwareDevice, pInitializeFunction); } + STDMETHOD_(UINT, GetAdapterCount)(THIS) + { XXX; return _DOCALL0(GetAdapterCount); } + STDMETHOD(GetAdapterIdentifier)(THIS_ UINT Adapter,DWORD Flags,D3DADAPTER_IDENTIFIER9* pIdentifier) + { XXX; return _DOCALL(GetAdapterIdentifier, Adapter, Flags, pIdentifier);} + STDMETHOD_(UINT, GetAdapterModeCount)(THIS_ UINT Adapter,D3DFORMAT Format) + { XXX; return _DOCALL(GetAdapterModeCount, Adapter, Format);} + STDMETHOD(EnumAdapterModes)(THIS_ UINT Adapter,D3DFORMAT Format,UINT Mode,D3DDISPLAYMODE* pMode) + { XXX; return _DOCALL(EnumAdapterModes, Adapter, Format, Mode, pMode); } + STDMETHOD(GetAdapterDisplayMode)(THIS_ UINT Adapter,D3DDISPLAYMODE* pMode) + { XXX; return _DOCALL(GetAdapterDisplayMode, Adapter, pMode); } + STDMETHOD(CheckDeviceType)(THIS_ UINT iAdapter,D3DDEVTYPE DevType,D3DFORMAT DisplayFormat,D3DFORMAT BackBufferFormat,BOOL bWindowed) + { XXX; return _DOCALL(CheckDeviceType, iAdapter, DevType, DisplayFormat, BackBufferFormat, bWindowed); } + STDMETHOD(CheckDeviceFormat)(THIS_ UINT Adapter,D3DDEVTYPE DeviceType,D3DFORMAT AdapterFormat,DWORD Usage,D3DRESOURCETYPE RType,D3DFORMAT CheckFormat) + { XXX; return _DOCALL(CheckDeviceFormat, Adapter, DeviceType, AdapterFormat, Usage, RType, CheckFormat); } + STDMETHOD(CheckDeviceMultiSampleType)(THIS_ UINT Adapter,D3DDEVTYPE DeviceType,D3DFORMAT SurfaceFormat,BOOL Windowed,D3DMULTISAMPLE_TYPE MultiSampleType,DWORD* pQualityLevels) + { XXX; return _DOCALL(CheckDeviceMultiSampleType, Adapter, DeviceType, SurfaceFormat, Windowed, MultiSampleType, pQualityLevels); } + STDMETHOD(CheckDepthStencilMatch)(THIS_ UINT Adapter,D3DDEVTYPE DeviceType,D3DFORMAT AdapterFormat,D3DFORMAT RenderTargetFormat,D3DFORMAT DepthStencilFormat) + { XXX; return _DOCALL(CheckDepthStencilMatch, Adapter, DeviceType, AdapterFormat, RenderTargetFormat, DepthStencilFormat); } + STDMETHOD(CheckDeviceFormatConversion)(THIS_ UINT Adapter,D3DDEVTYPE DeviceType,D3DFORMAT SourceFormat,D3DFORMAT TargetFormat) + { XXX; return _DOCALL(CheckDeviceFormatConversion, Adapter, DeviceType, SourceFormat, TargetFormat); } + STDMETHOD(GetDeviceCaps)(THIS_ UINT Adapter,D3DDEVTYPE DeviceType,D3DCAPS9* pCaps) + { XXX; return _DOCALL(GetDeviceCaps, Adapter, DeviceType, pCaps); } + STDMETHOD_(HMONITOR, GetAdapterMonitor)(THIS_ UINT Adapter) + { XXX; return _DOCALL(GetAdapterMonitor, Adapter); } + STDMETHOD(CreateDevice)(THIS_ UINT Adapter,D3DDEVTYPE DeviceType,HWND hFocusWindow,DWORD BehaviorFlags,D3DPRESENT_PARAMETERS* pPresentationParameters,IDirect3DDevice9** ppReturnedDeviceInterface) + { + XXX; + HRESULT hr = _DOCALL(CreateDevice, Adapter, DeviceType, hFocusWindow, BehaviorFlags, pPresentationParameters, ppReturnedDeviceInterface); + + if( SUCCEEDED( hr ) && ppReturnedDeviceInterface && *ppReturnedDeviceInterface ) + { + CDirect3DDevice9Hook *pDevice = new CDirect3DDevice9Hook; + if(!pDevice) + { + ( *ppReturnedDeviceInterface )->Release(); + hr = E_OUTOFMEMORY; + return NULL; + } + pDevice->m_Data.pDevice = pDevice; + pDevice->m_Data.pHWObj = *ppReturnedDeviceInterface; + *ppReturnedDeviceInterface = pDevice; + } + return hr; + } +}; + +inline IDirect3D9 *Direct3DCreate9Hook( UINT SDKVersion ) +{ + HRESULT hr = S_OK; + IDirect3D9 *pD3D = Direct3DCreate9( D3D_SDK_VERSION ); + AllocOverride( &hr, NULL, &pD3D ); + return pD3D; +} + +#endif // _DX9HOOK_H_ diff --git a/materialsystem/shaderapidx9/dynamicib.h b/materialsystem/shaderapidx9/dynamicib.h new file mode 100644 index 0000000..304c7ef --- /dev/null +++ b/materialsystem/shaderapidx9/dynamicib.h @@ -0,0 +1,1056 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//===========================================================================// + +#ifndef DYNAMICIB_H +#define DYNAMICIB_H + +#ifdef _WIN32 +#pragma once +#endif + +#include "locald3dtypes.h" +#include "recording.h" +#include "shaderapidx8_global.h" +#include "shaderapidx8.h" +#include "shaderapi/ishaderutil.h" +#include "materialsystem/ivballoctracker.h" +#include "tier1/memstack.h" +#include "gpubufferallocator.h" + +///////////////////////////// +// D. Sim Dietrich Jr. +// sim.dietrich@nvidia.com +///////////////////////////// + +#ifdef _WIN32 +#pragma warning (disable:4189) +#endif + +#include "locald3dtypes.h" +#include "tier1/strtools.h" +#include "tier1/utlqueue.h" +#include "tier0/memdbgon.h" + +// Helper function to unbind an index buffer +void Unbind( IDirect3DIndexBuffer9 *pIndexBuffer ); + +#define X360_INDEX_BUFFER_SIZE_MULTIPLIER 4.0 //minimum of 1, only affects dynamic buffers +//#define X360_BLOCK_ON_IB_FLUSH //uncomment to block until all data is consumed when a flush is requested. Otherwise we only block when absolutely necessary + +#define SPEW_INDEX_BUFFER_STALLS //uncomment to allow buffer stall spewing. + +class CIndexBuffer +{ +public: + CIndexBuffer( IDirect3DDevice9 *pD3D, int count, bool bSoftwareVertexProcessing, bool dynamic = false ); + +#ifdef _X360 + CIndexBuffer(); + void Init( IDirect3DDevice9 *pD3D, uint16 *pIndexMemory, int count ); +#endif + + int AddRef() { return ++m_nReferenceCount; } + int Release() + { + int retVal = --m_nReferenceCount; + if ( retVal == 0 ) + delete this; + return retVal; + } + + LPDIRECT3DINDEXBUFFER GetInterface() const + { + // If this buffer still exists, then Late Creation didn't happen. Best case: we'll render the wrong image. Worst case: Crash. + Assert( !m_pSysmemBuffer ); + return m_pIB; + } + + // Use at beginning of frame to force a flush of VB contents on first draw + void FlushAtFrameStart() { m_bFlush = true; } + + // lock, unlock + unsigned short *Lock( bool bReadOnly, int numIndices, int &startIndex, int startPosition = -1 ); + void Unlock( int numIndices ); + void HandleLateCreation( ); + + // Index position + int IndexPosition() const { return m_Position; } + + // Index size + int IndexSize() const { return sizeof(unsigned short); } + + // Index count + int IndexCount() const { return m_IndexCount; } + +#if _X360 + // For some IBs, memory allocation is managed by CGPUBufferAllocator, via ShaderAPI + const GPUBufferHandle_t *GetBufferAllocationHandle( void ); + void SetBufferAllocationHandle( const GPUBufferHandle_t &bufferAllocationHandle ); + bool IsPooled( void ) { return m_GPUBufferHandle.IsValid(); } + // Expose the data pointer for read-only CPU access to the data + // (double-indirection supports relocation of the data by CGPUBufferAllocator) + const byte **GetBufferDataPointerAddress( void ); +#endif // _X360 + // Do we have enough room without discarding? + bool HasEnoughRoom( int numIndices ) const; + + bool IsDynamic() const { return m_bDynamic; } + bool IsExternal() const { return m_bExternalMemory; } + + // Block until there's a free portion of the buffer of this size, m_Position will be updated to point at where this section starts + void BlockUntilUnused( int nAllocationSize ); + +#ifdef CHECK_INDICES + void UpdateShadowIndices( unsigned short *pData ) + { + Assert( m_LockedStartIndex + m_LockedNumIndices <= m_NumIndices ); + memcpy( m_pShadowIndices + m_LockedStartIndex, pData, m_LockedNumIndices * IndexSize() ); + } + + unsigned short GetShadowIndex( int i ) + { + Assert( i >= 0 && i < (int)m_NumIndices ); + return m_pShadowIndices[i]; + } +#endif + + // UID + unsigned int UID() const + { +#ifdef RECORDING + return m_UID; +#else + return 0; +#endif + } + + void HandlePerFrameTextureStats( int frame ) + { +#ifdef VPROF_ENABLED + if ( m_Frame != frame && !m_bDynamic ) + { + m_Frame = frame; + VPROF_INCREMENT_GROUP_COUNTER( "TexGroup_frame_" TEXTURE_GROUP_STATIC_INDEX_BUFFER, + COUNTER_GROUP_TEXTURE_PER_FRAME, IndexCount() * IndexSize() ); + } +#endif + } + + static int BufferCount() + { +#ifdef _DEBUG + return s_BufferCount; +#else + return 0; +#endif + } + + inline int AllocationSize() const; + + inline int AllocationCount() const; + + // Marks a fence indicating when this buffer was used + void MarkUsedInRendering() + { +#ifdef _X360 + if ( m_bDynamic && m_pIB ) + { + Assert( m_AllocationRing.Count() > 0 ); + m_AllocationRing[m_AllocationRing.Tail()].m_Fence = Dx9Device()->GetCurrentFence(); + } +#endif + } + +private : + void Create( IDirect3DDevice9 *pD3D ); + inline void ReallyUnlock( int unlockBytes ) + { + #if DX_TO_GL_ABSTRACTION + // Knowing how much data was actually written is critical for performance under OpenGL. + m_pIB->UnlockActualSize( unlockBytes ); + #else + unlockBytes; // Unused here + m_pIB->Unlock(); + #endif + } + + enum LOCK_FLAGS + { + LOCKFLAGS_FLUSH = D3DLOCK_NOSYSLOCK | D3DLOCK_DISCARD, +#if !defined( _X360 ) + LOCKFLAGS_APPEND = D3DLOCK_NOSYSLOCK | D3DLOCK_NOOVERWRITE +#else + // X360BUG: forcing all locks to gpu flush, otherwise bizarre mesh corruption on decals + // Currently iterating with microsoft 360 support to track source of gpu corruption + LOCKFLAGS_APPEND = D3DLOCK_NOSYSLOCK +#endif + }; + + LPDIRECT3DINDEXBUFFER m_pIB; +#ifdef _X360 + + struct DynamicBufferAllocation_t + { + DWORD m_Fence; //track whether this memory is safe to use again. + int m_iStartOffset; + int m_iEndOffset; + unsigned int m_iZPassIdx; // The zpass during which this allocation was made + }; + + int m_iNextBlockingPosition; // m_iNextBlockingPosition >= m_Position where another allocation is still in use. + unsigned char *m_pAllocatedMemory; + int m_iAllocationCount; //The total number of indices the buffer we allocated can hold. Usually greater than the number of indices asked for + IDirect3DIndexBuffer9 m_D3DIndexBuffer; //Only need one shared D3D header for our usage patterns. + CUtlLinkedList m_AllocationRing; //tracks what chunks of our memory are potentially still in use by D3D + + GPUBufferHandle_t m_GPUBufferHandle; // Handle to a memory allocation within a shared physical memory pool (see CGPUBufferAllocator) +#endif + + int m_IndexCount; + int m_Position; + byte *m_pSysmemBuffer; + int m_nSysmemBufferStartBytes; + unsigned char m_bLocked : 1; + unsigned char m_bFlush : 1; + unsigned char m_bDynamic : 1; + unsigned char m_bExternalMemory : 1; + unsigned char m_bSoftwareVertexProcessing : 1; + unsigned char m_bLateCreateShouldDiscard : 1; + +#ifdef VPROF_ENABLED + int m_Frame; +#endif + + CInterlockedInt m_nReferenceCount; + +#ifdef _DEBUG + static int s_BufferCount; +#endif + +#ifdef RECORDING + unsigned int m_UID; +#endif + +#if !defined( _X360 ) + //LockedBufferContext m_LockData; +#endif + +protected: +#ifdef CHECK_INDICES + unsigned short *m_pShadowIndices; + unsigned int m_NumIndices; +#endif + + unsigned int m_LockedStartIndex; + unsigned int m_LockedNumIndices; + +private: + // Must use reference counting functions above + ~CIndexBuffer(); +}; + +#if defined( _X360 ) +#include "utlmap.h" +MEMALLOC_DECLARE_EXTERNAL_TRACKING( XMem_CIndexBuffer ); +#endif + + +//----------------------------------------------------------------------------- +// constructor, destructor +//----------------------------------------------------------------------------- + +inline CIndexBuffer::CIndexBuffer( IDirect3DDevice9 *pD3D, int count, + bool bSoftwareVertexProcessing, bool dynamic ) : + m_pIB(0), + m_Position(0), + m_bFlush(true), + m_bLocked(false), + m_bExternalMemory(false), + m_bDynamic(dynamic), + m_bSoftwareVertexProcessing( bSoftwareVertexProcessing ), + m_bLateCreateShouldDiscard( false ) +#ifdef _X360 + ,m_pAllocatedMemory(NULL) + ,m_iNextBlockingPosition(0) + ,m_iAllocationCount(0) +#endif +#ifdef VPROF_ENABLED + ,m_Frame( -1 ) +#endif + , m_nReferenceCount( 0 ) +{ + // For write-combining, ensure we always have locked memory aligned to 4-byte boundaries + count = ALIGN_VALUE( count, 2 ); + m_IndexCount = count; + + MEM_ALLOC_CREDIT_( m_bDynamic ? ( "D3D: " TEXTURE_GROUP_DYNAMIC_INDEX_BUFFER ) : ( "D3D: " TEXTURE_GROUP_STATIC_INDEX_BUFFER ) ); + +#ifdef CHECK_INDICES + m_pShadowIndices = NULL; +#endif + +#ifdef RECORDING + // assign a UID + static unsigned int uid = 0; + m_UID = uid++; +#endif + +#ifdef _DEBUG + ++s_BufferCount; +#endif + +#ifdef CHECK_INDICES + m_pShadowIndices = new unsigned short[ m_IndexCount ]; + m_NumIndices = m_IndexCount; +#endif + + + if ( g_pShaderUtil->GetThreadMode() != MATERIAL_SINGLE_THREADED || !ThreadInMainThread() ) + { + m_pSysmemBuffer = ( byte * )malloc( count * IndexSize() ); + m_nSysmemBufferStartBytes = 0; + } + else + { + m_pSysmemBuffer = NULL; + Create( pD3D ); + } + +#else // _X360 + int nBufferSize = (count * IndexSize()); + if ( m_bDynamic ) + { + m_iAllocationCount = count * X360_INDEX_BUFFER_SIZE_MULTIPLIER; + Assert( m_iAllocationCount >= count ); + m_iAllocationCount = ALIGN_VALUE( m_iAllocationCount, 2 ); + m_pAllocatedMemory = (unsigned char*)XPhysicalAlloc( m_iAllocationCount * IndexSize(), MAXULONG_PTR, 0, PAGE_READWRITE | MEM_LARGE_PAGES | PAGE_WRITECOMBINE ); + } + else if ( MeshMgr()->AllocatePooledIB( this, nBufferSize, TEXTURE_GROUP_STATIC_INDEX_BUFFER ) ) + { + // Successfully allocated in a shared ShaderAPI memory pool (SetBufferAllocationHandle will have been called to set the pointer and stream offset) + m_iAllocationCount = count; + Assert( m_pAllocatedMemory ); + } + else + { + // Fall back to allocating a standalone IB + // NOTE: write-combining (PAGE_WRITECOMBINE) is deliberately not used, since it slows down CPU access to the data (decals+defragmentation) + m_iAllocationCount = count; + m_pAllocatedMemory = (unsigned char*)XPhysicalAlloc( nBufferSize, MAXULONG_PTR, 0, PAGE_READWRITE ); + } + + if ( m_pAllocatedMemory && !IsPooled() ) + { + MemAlloc_RegisterExternalAllocation( XMem_CIndexBuffer, m_pAllocatedMemory, XPhysicalSize( m_pAllocatedMemory ) ); + if ( !m_bDynamic ) + { + // Track non-pooled physallocs, to help tune CGPUBufferAllocator usage + g_SizeIndividualIBPhysAllocs += XPhysicalSize( m_pAllocatedMemory ); + g_NumIndividualIBPhysAllocs++; + } + } + + m_iNextBlockingPosition = m_iAllocationCount; +#endif // _X360 + + +#ifdef VPROF_ENABLED + if ( !m_bDynamic ) + { + VPROF_INCREMENT_GROUP_COUNTER( "TexGroup_global_" TEXTURE_GROUP_STATIC_INDEX_BUFFER, + COUNTER_GROUP_TEXTURE_GLOBAL, IndexCount() * IndexSize() ); + } + else if ( IsX360() ) + { + VPROF_INCREMENT_GROUP_COUNTER( "TexGroup_global_" TEXTURE_GROUP_DYNAMIC_INDEX_BUFFER, + COUNTER_GROUP_TEXTURE_GLOBAL, IndexCount() * IndexSize() ); + } +#endif +} + + +void CIndexBuffer::Create( IDirect3DDevice9 *pD3D ) +{ + D3DINDEXBUFFER_DESC desc; + memset( &desc, 0x00, sizeof( desc ) ); + desc.Format = D3DFMT_INDEX16; + desc.Size = sizeof(unsigned short) * m_IndexCount; + desc.Type = D3DRTYPE_INDEXBUFFER; + desc.Pool = D3DPOOL_DEFAULT; + desc.Usage = D3DUSAGE_WRITEONLY; + if ( m_bDynamic ) + { + desc.Usage |= D3DUSAGE_DYNAMIC; + } + if ( m_bSoftwareVertexProcessing ) + { + desc.Usage |= D3DUSAGE_SOFTWAREPROCESSING; + } + + RECORD_COMMAND( DX8_CREATE_INDEX_BUFFER, 6 ); + RECORD_INT( m_UID ); + RECORD_INT( m_IndexCount * IndexSize() ); + RECORD_INT( desc.Usage ); + RECORD_INT( desc.Format ); + RECORD_INT( desc.Pool ); + RECORD_INT( m_bDynamic ); + +#if !defined( _X360 ) + HRESULT hr = pD3D->CreateIndexBuffer( + m_IndexCount * IndexSize(), + desc.Usage, + desc.Format, + desc.Pool, + &m_pIB, + NULL ); + if ( hr != D3D_OK ) + { + Warning( "CreateIndexBuffer failed!\n" ); + } + + if ( ( hr == D3DERR_OUTOFVIDEOMEMORY ) || ( hr == E_OUTOFMEMORY ) ) + { + // Don't have the memory for this. Try flushing all managed resources + // out of vid mem and try again. + // FIXME: need to record this + pD3D->EvictManagedResources(); + hr = pD3D->CreateIndexBuffer( m_IndexCount * IndexSize(), + desc.Usage, desc.Format, desc.Pool, &m_pIB, NULL ); + } + + Assert( m_pIB ); + Assert( hr == D3D_OK ); + +#ifdef MEASURE_DRIVER_ALLOCATIONS + int nMemUsed = 1024; + VPROF_INCREMENT_GROUP_COUNTER( "ib count", COUNTER_GROUP_NO_RESET, 1 ); + VPROF_INCREMENT_GROUP_COUNTER( "ib driver mem", COUNTER_GROUP_NO_RESET, nMemUsed ); + VPROF_INCREMENT_GROUP_COUNTER( "total driver mem", COUNTER_GROUP_NO_RESET, nMemUsed ); +#endif + +#if defined( _DEBUG ) + if ( IsPC() && m_pIB && !m_pSysmemBuffer ) + { + D3DINDEXBUFFER_DESC aDesc; + m_pIB->GetDesc( &aDesc ); + Assert( memcmp( &aDesc, &desc, sizeof( desc ) ) == 0 ); + } +#endif +} + + +#ifdef _X360 +void *AllocateTempBuffer( size_t nSizeInBytes ); + +inline CIndexBuffer::CIndexBuffer() : + m_pIB(0), + m_Position(0), + m_bFlush(false), + m_bLocked(false), + m_bExternalMemory( true ), + m_bDynamic( false ) +#ifdef VPROF_ENABLED + ,m_Frame( -1 ) +#endif +{ + m_IndexCount = 0; + +#ifdef CHECK_INDICES + m_pShadowIndices = NULL; +#endif + + m_iAllocationCount = 0; + m_pAllocatedMemory = NULL; + m_iNextBlockingPosition = 0; +} + +#include "tier0/memdbgoff.h" + +inline void CIndexBuffer::Init( IDirect3DDevice9 *pD3D, uint16 *pIndexMemory, int count ) +{ + m_IndexCount = count; + m_Position = count; + + m_iAllocationCount = count; + m_pAllocatedMemory = (uint8*)pIndexMemory; + m_iNextBlockingPosition = m_iAllocationCount; + + int nBufferSize = count * sizeof(uint16); + m_pIB = new( AllocateTempBuffer( sizeof( IDirect3DIndexBuffer9 ) ) ) IDirect3DIndexBuffer9; + XGSetIndexBufferHeader( nBufferSize, 0, D3DFMT_INDEX16, 0, 0, m_pIB ); + XGOffsetResourceAddress( m_pIB, pIndexMemory ); +} + +#include "tier0/memdbgon.h" + +#endif // _X360 + +inline CIndexBuffer::~CIndexBuffer() +{ +#ifdef _DEBUG + if ( !m_bExternalMemory ) + { + --s_BufferCount; + } +#endif + + Unlock(0); + +#ifdef CHECK_INDICES + if ( m_pShadowIndices ) + { + delete [] m_pShadowIndices; + m_pShadowIndices = NULL; + } +#endif + + if ( m_pSysmemBuffer ) + { + free( m_pSysmemBuffer ); + m_pSysmemBuffer = NULL; + } + +#ifdef MEASURE_DRIVER_ALLOCATIONS + if ( !m_bExternalMemory ) + { + int nMemUsed = 1024; + VPROF_INCREMENT_GROUP_COUNTER( "ib count", COUNTER_GROUP_NO_RESET, -1 ); + VPROF_INCREMENT_GROUP_COUNTER( "ib driver mem", COUNTER_GROUP_NO_RESET, -nMemUsed ); + VPROF_INCREMENT_GROUP_COUNTER( "total driver mem", COUNTER_GROUP_NO_RESET, -nMemUsed ); + } +#endif + +#if !defined( _X360 ) + if ( m_pIB ) + { + RECORD_COMMAND( DX8_DESTROY_INDEX_BUFFER, 1 ); + RECORD_INT( m_UID ); + + m_pIB->Release(); + } +#else + if ( m_pIB && m_pIB->IsSet( Dx9Device() ) ) + { + Unbind( m_pIB ); + } + + if ( m_pAllocatedMemory && !m_bExternalMemory ) + { + if ( IsPooled() ) + { + MeshMgr()->DeallocatePooledIB( this ); + } + else + { + MemAlloc_RegisterExternalDeallocation( XMem_CIndexBuffer, m_pAllocatedMemory, XPhysicalSize( m_pAllocatedMemory ) ); + if ( !m_bDynamic ) + { + // Track non-pooled physallocs, to help tune CGPUBufferAllocator usage + g_SizeIndividualIBPhysAllocs -= XPhysicalSize( m_pAllocatedMemory ); + g_NumIndividualIBPhysAllocs--; + } + XPhysicalFree( m_pAllocatedMemory ); + } + } + + m_pAllocatedMemory = NULL; + m_pIB = NULL; +#endif + +#ifdef VPROF_ENABLED + if ( !m_bExternalMemory ) + { + if ( !m_bDynamic ) + { + VPROF_INCREMENT_GROUP_COUNTER( "TexGroup_global_" TEXTURE_GROUP_STATIC_INDEX_BUFFER, + COUNTER_GROUP_TEXTURE_GLOBAL, - IndexCount() * IndexSize() ); + } + else if ( IsX360() ) + { + VPROF_INCREMENT_GROUP_COUNTER( "TexGroup_global_" TEXTURE_GROUP_DYNAMIC_INDEX_BUFFER, + COUNTER_GROUP_TEXTURE_GLOBAL, - IndexCount() * IndexSize() ); + } + } +#endif +} + +#ifdef _X360 +//----------------------------------------------------------------------------- +// Get memory allocation data +//----------------------------------------------------------------------------- +inline const GPUBufferHandle_t *CIndexBuffer::GetBufferAllocationHandle( void ) +{ + Assert( IsPooled() ); + return ( IsPooled() ? &m_GPUBufferHandle : NULL ); +} + +//----------------------------------------------------------------------------- +// Update memory allocation data +//----------------------------------------------------------------------------- +inline void CIndexBuffer::SetBufferAllocationHandle( const GPUBufferHandle_t &bufferAllocationHandle ) +{ + // This IB's memory has been reallocated or freed, update our cached pointer and the D3D header + // NOTE: this should never be called while any rendering is in flight! + Assert( ( m_pAllocatedMemory == NULL ) || IsPooled() ); + if ( ( m_pAllocatedMemory == NULL ) || IsPooled() ) + { + m_GPUBufferHandle = bufferAllocationHandle; + m_pAllocatedMemory = m_GPUBufferHandle.pMemory; + if ( m_pIB ) + { + int nBufferSize = m_IndexCount * IndexSize(); + XGSetIndexBufferHeader( nBufferSize, 0, D3DFMT_INDEX16, 0, 0, m_pIB ); + XGOffsetResourceAddress( m_pIB, m_pAllocatedMemory ); + } + } +} + +//----------------------------------------------------------------------------- +// Expose the data pointer for read-only CPU access to the data +//----------------------------------------------------------------------------- +inline const byte **CIndexBuffer::GetBufferDataPointerAddress( void ) +{ + if ( m_bDynamic /* FIXME: || m_bExternalMemory */ ) + return NULL; + return (const byte **)&m_pAllocatedMemory; +} +#endif // _X360 + +//----------------------------------------------------------------------------- +// Do we have enough room without discarding? +//----------------------------------------------------------------------------- +inline bool CIndexBuffer::HasEnoughRoom( int numIndices ) const +{ +#if !defined( _X360 ) + return ( numIndices + m_Position ) <= m_IndexCount; +#else + return numIndices <= m_IndexCount; //the ring buffer will free room as needed +#endif +} + +//----------------------------------------------------------------------------- +// Block until this part of the index buffer is free +//----------------------------------------------------------------------------- +inline void CIndexBuffer::BlockUntilUnused( int nAllocationSize ) +{ + Assert( nAllocationSize <= m_IndexCount ); + +#ifdef _X360 + Assert( (m_AllocationRing.Count() != 0) || ((m_Position == 0) && (m_iNextBlockingPosition == m_iAllocationCount)) ); + + if ( (m_iNextBlockingPosition - m_Position) >= nAllocationSize ) + return; + + Assert( (m_AllocationRing[m_AllocationRing.Head()].m_iStartOffset == 0) || ((m_iNextBlockingPosition == m_AllocationRing[m_AllocationRing.Head()].m_iStartOffset) && (m_Position <= m_iNextBlockingPosition)) ); + + int iMinBlockPosition = m_Position + nAllocationSize; + if( iMinBlockPosition > m_iAllocationCount ) + { + //Allocation requires us to wrap + iMinBlockPosition = nAllocationSize; + m_Position = 0; + + //modify the last allocation so that it uses up the whole tail end of the buffer. Makes other code simpler + Assert( m_AllocationRing.Count() != 0 ); + m_AllocationRing[m_AllocationRing.Tail()].m_iEndOffset = m_iAllocationCount; + + //treat all allocations between the current position and the tail end of the ring as freed since they will be before we unblock + while( m_AllocationRing.Count() ) + { + unsigned int head = m_AllocationRing.Head(); + if( m_AllocationRing[head].m_iStartOffset == 0 ) + break; + + m_AllocationRing.Remove( head ); + } + } + + //now we go through the allocations until we find the last fence we care about. Treat everything up until that fence as freed. + DWORD FinalFence = 0; + unsigned int iFinalAllocationZPassIdx = 0; + while( m_AllocationRing.Count() ) + { + unsigned int head = m_AllocationRing.Head(); + + if( m_AllocationRing[head].m_iEndOffset >= iMinBlockPosition ) + { + //When this frees, we'll finally have enough space for the allocation + FinalFence = m_AllocationRing[head].m_Fence; + iFinalAllocationZPassIdx = m_AllocationRing[head].m_iZPassIdx; + m_iNextBlockingPosition = m_AllocationRing[head].m_iEndOffset; + m_AllocationRing.Remove( head ); + break; + } + m_AllocationRing.Remove( head ); + } + Assert( FinalFence != 0 ); + + if( Dx9Device()->IsFencePending( FinalFence ) ) + { +#ifdef SPEW_INDEX_BUFFER_STALLS + float st = Plat_FloatTime(); +#endif + + if ( ( Dx9Device()->GetDeviceState() & D3DDEVICESTATE_ZPASS_BRACKET ) && + ( iFinalAllocationZPassIdx == ShaderAPI()->Get360ZPassCounter() ) ) + { + // We're about to overrun our IB ringbuffer in a single Z prepass. To avoid rendering corruption, close out the + // Z prepass and continue. This will reduce early-Z rejection efficiency and could cause a momentary framerate drop, + // but it's better than rendering corruption. + Warning( "Dynamic IB ring buffer overrun in Z Prepass. Tell Thorsten.\n" ); + + ShaderAPI()->End360ZPass(); + } + + Dx9Device()->BlockOnFence( FinalFence ); + +#ifdef SPEW_INDEX_BUFFER_STALLS + float dt = Plat_FloatTime() - st; + Warning( "Blocked locking dynamic index buffer for %f ms!\n", 1000.0 * dt ); +#endif + } + +#endif +} + + +//----------------------------------------------------------------------------- +// lock, unlock +//----------------------------------------------------------------------------- +inline unsigned short* CIndexBuffer::Lock( bool bReadOnly, int numIndices, int& startIndex, int startPosition ) +{ + Assert( !m_bLocked ); + +#if defined( _X360 ) + if ( m_pIB && m_pIB->IsSet( Dx9Device() ) ) + { + Unbind( m_pIB ); + } +#endif + + unsigned short* pLockedData = NULL; + + // For write-combining, ensure we always have locked memory aligned to 4-byte boundaries + if( m_bDynamic ) + numIndices = ALIGN_VALUE( numIndices, 2 ); + + // Ensure there is enough space in the IB for this data + if ( numIndices > m_IndexCount ) + { + Error( "too many indices for index buffer. . tell a programmer (%d>%d)\n", ( int )numIndices, ( int )m_IndexCount ); + Assert( false ); + return 0; + } + + if ( !IsX360() && !m_pIB && !m_pSysmemBuffer ) + return 0; + + DWORD dwFlags; + + if ( m_bDynamic ) + { + // startPosition now can be != -1, when calling in here with a static (staging) buffer. +#if !defined( _X360 ) + dwFlags = LOCKFLAGS_APPEND; + + // If either user forced us to flush, + // or there is not enough space for the vertex data, + // then flush the buffer contents + // xbox must not append at position 0 because nooverwrite cannot be guaranteed + + if ( !m_Position || m_bFlush || !HasEnoughRoom(numIndices) ) + { + if ( m_pSysmemBuffer || !g_pShaderUtil->IsRenderThreadSafe() ) + m_bLateCreateShouldDiscard = true; + + m_bFlush = false; + m_Position = 0; + + dwFlags = LOCKFLAGS_FLUSH; + } +#else + if ( m_bFlush ) + { +# if ( defined( X360_BLOCK_ON_IB_FLUSH ) ) + { + if( m_AllocationRing.Count() ) + { + DWORD FinalFence = m_AllocationRing[m_AllocationRing.Tail()].m_Fence; + + m_AllocationRing.RemoveAll(); + m_Position = 0; + m_iNextBlockingPosition = m_iAllocationCount; + +# if ( defined( SPEW_VERTEX_BUFFER_STALLS ) ) + if( Dx9Device()->IsFencePending( FinalFence ) ) + { + float st = Plat_FloatTime(); +# endif + Dx9Device()->BlockOnFence( FinalFence ); +# if ( defined ( SPEW_VERTEX_BUFFER_STALLS ) ) + float dt = Plat_FloatTime() - st; + Warning( "Blocked FLUSHING dynamic index buffer for %f ms!\n", 1000.0 * dt ); + } +# endif + } + } +# endif + m_bFlush = false; + } +#endif + } + else + { + dwFlags = D3DLOCK_NOSYSLOCK; + } + + if ( bReadOnly ) + { + dwFlags |= D3DLOCK_READONLY; + } + + int position = m_Position; + if( startPosition >= 0 ) + { + position = startPosition; + } + + RECORD_COMMAND( DX8_LOCK_INDEX_BUFFER, 4 ); + RECORD_INT( m_UID ); + RECORD_INT( position * IndexSize() ); + RECORD_INT( numIndices * IndexSize() ); + RECORD_INT( dwFlags ); + + m_LockedStartIndex = position; + m_LockedNumIndices = numIndices; + + HRESULT hr = D3D_OK; + +#if !defined( _X360 ) + // If the caller isn't in the thread that owns the render lock, need to return a system memory pointer--cannot talk to GL from + // the non-current thread. + if ( !m_pSysmemBuffer && !g_pShaderUtil->IsRenderThreadSafe() ) + { + m_pSysmemBuffer = ( byte * )malloc( m_IndexCount * IndexSize() ); + m_nSysmemBufferStartBytes = position * IndexSize(); + } + + if ( m_pSysmemBuffer != NULL ) + { + // Ensure that we're never moving backwards in a buffer--this code would need to be rewritten if so. + // We theorize this can happen if you hit the end of a buffer and then wrap before drawing--but + // this would probably break in other places as well. + Assert( position * IndexSize() >= m_nSysmemBufferStartBytes ); + pLockedData = ( unsigned short * )( m_pSysmemBuffer + ( position * IndexSize() ) ); + } + else + { + hr = m_pIB->Lock( position * IndexSize(), numIndices * IndexSize(), + reinterpret_cast< void** >( &pLockedData ), dwFlags ); + } +#else + if ( m_bDynamic ) + { + // Block until earlier parts of the buffer are free + BlockUntilUnused( numIndices ); + position = m_Position; + m_pIB = NULL; + Assert( (m_Position + numIndices) <= m_iAllocationCount ); + } + else + { + //static, block until last lock finished? + m_Position = position; + } + pLockedData = (unsigned short *)(m_pAllocatedMemory + (position * IndexSize())); + +#endif + + switch ( hr ) + { + case D3DERR_INVALIDCALL: + Msg( "D3DERR_INVALIDCALL - Index Buffer Lock Failed in %s on line %d(offset %d, size %d, flags 0x%x)\n", V_UnqualifiedFileName(__FILE__), __LINE__, position * IndexSize(), numIndices * IndexSize(), dwFlags ); + break; + case D3DERR_DRIVERINTERNALERROR: + Msg( "D3DERR_DRIVERINTERNALERROR - Index Buffer Lock Failed in %s on line %d (offset %d, size %d, flags 0x%x)\n", V_UnqualifiedFileName(__FILE__), __LINE__, position * IndexSize(), numIndices * IndexSize(), dwFlags ); + break; + case D3DERR_OUTOFVIDEOMEMORY: + Msg( "D3DERR_OUTOFVIDEOMEMORY - Index Buffer Lock Failed in %s on line %d (offset %d, size %d, flags 0x%x)\n", V_UnqualifiedFileName(__FILE__), __LINE__, position * IndexSize(), numIndices * IndexSize(), dwFlags ); + break; + } + + Assert( pLockedData != NULL ); + + if ( !IsX360() ) + { + startIndex = position; + } + else + { + startIndex = 0; + } + + Assert( m_bLocked == false ); + m_bLocked = true; + return pLockedData; +} + +inline void CIndexBuffer::Unlock( int numIndices ) +{ +#if defined( _X360 ) + Assert( (m_Position + numIndices) <= m_iAllocationCount ); +#else + Assert( (m_Position + numIndices) <= m_IndexCount ); +#endif + + if ( !m_bLocked ) + return; + + // For write-combining, ensure we always have locked memory aligned to 4-byte boundaries +// if( m_bDynamic ) +// numIndices = ALIGN_VALUE( numIndices, 2 ); + + if ( !IsX360() && !m_pIB && !m_pSysmemBuffer ) + return; + + RECORD_COMMAND( DX8_UNLOCK_INDEX_BUFFER, 1 ); + RECORD_INT( m_UID ); + +#if !defined( _X360 ) + if ( m_pSysmemBuffer ) + { + } + else + { +#if DX_TO_GL_ABSTRACTION + // Knowing how much data was actually written is critical for performance under OpenGL. + // Important notes: numIndices indicates how much we could move the current position. For dynamic buffer, it should indicate the # of actually written indices, for static buffers it's typically 0. + // If it's a dynamic buffer (where we actually care about perf), assume the caller isn't lying about numIndices, otherwise just assume they wrote the entire thing. + // If you modify this code, be sure to test on both AMD and NVidia drivers! + Assert( numIndices <= (int)m_LockedNumIndices ); + int unlockBytes = ( m_bDynamic ? numIndices : m_LockedNumIndices ) * IndexSize(); +#else + int unlockBytes = 0; +#endif + ReallyUnlock( unlockBytes ); + } +#else + if ( m_bDynamic ) + { + Assert( (m_Position == 0) || (m_AllocationRing[m_AllocationRing.Tail()].m_iEndOffset == m_Position) ); + + DynamicBufferAllocation_t LockData; + LockData.m_Fence = Dx9Device()->GetCurrentFence(); //This isn't the correct fence, but it's all we have access to for now and it'll provide marginal safety if something goes really wrong. + LockData.m_iStartOffset = m_Position; + LockData.m_iEndOffset = LockData.m_iStartOffset + numIndices; + LockData.m_iZPassIdx = ( Dx9Device()->GetDeviceState() & D3DDEVICESTATE_ZPASS_BRACKET ) ? ShaderAPI()->Get360ZPassCounter() : 0; + Assert( (LockData.m_iStartOffset == 0) || (LockData.m_iStartOffset == m_AllocationRing[m_AllocationRing.Tail()].m_iEndOffset) ); + m_AllocationRing.AddToTail( LockData ); + + void* pLockedData = m_pAllocatedMemory + (LockData.m_iStartOffset * IndexSize()); + + //Always re-use the same index buffer header based on the assumption that D3D copies it off in the draw calls. + m_pIB = &m_D3DIndexBuffer; + XGSetIndexBufferHeader( numIndices * IndexSize(), 0, D3DFMT_INDEX16, 0, 0, m_pIB ); + XGOffsetResourceAddress( m_pIB, pLockedData ); + + // Invalidate the GPU caches for this memory. + // FIXME: Should dynamic allocations be 4k aligned? + Dx9Device()->InvalidateGpuCache( pLockedData, numIndices * IndexSize(), 0 ); + } + else + { + if ( !m_pIB ) + { + int nBufferSize = m_IndexCount * IndexSize(); + XGSetIndexBufferHeader( nBufferSize, 0, D3DFMT_INDEX16, 0, 0, &m_D3DIndexBuffer ); + XGOffsetResourceAddress( &m_D3DIndexBuffer, m_pAllocatedMemory ); + m_pIB = &m_D3DIndexBuffer; + } + + // Invalidate the GPU caches for this memory. + Dx9Device()->InvalidateGpuCache( m_pAllocatedMemory, m_IndexCount * IndexSize(), 0 ); + } +#endif + + m_Position += numIndices; + m_bLocked = false; + + m_LockedStartIndex = 0; + m_LockedNumIndices = 0; +} + + +inline void CIndexBuffer::HandleLateCreation( ) +{ + if ( !m_pSysmemBuffer ) + { + return; + } + + if( !m_pIB ) + { + bool bPrior = g_VBAllocTracker->TrackMeshAllocations( "HandleLateCreation" ); + Create( Dx9Device() ); + if ( !bPrior ) + { + g_VBAllocTracker->TrackMeshAllocations( NULL ); + } + } + + void* pWritePtr = NULL; + const int dataToWriteBytes = ( m_Position * IndexSize() ) - m_nSysmemBufferStartBytes; + DWORD dwFlags = D3DLOCK_NOSYSLOCK; + if ( m_bDynamic ) + dwFlags |= ( m_bLateCreateShouldDiscard ? D3DLOCK_DISCARD : D3DLOCK_NOOVERWRITE ); + + // Always clear this. + m_bLateCreateShouldDiscard = false; + + // Don't use the Lock function, it does a bunch of stuff we don't want. + HRESULT hr = m_pIB->Lock( m_nSysmemBufferStartBytes, + dataToWriteBytes, + &pWritePtr, + dwFlags); + + // If this fails we're about to crash. Consider skipping the update and leaving + // m_pSysmemBuffer around to try again later. (For example in case of device loss) + Assert( SUCCEEDED( hr ) ); hr; + memcpy( pWritePtr, m_pSysmemBuffer + m_nSysmemBufferStartBytes, dataToWriteBytes ); + ReallyUnlock( dataToWriteBytes ); + + free( m_pSysmemBuffer ); + m_pSysmemBuffer = NULL; +} + + +// Returns the allocated size +inline int CIndexBuffer::AllocationSize() const +{ +#ifdef _X360 + return m_iAllocationCount * IndexSize(); +#else + return m_IndexCount * IndexSize(); +#endif +} + +inline int CIndexBuffer::AllocationCount() const +{ +#ifdef _X360 + return m_iAllocationCount; +#else + return m_IndexCount; +#endif +} + +#ifdef _WIN32 +#pragma warning (default:4189) +#endif + +#include "tier0/memdbgoff.h" + +#endif // DYNAMICIB_H + diff --git a/materialsystem/shaderapidx9/dynamicvb.h b/materialsystem/shaderapidx9/dynamicvb.h new file mode 100644 index 0000000..5eeee8f --- /dev/null +++ b/materialsystem/shaderapidx9/dynamicvb.h @@ -0,0 +1,1098 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//===========================================================================// + +#ifndef DYNAMICVB_H +#define DYNAMICVB_H + +#ifdef _WIN32 +#pragma once +#endif + +#include "locald3dtypes.h" +#include "recording.h" +#include "shaderapidx8_global.h" +#include "shaderapidx8.h" +#include "imeshdx8.h" +#include "materialsystem/ivballoctracker.h" +#include "gpubufferallocator.h" +#include "tier1/utllinkedlist.h" +#include "tier0/dbg.h" +#include "tier1/memstack.h" + +///////////////////////////// +// D. Sim Dietrich Jr. +// sim.dietrich@nvidia.com +////////////////////// + + +// Helper function to unbind an vertex buffer +void Unbind( IDirect3DVertexBuffer9 *pVertexBuffer ); + +#define X360_VERTEX_BUFFER_SIZE_MULTIPLIER 2.0 //minimum of 1, only affects dynamic buffers +//#define X360_BLOCK_ON_VB_FLUSH //uncomment to block until all data is consumed when a flush is requested. Otherwise we only block when absolutely necessary + +//#define SPEW_VERTEX_BUFFER_STALLS //uncomment to allow buffer stall spewing. + + +class CVertexBuffer +{ +public: + CVertexBuffer( IDirect3DDevice9 * pD3D, VertexFormat_t fmt, DWORD theFVF, int vertexSize, + int theVertexCount, const char *pTextureBudgetName, bool bSoftwareVertexProcessing, bool dynamic = false ); + +#ifdef _X360 + CVertexBuffer(); + void Init( IDirect3DDevice9 * pD3D, VertexFormat_t fmt, DWORD theFVF, uint8 *pVertexData, int vertexSize, int theVertexCount ); +#endif + + ~CVertexBuffer(); + + LPDIRECT3DVERTEXBUFFER GetInterface() const + { + // If this buffer still exists, then Late Creation didn't happen. Best case: we'll render the wrong image. Worst case: Crash. + Assert( !m_pSysmemBuffer ); + return m_pVB; + } + + // Use at beginning of frame to force a flush of VB contents on first draw + void FlushAtFrameStart() { m_bFlush = true; } + + // lock, unlock + unsigned char* Lock( int numVerts, int& baseVertexIndex ); + unsigned char* Modify( bool bReadOnly, int firstVertex, int numVerts ); + void Unlock( int numVerts ); + + void HandleLateCreation( ); + + // Vertex size + int VertexSize() const { return m_VertexSize; } + + // Vertex count + int VertexCount() const { return m_VertexCount; } +#ifdef _X360 + // For some VBs, memory allocation is managed by CGPUBufferAllocator, via ShaderAPI + const GPUBufferHandle_t *GetBufferAllocationHandle( void ); + void SetBufferAllocationHandle( const GPUBufferHandle_t &bufferAllocationHandle ); + bool IsPooled( void ) { creturn m_GPUBufferHandle.IsValid(); } + // Expose the data pointer for read-only CPU access to the data + // (double-indirection supports relocation of the data by CGPUBufferAllocator) + const byte **GetBufferDataPointerAddress( void ); +#endif // _X360 + + static int BufferCount() + { +#ifdef _DEBUG + return s_BufferCount; +#else + return 0; +#endif + } + + // UID + unsigned int UID() const + { +#ifdef RECORDING + return m_UID; +#else + return 0; +#endif + } + + void HandlePerFrameTextureStats( int frame ) + { +#ifdef VPROF_ENABLED + if ( m_Frame != frame && !m_bDynamic ) + { + m_Frame = frame; + m_pFrameCounter += m_nBufferSize; + } +#endif + } + + // Do we have enough room without discarding? + bool HasEnoughRoom( int numVertices ) const; + + // Is this dynamic? + bool IsDynamic() const { return m_bDynamic; } + bool IsExternal() const { return m_bExternalMemory; } + + // Block until this part of the vertex buffer is free + void BlockUntilUnused( int nBufferSize ); + + // used to alter the characteristics after creation + // allows one dynamic vb to be shared for multiple formats + void ChangeConfiguration( int vertexSize, int totalSize ) + { + Assert( m_bDynamic && !m_bLocked && vertexSize ); + m_VertexSize = vertexSize; + m_VertexCount = m_nBufferSize / vertexSize; + } + + // Compute the next offset for the next lock + int NextLockOffset( ) const; + + // Returns the allocated size + int AllocationSize() const; + + // Returns the number of vertices we have enough room for + int NumVerticesUntilFlush() const + { +#if defined( _X360 ) + if( m_AllocationRing.Count() ) + { + //Cycle through the ring buffer and see what memory is free now + int iNode = m_AllocationRing.Head(); + while( m_AllocationRing.IsValidIndex( iNode ) ) + { + if( Dx9Device()->IsFencePending( m_AllocationRing[iNode].m_Fence ) ) + break; + + iNode = m_AllocationRing.Next( iNode ); + } + + if( m_AllocationRing.IsValidIndex( iNode ) ) + { + int iEndFreeOffset = m_AllocationRing[iNode].m_iEndOffset; + if( iEndFreeOffset < m_Position ) + { + //Wrapped. Making the arbitrary decision that the return value for this function *should* handle the singe giant allocation case which requires contiguous memory + if( iEndFreeOffset > (m_iNextBlockingPosition - m_Position) ) + return iEndFreeOffset / m_VertexSize; + else + return (m_iNextBlockingPosition - m_Position) / m_VertexSize; + } + } + else + { + //we didn't block on any fence + return m_VertexCount; + } + } + + return m_VertexCount; +#else + return (m_nBufferSize - NextLockOffset()) / m_VertexSize; +#endif + } + + // Marks a fence indicating when this buffer was used + void MarkUsedInRendering() + { +#ifdef _X360 + if ( m_bDynamic && m_pVB ) + { + Assert( m_AllocationRing.Count() > 0 ); + m_AllocationRing[m_AllocationRing.Tail()].m_Fence = Dx9Device()->GetCurrentFence(); + } +#endif + } + +private: + void Create( IDirect3DDevice9 *pD3D ); + inline void ReallyUnlock( int unlockBytes ) + { + #if DX_TO_GL_ABSTRACTION + // Knowing how much data was actually written is critical for performance under OpenGL. + m_pVB->UnlockActualSize( unlockBytes ); + #else + unlockBytes; // Unused here + m_pVB->Unlock(); + #endif + } + + enum LOCK_FLAGS + { + LOCKFLAGS_FLUSH = D3DLOCK_NOSYSLOCK | D3DLOCK_DISCARD, +#if !defined( _X360 ) + LOCKFLAGS_APPEND = D3DLOCK_NOSYSLOCK | D3DLOCK_NOOVERWRITE +#else + // X360BUG: forcing all locks to gpu flush, otherwise bizarre mesh corruption on decals + // Currently iterating with microsoft 360 support to track source of gpu corruption + LOCKFLAGS_APPEND = D3DLOCK_NOSYSLOCK +#endif + }; + + LPDIRECT3DVERTEXBUFFER m_pVB; + +#ifdef _X360 + struct DynamicBufferAllocation_t + { + DWORD m_Fence; //track whether this memory is safe to use again. + int m_iStartOffset; + int m_iEndOffset; + unsigned int m_iZPassIdx; // The zpass during which this allocation was made + }; + + int m_iNextBlockingPosition; // m_iNextBlockingPosition >= m_Position where another allocation is still in use. + unsigned char *m_pAllocatedMemory; + int m_iAllocationSize; //Total size of the ring buffer, usually more than what was asked for + IDirect3DVertexBuffer9 m_D3DVertexBuffer; //Only need one shared D3D header for our usage patterns. + CUtlLinkedList m_AllocationRing; //tracks what chunks of our memory are potentially still in use by D3D + + GPUBufferHandle_t m_GPUBufferHandle; // Handle to a memory allocation within a shared physical memory pool (see CGPUBufferAllocator) +#endif + + VertexFormat_t m_VertexBufferFormat; // yes, Vertex, only used for allocation tracking + int m_nBufferSize; + int m_Position; + int m_VertexCount; + int m_VertexSize; + DWORD m_TheFVF; + byte *m_pSysmemBuffer; + int m_nSysmemBufferStartBytes; + + uint m_nLockCount; + unsigned char m_bDynamic : 1; + unsigned char m_bLocked : 1; + unsigned char m_bFlush : 1; + unsigned char m_bExternalMemory : 1; + unsigned char m_bSoftwareVertexProcessing : 1; + unsigned char m_bLateCreateShouldDiscard : 1; + +#ifdef VPROF_ENABLED + int m_Frame; + int *m_pFrameCounter; + int *m_pGlobalCounter; +#endif + +#ifdef _DEBUG + static int s_BufferCount; +#endif + +#ifdef RECORDING + unsigned int m_UID; +#endif +}; + +#if defined( _X360 ) +#include "utlmap.h" +MEMALLOC_DECLARE_EXTERNAL_TRACKING( XMem_CVertexBuffer ); +#endif + +//----------------------------------------------------------------------------- +// constructor, destructor +//----------------------------------------------------------------------------- +inline CVertexBuffer::CVertexBuffer(IDirect3DDevice9 * pD3D, VertexFormat_t fmt, DWORD theFVF, + int vertexSize, int vertexCount, const char *pTextureBudgetName, + bool bSoftwareVertexProcessing, bool dynamic ) : + m_pVB(0), + m_Position(0), + m_VertexSize(vertexSize), + m_VertexCount(vertexCount), + m_bFlush(true), + m_bLocked(false), + m_bExternalMemory( false ), + m_nBufferSize(vertexSize * vertexCount), + m_TheFVF( theFVF ), + m_bSoftwareVertexProcessing( bSoftwareVertexProcessing ), + m_bDynamic(dynamic), + m_VertexBufferFormat( fmt ), + m_bLateCreateShouldDiscard( false ) +#ifdef _X360 + ,m_pAllocatedMemory(NULL) + ,m_iNextBlockingPosition(0) + ,m_iAllocationSize(0) +#endif +#ifdef VPROF_ENABLED + ,m_Frame( -1 ) +#endif +{ + MEM_ALLOC_CREDIT_( pTextureBudgetName ); + +#ifdef RECORDING + // assign a UID + static unsigned int uid = 0; + m_UID = uid++; +#endif + +#ifdef _DEBUG + ++s_BufferCount; +#endif + +#ifdef VPROF_ENABLED + if ( !m_bDynamic ) + { + char name[256]; + V_strcpy_safe( name, "TexGroup_global_" ); + V_strcat_safe( name, pTextureBudgetName, sizeof(name) ); + m_pGlobalCounter = g_VProfCurrentProfile.FindOrCreateCounter( name, COUNTER_GROUP_TEXTURE_GLOBAL ); + + V_strcpy_safe( name, "TexGroup_frame_" ); + V_strcat_safe( name, pTextureBudgetName, sizeof(name) ); + m_pFrameCounter = g_VProfCurrentProfile.FindOrCreateCounter( name, COUNTER_GROUP_TEXTURE_PER_FRAME ); + } + else + { + m_pGlobalCounter = g_VProfCurrentProfile.FindOrCreateCounter( "TexGroup_global_" TEXTURE_GROUP_DYNAMIC_VERTEX_BUFFER, COUNTER_GROUP_TEXTURE_GLOBAL ); + } +#endif + + if ( !g_pShaderUtil->IsRenderThreadSafe() ) + { + m_pSysmemBuffer = ( byte * )MemAlloc_AllocAligned( m_nBufferSize, 16 ); + m_nSysmemBufferStartBytes = 0; + } + else + { + m_pSysmemBuffer = NULL; + Create( pD3D ); + } + +#ifdef VPROF_ENABLED + if ( IsX360() || !m_bDynamic ) + { + Assert( m_pGlobalCounter ); + *m_pGlobalCounter += m_nBufferSize; + } +#endif +} + + +void CVertexBuffer::Create( IDirect3DDevice9 *pD3D ) +{ + D3DVERTEXBUFFER_DESC desc; + memset( &desc, 0x00, sizeof( desc ) ); + desc.Format = D3DFMT_VERTEXDATA; + desc.Size = m_nBufferSize; + desc.Type = D3DRTYPE_VERTEXBUFFER; + desc.Pool = m_bDynamic ? D3DPOOL_DEFAULT : D3DPOOL_MANAGED; + desc.FVF = m_TheFVF; + +#if defined(IS_WINDOWS_PC) && defined(SHADERAPIDX9) + extern bool g_ShaderDeviceUsingD3D9Ex; + if ( g_ShaderDeviceUsingD3D9Ex ) + { + desc.Pool = D3DPOOL_DEFAULT; + } +#endif + + desc.Usage = D3DUSAGE_WRITEONLY; + if ( m_bDynamic ) + { + desc.Usage |= D3DUSAGE_DYNAMIC; + // Dynamic meshes should never be compressed (slows down writing to them) + Assert( CompressionType( m_TheFVF ) == VERTEX_COMPRESSION_NONE ); + } + if ( m_bSoftwareVertexProcessing ) + { + desc.Usage |= D3DUSAGE_SOFTWAREPROCESSING; + } + +#if !defined( _X360 ) + RECORD_COMMAND( DX8_CREATE_VERTEX_BUFFER, 6 ); + RECORD_INT( m_UID ); + RECORD_INT( m_nBufferSize ); + RECORD_INT( desc.Usage ); + RECORD_INT( desc.FVF ); + RECORD_INT( desc.Pool ); + RECORD_INT( m_bDynamic ); + + HRESULT hr = pD3D->CreateVertexBuffer( m_nBufferSize, desc.Usage, desc.FVF, desc.Pool, &m_pVB, NULL ); + + if ( hr == D3DERR_OUTOFVIDEOMEMORY || hr == E_OUTOFMEMORY ) + { + // Don't have the memory for this. Try flushing all managed resources + // out of vid mem and try again. + // FIXME: need to record this + pD3D->EvictManagedResources(); + pD3D->CreateVertexBuffer( m_nBufferSize, desc.Usage, desc.FVF, desc.Pool, &m_pVB, NULL ); + } + +#ifdef _DEBUG + if ( hr != D3D_OK ) + { + switch ( hr ) + { + case D3DERR_INVALIDCALL: + Assert( !"D3DERR_INVALIDCALL" ); + break; + case D3DERR_OUTOFVIDEOMEMORY: + Assert( !"D3DERR_OUTOFVIDEOMEMORY" ); + break; + case E_OUTOFMEMORY: + Assert( !"E_OUTOFMEMORY" ); + break; + default: + Assert( 0 ); + break; + } + } +#endif + + Assert( m_pVB ); +#else + // _X360 + if ( m_bDynamic ) + { + m_iAllocationSize = m_nBufferSize * X360_VERTEX_BUFFER_SIZE_MULTIPLIER; + Assert( m_iAllocationSize >= m_nBufferSize ); + m_pAllocatedMemory = (unsigned char*)XPhysicalAlloc( m_iAllocationSize, MAXULONG_PTR, 0, PAGE_READWRITE | MEM_LARGE_PAGES | PAGE_WRITECOMBINE ); + } + else if ( MeshMgr()->AllocatePooledVB( this, m_nBufferSize, pTextureBudgetName ) ) + { + // Successfully allocated in a shared ShaderAPI memory pool (SetBufferAllocationHandle will have been called to set the pointer and stream offset) + m_iAllocationSize = m_nBufferSize; + Assert( m_pAllocatedMemory ); + } + else + { + // Fall back to allocating a standalone VB + // NOTE: write-combining (PAGE_WRITECOMBINE) is deliberately not used, since it slows down CPU access to the data (decals+defragmentation) + m_iAllocationSize = m_nBufferSize; + m_pAllocatedMemory = (unsigned char*)XPhysicalAlloc( m_iAllocationSize, MAXULONG_PTR, 0, PAGE_READWRITE ); + } + + if ( m_pAllocatedMemory && !IsPooled() ) + { + MemAlloc_RegisterExternalAllocation( XMem_CVertexBuffer, m_pAllocatedMemory, XPhysicalSize( m_pAllocatedMemory ) ); + if ( !m_bDynamic ) + { + // Track non-pooled physallocs, to help tune CGPUBufferAllocator usage + g_SizeIndividualVBPhysAllocs += XPhysicalSize( m_pAllocatedMemory ); + g_NumIndividualVBPhysAllocs++; + } + } + + m_iNextBlockingPosition = m_iAllocationSize; +#endif + +#ifdef MEASURE_DRIVER_ALLOCATIONS + int nMemUsed = 1024; + VPROF_INCREMENT_GROUP_COUNTER( "vb count", COUNTER_GROUP_NO_RESET, 1 ); + VPROF_INCREMENT_GROUP_COUNTER( "vb driver mem", COUNTER_GROUP_NO_RESET, nMemUsed ); + VPROF_INCREMENT_GROUP_COUNTER( "total driver mem", COUNTER_GROUP_NO_RESET, nMemUsed ); +#endif + + // Track VB allocations +#if !defined( _X360 ) + g_VBAllocTracker->CountVB( m_pVB, m_bDynamic, m_nBufferSize, m_VertexSize, m_VertexBufferFormat ); +#else + g_VBAllocTracker->CountVB( this, m_bDynamic, m_iAllocationSize, m_VertexSize, m_VertexBufferFormat ); +#endif +} + + +#ifdef _X360 +void *AllocateTempBuffer( size_t nSizeInBytes ); + +//----------------------------------------------------------------------------- +// This variant is for when we already have the data in physical memory +//----------------------------------------------------------------------------- +inline CVertexBuffer::CVertexBuffer( ) : + m_pVB( 0 ), + m_Position( 0 ), + m_VertexSize( 0 ), + m_VertexCount( 0 ), + m_bFlush( false ), + m_bLocked( false ), + m_bExternalMemory( true ), + m_nBufferSize( 0 ), + m_bDynamic( false ) +#ifdef VPROF_ENABLED + ,m_Frame( -1 ) +#endif +{ + m_iAllocationSize = 0; + m_pAllocatedMemory = 0; + m_iNextBlockingPosition = 0; +} + +#include "tier0/memdbgoff.h" + +inline void CVertexBuffer::Init( IDirect3DDevice9 *pD3D, VertexFormat_t fmt, DWORD theFVF, uint8 *pVertexData, int vertexSize, int vertexCount ) +{ + m_nBufferSize = vertexSize * vertexCount; + m_Position = m_Position; + m_VertexSize = vertexSize; + m_VertexCount = vertexCount; + m_iAllocationSize = m_nBufferSize; + m_pAllocatedMemory = pVertexData; + m_iNextBlockingPosition = m_iAllocationSize; + + m_pVB = new( AllocateTempBuffer( sizeof( IDirect3DVertexBuffer9 ) ) ) IDirect3DVertexBuffer9; + XGSetVertexBufferHeader( m_nBufferSize, 0, 0, 0, m_pVB ); + XGOffsetResourceAddress( m_pVB, pVertexData ); +} + +#include "tier0/memdbgon.h" + +#endif // _X360 + +inline CVertexBuffer::~CVertexBuffer() +{ + // Track VB allocations (even if pooled) +#if !defined( _X360 ) + if ( m_pVB != NULL ) + { + g_VBAllocTracker->UnCountVB( m_pVB ); + } +#else + if ( m_pVB && m_pVB->IsSet( Dx9Device() ) ) + { + Unbind( m_pVB ); + } + + if ( !m_bExternalMemory ) + { + g_VBAllocTracker->UnCountVB( this ); + } +#endif + + if ( !m_bExternalMemory ) + { +#ifdef MEASURE_DRIVER_ALLOCATIONS + int nMemUsed = 1024; + VPROF_INCREMENT_GROUP_COUNTER( "vb count", COUNTER_GROUP_NO_RESET, -1 ); + VPROF_INCREMENT_GROUP_COUNTER( "vb driver mem", COUNTER_GROUP_NO_RESET, -nMemUsed ); + VPROF_INCREMENT_GROUP_COUNTER( "total driver mem", COUNTER_GROUP_NO_RESET, -nMemUsed ); +#endif + +#ifdef VPROF_ENABLED + if ( IsX360() || !m_bDynamic ) + { + Assert( m_pGlobalCounter ); + *m_pGlobalCounter -= m_nBufferSize; + } +#endif + +#ifdef _DEBUG + --s_BufferCount; +#endif + } + + Unlock( 0 ); + + if ( m_pSysmemBuffer ) + { + MemAlloc_FreeAligned( m_pSysmemBuffer ); + m_pSysmemBuffer = NULL; + } + +#if !defined( _X360 ) + if ( m_pVB ) + { + RECORD_COMMAND( DX8_DESTROY_VERTEX_BUFFER, 1 ); + RECORD_INT( m_UID ); + + m_pVB->Release(); + } +#else + if ( m_pAllocatedMemory && !m_bExternalMemory ) + { + if ( IsPooled() ) + { + MeshMgr()->DeallocatePooledVB( this ); + } + else + { + MemAlloc_RegisterExternalDeallocation( XMem_CVertexBuffer, m_pAllocatedMemory, XPhysicalSize( m_pAllocatedMemory ) ); + if ( !m_bDynamic ) + { + // Track non-pooled physallocs, to help tune CGPUBufferAllocator usage + g_SizeIndividualVBPhysAllocs -= XPhysicalSize( m_pAllocatedMemory ); + g_NumIndividualVBPhysAllocs--; + } + XPhysicalFree( m_pAllocatedMemory ); + } + } + + m_pAllocatedMemory = NULL; + m_pVB = NULL; +#endif // _X360 +} +#ifdef _X360 +//----------------------------------------------------------------------------- +// Get memory allocation data +//----------------------------------------------------------------------------- +inline const GPUBufferHandle_t *CVertexBuffer::GetBufferAllocationHandle( void ) +{ + Assert( IsPooled() ); + return ( IsPooled() ? &m_GPUBufferHandle : NULL ); +} + +//----------------------------------------------------------------------------- +// Update memory allocation data +//----------------------------------------------------------------------------- +inline void CVertexBuffer::SetBufferAllocationHandle( const GPUBufferHandle_t &bufferAllocationHandle ) +{ + // This VB's memory has been reallocated or freed, update our cached pointer and the D3D header + // NOTE: this should never be called while any rendering is in flight! + Assert( ( m_pAllocatedMemory == NULL ) || IsPooled() ); + if ( ( m_pAllocatedMemory == NULL ) || IsPooled() ) + { + m_GPUBufferHandle = bufferAllocationHandle; + m_pAllocatedMemory = m_GPUBufferHandle.pMemory; + if ( m_pVB ) + { + XGSetVertexBufferHeader( m_nBufferSize, 0, D3DPOOL_DEFAULT, 0, m_pVB ); + XGOffsetResourceAddress( m_pVB, m_pAllocatedMemory ); + } + } +} + +//----------------------------------------------------------------------------- +// Expose the data pointer for read-only CPU access to the data +//----------------------------------------------------------------------------- +inline const byte **CVertexBuffer::GetBufferDataPointerAddress( void ) +{ + if ( m_bDynamic /* FIXME: || m_bExternalMemory*/ ) + return NULL; + return (const byte **)&m_pAllocatedMemory; +} +#endif // _X360 + +//----------------------------------------------------------------------------- +// Compute the next offset for the next lock +//----------------------------------------------------------------------------- +inline int CVertexBuffer::NextLockOffset( ) const +{ +#if !defined( _X360 ) + int nNextOffset = ( m_Position + m_VertexSize - 1 ) / m_VertexSize; + nNextOffset *= m_VertexSize; + return nNextOffset; +#else + return m_Position; //position is already aligned properly on unlocks for 360. +#endif +} + + +//----------------------------------------------------------------------------- +// Do we have enough room without discarding? +//----------------------------------------------------------------------------- +inline bool CVertexBuffer::HasEnoughRoom( int numVertices ) const +{ +#if defined( _X360 ) + return numVertices <= m_VertexCount; //the ring buffer will free room as needed +#else + return (NextLockOffset() + (numVertices * m_VertexSize)) <= m_nBufferSize; +#endif +} + +//----------------------------------------------------------------------------- +// Block until this part of the index buffer is free +//----------------------------------------------------------------------------- +inline void CVertexBuffer::BlockUntilUnused( int nBufferSize ) +{ + Assert( nBufferSize <= m_nBufferSize ); + +#ifdef _X360 + int nLockOffset = NextLockOffset(); + Assert( (m_AllocationRing.Count() != 0) || ((m_Position == 0) && (m_iNextBlockingPosition == m_iAllocationSize)) ); + + if ( (m_iNextBlockingPosition - nLockOffset) >= nBufferSize ) + return; + + Assert( (m_AllocationRing[m_AllocationRing.Head()].m_iStartOffset == 0) || ((m_iNextBlockingPosition == m_AllocationRing[m_AllocationRing.Head()].m_iStartOffset) && (m_Position <= m_iNextBlockingPosition)) ); + + int iMinBlockPosition = nLockOffset + nBufferSize; + if( iMinBlockPosition > m_iAllocationSize ) + { + //Allocation requires us to wrap + iMinBlockPosition = nBufferSize; + m_Position = 0; + + //modify the last allocation so that it uses up the whole tail end of the buffer. Makes other code simpler + Assert( m_AllocationRing.Count() != 0 ); + m_AllocationRing[m_AllocationRing.Tail()].m_iEndOffset = m_iAllocationSize; + + //treat all allocations between the current position and the tail end of the ring as freed since they will be before we unblock + while( m_AllocationRing.Count() ) + { + unsigned int head = m_AllocationRing.Head(); + if( m_AllocationRing[head].m_iStartOffset == 0 ) + break; + + m_AllocationRing.Remove( head ); + } + } + + //now we go through the allocations until we find the last fence we care about. Treat everything up until that fence as freed. + DWORD FinalFence = 0; + unsigned int iFinalAllocationZPassIdx = 0; + while( m_AllocationRing.Count() ) + { + unsigned int head = m_AllocationRing.Head(); + + if( m_AllocationRing[head].m_iEndOffset >= iMinBlockPosition ) + { + //When this frees, we'll finally have enough space for the allocation + FinalFence = m_AllocationRing[head].m_Fence; + iFinalAllocationZPassIdx = m_AllocationRing[head].m_iZPassIdx; + m_iNextBlockingPosition = m_AllocationRing[head].m_iEndOffset; + m_AllocationRing.Remove( head ); + break; + } + m_AllocationRing.Remove( head ); + } + Assert( FinalFence != 0 ); + + if( Dx9Device()->IsFencePending( FinalFence ) ) + { +#ifdef SPEW_VERTEX_BUFFER_STALLS + float st = Plat_FloatTime(); +#endif + + if ( ( Dx9Device()->GetDeviceState() & D3DDEVICESTATE_ZPASS_BRACKET ) && + ( iFinalAllocationZPassIdx == ShaderAPI()->Get360ZPassCounter() ) ) + { + // We're about to overrun our VB ringbuffer in a single Z prepass. To avoid rendering corruption, close out the + // Z prepass and continue. This will reduce early-Z rejection efficiency and could cause a momentary framerate drop, + // but it's better than rendering corruption. + Warning( "Dynamic VB ring buffer overrun in Z Prepass. Tell Thorsten.\n" ); + + ShaderAPI()->End360ZPass(); + } + + Dx9Device()->BlockOnFence( FinalFence ); + +#ifdef SPEW_VERTEX_BUFFER_STALLS + float dt = Plat_FloatTime() - st; + Warning( "Blocked locking dynamic vertex buffer for %f ms!\n", 1000.0 * dt ); +#endif + } + +#endif +} + + +//----------------------------------------------------------------------------- +// lock, unlock +//----------------------------------------------------------------------------- +inline unsigned char* CVertexBuffer::Lock( int numVerts, int& baseVertexIndex ) +{ +#if defined( _X360 ) + if ( m_pVB && m_pVB->IsSet( Dx9Device() ) ) + { + Unbind( m_pVB ); + } +#endif + + m_nLockCount = numVerts; + + unsigned char* pLockedData = 0; + baseVertexIndex = 0; + int nBufferSize = numVerts * m_VertexSize; + + Assert( IsPC() || ( IsX360() && !m_bLocked ) ); + + // Ensure there is enough space in the VB for this data + if ( numVerts > m_VertexCount ) + { + Assert( 0 ); + return 0; + } + + if ( !IsX360() && !m_pVB && !m_pSysmemBuffer ) + return 0; + + DWORD dwFlags; + if ( m_bDynamic ) + { + dwFlags = LOCKFLAGS_APPEND; + +#if !defined( _X360 ) + // If either the user forced us to flush, + // or there is not enough space for the vertex data, + // then flush the buffer contents + if ( !m_Position || m_bFlush || !HasEnoughRoom(numVerts) ) + { + if ( m_pSysmemBuffer || !g_pShaderUtil->IsRenderThreadSafe() ) + m_bLateCreateShouldDiscard = true; + m_bFlush = false; + m_Position = 0; + + dwFlags = LOCKFLAGS_FLUSH; + } +#else + if( m_bFlush ) + { +# if ( defined( X360_BLOCK_ON_VB_FLUSH ) ) + { + if( m_AllocationRing.Count() ) + { + DWORD FinalFence = m_AllocationRing[m_AllocationRing.Tail()].m_Fence; + + m_AllocationRing.RemoveAll(); + m_Position = 0; + m_iNextBlockingPosition = m_iAllocationSize; + +# if ( defined( SPEW_VERTEX_BUFFER_STALLS ) ) + if( Dx9Device()->IsFencePending( FinalFence ) ) + { + float st = Plat_FloatTime(); +# endif + Dx9Device()->BlockOnFence( FinalFence ); +# if ( defined ( SPEW_VERTEX_BUFFER_STALLS ) ) + float dt = Plat_FloatTime() - st; + Warning( "Blocked FLUSHING dynamic vertex buffer for %f ms!\n", 1000.0 * dt ); + } +# endif + } + } +# endif + m_bFlush = false; + } +#endif + } + else + { + // Since we are a static VB, always lock the beginning of the buffer. + dwFlags = D3DLOCK_NOSYSLOCK; + m_Position = 0; + } + + if ( IsX360() && m_bDynamic ) + { + // Block until we have enough room in the buffer, this affects the result of NextLockOffset() in wrap conditions. + BlockUntilUnused( nBufferSize ); + m_pVB = NULL; + } + + int nLockOffset = NextLockOffset( ); + RECORD_COMMAND( DX8_LOCK_VERTEX_BUFFER, 4 ); + RECORD_INT( m_UID ); + RECORD_INT( nLockOffset ); + RECORD_INT( nBufferSize ); + RECORD_INT( dwFlags ); + +#if !defined( _X360 ) + // If the caller isn't in the thread that owns the render lock, need to return a system memory pointer--cannot talk to GL from + // the non-current thread. + if ( !m_pSysmemBuffer && !g_pShaderUtil->IsRenderThreadSafe() ) + { + m_pSysmemBuffer = ( byte * )MemAlloc_AllocAligned( m_nBufferSize, 16 ); + m_nSysmemBufferStartBytes = nLockOffset; + Assert( ( m_nSysmemBufferStartBytes % m_VertexSize ) == 0 ); + } + + if ( m_pSysmemBuffer != NULL ) + { + // Ensure that we're never moving backwards in a buffer--this code would need to be rewritten if so. + // We theorize this can happen if you hit the end of a buffer and then wrap before drawing--but + // this would probably break in other places as well. + Assert( nLockOffset >= m_nSysmemBufferStartBytes ); + pLockedData = m_pSysmemBuffer + nLockOffset; + } + else + { + m_pVB->Lock( nLockOffset, + nBufferSize, + reinterpret_cast< void** >( &pLockedData ), + dwFlags ); + } +#else + pLockedData = m_pAllocatedMemory + nLockOffset; +#endif + + Assert( pLockedData != 0 ); + m_bLocked = true; + if ( !IsX360() ) + { + baseVertexIndex = nLockOffset / m_VertexSize; + } + else + { + baseVertexIndex = 0; + } + return pLockedData; +} + +inline unsigned char* CVertexBuffer::Modify( bool bReadOnly, int firstVertex, int numVerts ) +{ + unsigned char* pLockedData = 0; + + // D3D still returns a pointer when you call lock with 0 verts, so just in + // case it's actually doing something, don't even try to lock the buffer with 0 verts. + if ( numVerts == 0 ) + return NULL; + + m_nLockCount = numVerts; + + // If this hits, m_pSysmemBuffer logic needs to be added to this code. + Assert( g_pShaderUtil->IsRenderThreadSafe() ); + Assert( !m_pSysmemBuffer ); // if this hits, then we need to add code to handle it + + Assert( m_pVB && !m_bDynamic ); + + if ( firstVertex + numVerts > m_VertexCount ) + { + Assert( 0 ); + return NULL; + } + + DWORD dwFlags = D3DLOCK_NOSYSLOCK; + if ( bReadOnly ) + { + dwFlags |= D3DLOCK_READONLY; + } + + RECORD_COMMAND( DX8_LOCK_VERTEX_BUFFER, 4 ); + RECORD_INT( m_UID ); + RECORD_INT( firstVertex * m_VertexSize ); + RECORD_INT( numVerts * m_VertexSize ); + RECORD_INT( dwFlags ); + + // mmw: for forcing all dynamic... LOCKFLAGS_FLUSH ); +#if !defined( _X360 ) + m_pVB->Lock( + firstVertex * m_VertexSize, + numVerts * m_VertexSize, + reinterpret_cast< void** >( &pLockedData ), + dwFlags ); +#else + if ( m_pVB->IsSet( Dx9Device() ) ) + { + Unbind( m_pVB ); + } + pLockedData = m_pAllocatedMemory + (firstVertex * m_VertexSize); +#endif + + m_Position = firstVertex * m_VertexSize; + Assert( pLockedData != 0 ); + m_bLocked = true; + + return pLockedData; +} + +inline void CVertexBuffer::Unlock( int numVerts ) +{ + if ( !m_bLocked ) + return; + + if ( !IsX360() && !m_pVB && !m_pSysmemBuffer ) + return; + + int nLockOffset = NextLockOffset(); + int nBufferSize = numVerts * m_VertexSize; + + RECORD_COMMAND( DX8_UNLOCK_VERTEX_BUFFER, 1 ); + RECORD_INT( m_UID ); + +#if !defined( _X360 ) + if ( m_pSysmemBuffer != NULL ) + { + } + else + { + #if DX_TO_GL_ABSTRACTION + Assert( numVerts <= (int)m_nLockCount ); + int unlockBytes = ( m_bDynamic ? nBufferSize : ( m_nLockCount * m_VertexSize ) ); + #else + int unlockBytes = 0; + #endif + + ReallyUnlock( unlockBytes ); + } + m_Position = nLockOffset + nBufferSize; +#else + if ( m_bDynamic ) + { + if ( numVerts > 0 ) + { + DynamicBufferAllocation_t LockData; + LockData.m_Fence = Dx9Device()->GetCurrentFence(); //This isn't the correct fence, but it's all we have access to for now and it'll provide marginal safety if something goes really wrong. + LockData.m_iStartOffset = nLockOffset; + LockData.m_iEndOffset = LockData.m_iStartOffset + nBufferSize; + LockData.m_iZPassIdx = ( Dx9Device()->GetDeviceState() & D3DDEVICESTATE_ZPASS_BRACKET ) ? ShaderAPI()->Get360ZPassCounter() : 0; + + // Round dynamic locks to 4k boundaries for GPU cache reasons + LockData.m_iEndOffset = ALIGN_VALUE( LockData.m_iEndOffset, 4096 ); + if( LockData.m_iEndOffset > m_iAllocationSize ) + LockData.m_iEndOffset = m_iAllocationSize; + + m_AllocationRing.AddToTail( LockData ); + m_Position = LockData.m_iEndOffset; + + void* pLockedData = m_pAllocatedMemory + LockData.m_iStartOffset; + + //Always re-use the same vertex buffer header based on the assumption that D3D copies it off in the draw calls. + m_pVB = &m_D3DVertexBuffer; + XGSetVertexBufferHeader( nBufferSize, 0, D3DPOOL_DEFAULT, 0, m_pVB ); + XGOffsetResourceAddress( m_pVB, pLockedData ); + + // Invalidate the GPU caches for this memory. + Dx9Device()->InvalidateGpuCache( pLockedData, nBufferSize, 0 ); + } + } + else + { + if ( !m_pVB ) + { + m_pVB = &m_D3DVertexBuffer; + XGSetVertexBufferHeader( m_nBufferSize, 0, D3DPOOL_DEFAULT, 0, m_pVB ); + XGOffsetResourceAddress( m_pVB, m_pAllocatedMemory ); + } + m_Position = nLockOffset + nBufferSize; + + // Invalidate the GPU caches for this memory. + Dx9Device()->InvalidateGpuCache( m_pAllocatedMemory, m_nBufferSize, 0 ); + } +#endif + + m_bLocked = false; +} + + +inline void CVertexBuffer::HandleLateCreation( ) +{ + if ( !m_pSysmemBuffer ) + { + return; + } + + if( !m_pVB ) + { + bool bPrior = g_VBAllocTracker->TrackMeshAllocations( "HandleLateCreation" ); + Create( Dx9Device() ); + if ( !bPrior ) + { + g_VBAllocTracker->TrackMeshAllocations( NULL ); + } + } + + void* pWritePtr = NULL; + const int dataToWriteBytes = m_bDynamic ? ( m_Position - m_nSysmemBufferStartBytes ) : ( m_nLockCount * m_VertexSize ); + DWORD dwFlags = D3DLOCK_NOSYSLOCK; + if ( m_bDynamic ) + { + dwFlags |= ( m_bLateCreateShouldDiscard ? D3DLOCK_DISCARD : D3DLOCK_NOOVERWRITE ); + } + + // Always clear this. + m_bLateCreateShouldDiscard = false; + + // Don't use the Lock function, it does a bunch of stuff we don't want. + HRESULT hr = m_pVB->Lock( m_nSysmemBufferStartBytes, + dataToWriteBytes, + &pWritePtr, + dwFlags); + + // If this fails we're about to crash. Consider skipping the update and leaving + // m_pSysmemBuffer around to try again later. (For example in case of device loss) + Assert( SUCCEEDED( hr ) ); hr; + memcpy( pWritePtr, m_pSysmemBuffer + m_nSysmemBufferStartBytes, dataToWriteBytes ); + ReallyUnlock( dataToWriteBytes ); + + MemAlloc_FreeAligned( m_pSysmemBuffer ); + m_pSysmemBuffer = NULL; +} + + +// Returns the allocated size +inline int CVertexBuffer::AllocationSize() const +{ +#ifdef _X360 + return m_iAllocationSize; +#else + return m_VertexCount * m_VertexSize; +#endif +} + + +#endif // DYNAMICVB_H + diff --git a/materialsystem/shaderapidx9/gpubufferallocator.cpp b/materialsystem/shaderapidx9/gpubufferallocator.cpp new file mode 100644 index 0000000..e86a009 --- /dev/null +++ b/materialsystem/shaderapidx9/gpubufferallocator.cpp @@ -0,0 +1,480 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: See gpubufferallocator.h +// +// $NoKeywords: $ +// +//===========================================================================// + +#include "gpubufferallocator.h" +#include "dynamicvb.h" +#include "dynamicib.h" + +// NOTE: This has to be the last file included! +#include "tier0/memdbgon.h" + +#if defined( _X360 ) + + + +//----------------------------------------------------------------------------- +// globals +//----------------------------------------------------------------------------- + +#include "utlmap.h" +MEMALLOC_DEFINE_EXTERNAL_TRACKING( XMem_CGPUBufferPool ); + +// Track non-pooled VB/IB physical allocations (used by CGPUBufferAllocator::SpewStats) +CInterlockedInt g_NumIndividualVBPhysAllocs = 0; +CInterlockedInt g_SizeIndividualVBPhysAllocs = 0; +CInterlockedInt g_NumIndividualIBPhysAllocs = 0; +CInterlockedInt g_SizeIndividualIBPhysAllocs = 0; + + + +//============================================================================= +//============================================================================= +// CGPUBufferAllocator +//============================================================================= +//============================================================================= + +CGPUBufferAllocator::CGPUBufferAllocator( void ) + : m_nBufferPools( 0 ), + m_bEnabled( true ) +{ + memset( &( m_BufferPools[ 0 ] ), 0, sizeof( m_BufferPools ) ); + + m_bEnabled = USE_GPU_BUFFER_ALLOCATOR && !CommandLine()->FindParm( "-no_gpu_buffer_allocator" ); + if ( m_bEnabled ) + { + // Start with one pool (the size should be the lowest-common-denominator for all maps) + AllocatePool( INITIAL_POOL_SIZE ); + } +} + +CGPUBufferAllocator::~CGPUBufferAllocator( void ) +{ + for ( int i = 0; i < m_nBufferPools; i++ ) + { + delete m_BufferPools[ i ]; + } +} + +//----------------------------------------------------------------------------- +// Allocate a new memory pool +//----------------------------------------------------------------------------- +bool CGPUBufferAllocator::AllocatePool( int nPoolSize ) +{ + if ( m_nBufferPools == MAX_POOLS ) + return false; + + m_BufferPools[ m_nBufferPools ] = new CGPUBufferPool( nPoolSize ); + if ( m_BufferPools[ m_nBufferPools ]->m_pMemory == NULL ) + { + // Physical alloc failed! Continue without crashing, we *might* get away with it... + ExecuteOnce( DebuggerBreakIfDebugging() ); + ExecuteNTimes( 15, Warning( "CGPUBufferAllocator::AllocatePool - physical allocation failed! Physical fragmentation is in bad shape... falling back to non-pooled VB/IB allocations. Brace for a crash :o/\n" ) ); + delete m_BufferPools[ m_nBufferPools ]; + m_BufferPools[ m_nBufferPools ] = NULL; + return false; + } + m_nBufferPools++; + return true; +} + +//----------------------------------------------------------------------------- +// Make a new GPUBufferHandle_t to represent a given buffer allocation +//----------------------------------------------------------------------------- +inline GPUBufferHandle_t CGPUBufferAllocator::MakeGPUBufferHandle( int nPoolNum, int nPoolEntry ) +{ + GPUBufferHandle_t newHandle; + newHandle.nPoolNum = nPoolNum; + newHandle.nPoolEntry = nPoolEntry; + newHandle.pMemory = m_BufferPools[ nPoolNum ]->m_pMemory + m_BufferPools[ nPoolNum ]->m_PoolEntries[ nPoolEntry ].nOffset; + return newHandle; +} + +//----------------------------------------------------------------------------- +// Try to allocate a block of the given size from one of our pools +//----------------------------------------------------------------------------- +bool CGPUBufferAllocator::AllocateBuffer( GPUBufferHandle_t *pHandle, int nBufferSize, void *pObject, bool bIsVertexBuffer ) +{ + if ( m_bEnabled && ( nBufferSize <= MAX_BUFFER_SIZE ) ) + { + // Try to allocate at the end of one of our pools + for ( int nPool = 0; nPool < m_nBufferPools; nPool++ ) + { + int nPoolEntry = m_BufferPools[ nPool ]->Allocate( nBufferSize, bIsVertexBuffer, pObject ); + if ( nPoolEntry >= 0 ) + { + // Tada. + *pHandle = MakeGPUBufferHandle( nPool, nPoolEntry ); + return true; + } + if ( nPool == ( m_nBufferPools - 1 ) ) + { + // Allocate a new pool (in which this buffer should DEFINITELY fit!) + COMPILE_TIME_ASSERT( ADDITIONAL_POOL_SIZE >= MAX_BUFFER_SIZE ); + AllocatePool( ADDITIONAL_POOL_SIZE ); + } + } + } + return false; +} + +//----------------------------------------------------------------------------- +// Clear the given allocation from our pools (NOTE: the memory cannot be reused until Defrag() is called) +//----------------------------------------------------------------------------- +void CGPUBufferAllocator::DeallocateBuffer( const GPUBufferHandle_t *pHandle ) +{ + Assert( pHandle ); + if ( pHandle ) + { + Assert( ( pHandle->nPoolNum >= 0 ) && ( pHandle->nPoolNum < m_nBufferPools ) ); + if ( ( pHandle->nPoolNum >= 0 ) && ( pHandle->nPoolNum < m_nBufferPools ) ) + { + m_BufferPools[ pHandle->nPoolNum ]->Deallocate( pHandle ); + } + } +} + +//----------------------------------------------------------------------------- +// If appropriate, allocate this VB's memory from one of our pools +//----------------------------------------------------------------------------- +bool CGPUBufferAllocator::AllocateVertexBuffer( CVertexBuffer *pVertexBuffer, int nBufferSize ) +{ + AUTO_LOCK( m_mutex ); + + bool bIsVertexBuffer = true; + GPUBufferHandle_t handle; + if ( AllocateBuffer( &handle, nBufferSize, (void *)pVertexBuffer, bIsVertexBuffer ) ) + { + // Success - give the VB the handle to this allocation + pVertexBuffer->SetBufferAllocationHandle( handle ); + return true; + } + return false; +} + +//----------------------------------------------------------------------------- +// Deallocate this VB's memory from our pools +//----------------------------------------------------------------------------- +void CGPUBufferAllocator::DeallocateVertexBuffer( CVertexBuffer *pVertexBuffer ) +{ + AUTO_LOCK( m_mutex ); + + // Remove the allocation from the pool and clear the VB's handle + DeallocateBuffer( pVertexBuffer->GetBufferAllocationHandle() ); + pVertexBuffer->SetBufferAllocationHandle( GPUBufferHandle_t() ); +} + +//----------------------------------------------------------------------------- +// If appropriate, allocate this IB's memory from one of our pools +//----------------------------------------------------------------------------- +bool CGPUBufferAllocator::AllocateIndexBuffer( CIndexBuffer *pIndexBuffer, int nBufferSize ) +{ + AUTO_LOCK( m_mutex ); + + bool bIsNOTVertexBuffer = false; + GPUBufferHandle_t handle; + if ( AllocateBuffer( &handle, nBufferSize, (void *)pIndexBuffer, bIsNOTVertexBuffer ) ) + { + // Success - give the IB the handle to this allocation + pIndexBuffer->SetBufferAllocationHandle( handle ); + return true; + } + return false; +} + +//----------------------------------------------------------------------------- +// Deallocate this IB's memory from our pools +//----------------------------------------------------------------------------- +void CGPUBufferAllocator::DeallocateIndexBuffer( CIndexBuffer *pIndexBuffer ) +{ + AUTO_LOCK( m_mutex ); + + // Remove the allocation from the pool and clear the IB's handle + DeallocateBuffer( pIndexBuffer->GetBufferAllocationHandle() ); + pIndexBuffer->SetBufferAllocationHandle( GPUBufferHandle_t() ); +} + +//----------------------------------------------------------------------------- +// Move a buffer from one location to another (could be movement within the same pool) +//----------------------------------------------------------------------------- +void CGPUBufferAllocator::MoveBufferMemory( int nDstPool, int *pnDstEntry, int *pnDstOffset, CGPUBufferPool &srcPool, GPUBufferPoolEntry_t &srcEntry ) +{ + // Move the data + CGPUBufferPool &dstPool = *m_BufferPools[ nDstPool ]; + byte *pDest = dstPool.m_pMemory + *pnDstOffset; + byte *pSource = srcPool.m_pMemory + srcEntry.nOffset; + if ( pDest != pSource ) + V_memmove( pDest, pSource, srcEntry.nSize ); + + // Update the destination pool's allocation entry (NOTE: this could be srcEntry, so srcEntry.nOffset would change) + dstPool.m_PoolEntries[ *pnDstEntry ] = srcEntry; + dstPool.m_PoolEntries[ *pnDstEntry ].nOffset = *pnDstOffset; + + // Tell the VB/IB about the updated allocation + GPUBufferHandle_t newHandle = MakeGPUBufferHandle( nDstPool, *pnDstEntry ); + if ( srcEntry.bIsVertexBuffer ) + srcEntry.pVertexBuffer->SetBufferAllocationHandle( newHandle ); + else + srcEntry.pIndexBuffer->SetBufferAllocationHandle( newHandle ); + + // Move the write address past this entry and increment the pool high water mark + *pnDstOffset += srcEntry.nSize; + *pnDstEntry += 1; + dstPool.m_nBytesUsed += srcEntry.nSize; +} + +//----------------------------------------------------------------------------- +// Reclaim space freed by destroyed buffers and compact our pools ready for new allocations +//----------------------------------------------------------------------------- +void CGPUBufferAllocator::Compact( void ) +{ + // NOTE: this must only be called during map transitions, no rendering must be in flight and everything must be single-threaded! + AUTO_LOCK( m_mutex ); + + // SpewStats(); // pre-compact state + + CFastTimer timer; + timer.Start(); + + // Shuffle all pools to get rid of the empty space occupied by freed buffers. + // We just walk the pools and entries in order, moving each buffer down within the same pool, + // or to the end of a previous pool (if, after compaction, it now has free space). + // Each pool should end up with contiguous, usable free space (may be zero bytes) at the end. + int nDstPool = 0, nDstEntry = 0, nDstOffset = 0; + for ( int nSrcPool = 0; nSrcPool < m_nBufferPools; nSrcPool++ ) + { + CGPUBufferPool &srcPool = *m_BufferPools[ nSrcPool ]; + srcPool.m_nBytesUsed = 0; // Re-fill each pool from scratch + int nEntriesRemainingInPool = 0; + for ( int nSrcEntry = 0; nSrcEntry < srcPool.m_PoolEntries.Count(); nSrcEntry++ ) + { + GPUBufferPoolEntry_t &srcEntry = srcPool.m_PoolEntries[ nSrcEntry ]; + if ( srcEntry.pVertexBuffer ) + { + // First, try to move the buffer into one of the previous (already-compacted) pools + bool bDone = false; + while ( nDstPool < nSrcPool ) + { + CGPUBufferPool &dstPool = *m_BufferPools[ nDstPool ]; + if ( ( nDstOffset + srcEntry.nSize ) <= dstPool.m_nSize ) + { + // Add this buffer to the end of dstPool + Assert( nDstEntry == dstPool.m_PoolEntries.Count() ); + dstPool.m_PoolEntries.AddToTail(); + MoveBufferMemory( nDstPool, &nDstEntry, &nDstOffset, srcPool, srcEntry ); + bDone = true; + break; + } + else + { + // This pool is full, start writing into the next one + nDstPool++; + nDstEntry = 0; + nDstOffset = 0; + } + } + + // If that fails, just shuffle the entry down within srcPool + if ( !bDone ) + { + Assert( nSrcPool == nDstPool ); + MoveBufferMemory( nDstPool, &nDstEntry, &nDstOffset, srcPool, srcEntry ); + nEntriesRemainingInPool++; + } + } + } + + // Discard unused entries from the end of the pool (freed buffers, or buffers moved to other pools) + srcPool.m_PoolEntries.SetCountNonDestructively( nEntriesRemainingInPool ); + } + + // Now free empty pools (keep the first (very large) one around, since fragmentation makes freeing+reallocing it a big risk) + int nBytesFreed = 0; + for ( int nPool = ( m_nBufferPools - 1 ); nPool > 0; nPool-- ) + { + if ( m_BufferPools[ nPool ]->m_PoolEntries.Count() ) + break; + + nBytesFreed += m_BufferPools[ nPool ]->m_nSize; + Assert( m_BufferPools[ nPool ]->m_nBytesUsed == 0 ); + delete m_BufferPools[ nPool ]; + m_nBufferPools--; + } + + if ( m_nBufferPools > 1 ) + { + // The above compaction algorithm could waste space due to large allocs causing nDstPool to increment before that pool + // is actually full. With our current usage pattern (total in-use memory is less than INITIAL_POOL_SIZE, whenever Compact + // is called), that doesn't matter. If that changes (i.e. the below warning fires), then the fix would be: + // - for each pool, sort its entries by size (largest first) and try to allocate them on the end of prior (already-compacted) pools + // - pack whatever remains in the pool down, and proceed to the next pool + ExecuteOnce( Warning( "CGPUBufferAllocator::Compact may be wasting memory due to changed usage patterns (see code for suggested fix)." ) ); + } + +#ifdef _X360 + // Invalidate the GPU caches for all pooled memory, since stuff has moved around + for ( int nPool = 0; nPool < m_nBufferPools; nPool++ ) + { + Dx9Device()->InvalidateGpuCache( m_BufferPools[ nPool ]->m_pMemory, m_BufferPools[ nPool ]->m_nSize, 0 ); + } +#endif + + timer.End(); + float compactTime = (float)timer.GetDuration().GetSeconds(); + Msg( "CGPUBufferAllocator::Compact took %.2f seconds, and freed %.1fkb\n", compactTime, ( nBytesFreed / 1024.0f ) ); + + // SpewStats(); // post-compact state +} + +//----------------------------------------------------------------------------- +// Spew statistics about pool usage, so we can tune our constant values +//----------------------------------------------------------------------------- +void CGPUBufferAllocator::SpewStats( bool bBrief ) +{ + AUTO_LOCK( m_mutex ); + + int nMemAllocated = 0; + int nMemUsed = 0; + int nOldMemWasted = 0; + int nVBsInPools = 0; + int nIBsInPools = 0; + int nFreedBuffers = 0; + int nFreedBufferMem = 0; + for ( int i = 0; i < m_nBufferPools; i++ ) + { + CGPUBufferPool *pool = m_BufferPools[ i ]; + nMemAllocated += pool->m_nSize; + nMemUsed += pool->m_nBytesUsed; + for ( int j = 0; j < pool->m_PoolEntries.Count(); j++ ) + { + GPUBufferPoolEntry_t &poolEntry = pool->m_PoolEntries[ j ]; + if ( poolEntry.pVertexBuffer ) + { + // Figure out how much memory we WOULD have allocated for this buffer, if we'd allocated it individually: + nOldMemWasted += ALIGN_VALUE( poolEntry.nSize, 4096 ) - poolEntry.nSize; + if ( poolEntry.bIsVertexBuffer ) nVBsInPools++; + if ( !poolEntry.bIsVertexBuffer ) nIBsInPools++; + } + else + { + nFreedBuffers++; + nFreedBufferMem += poolEntry.nSize; + } + } + } + + // NOTE: 'unused' memory doesn't count memory used by freed buffers, which should be zero during gameplay. The purpose is + // to measure wastage at the END of a pool, to help determine ideal values for ADDITIONAL_POOL_SIZE and MAX_BUFFER_SIZE. + int nMemUnused = nMemAllocated - nMemUsed; + + const float KB = 1024.0f, MB = KB*KB; + if ( bBrief ) + { + ConMsg( "[GPUBUFLOG] Pools:%2d | Size:%5.1fMB | Unused:%5.1fMB | Freed:%5.1fMB | Unpooled:%5.1fMB\n", + m_nBufferPools, nMemAllocated / MB, nMemUnused / MB, nFreedBufferMem / MB, ( g_SizeIndividualVBPhysAllocs + g_SizeIndividualIBPhysAllocs ) / MB ); + } + else + { + Msg( "\nGPU Buffer Allocator stats:\n" ); + Msg( " -- %5d -- Num Pools allocated\n", m_nBufferPools ); + Msg( " -- %7.1fMB -- Memory allocated to pools\n", nMemAllocated / MB ); + Msg( " -- %7.1fkb -- Unused memory at tail-end of pools\n", nMemUnused / KB ); + Msg( " -- %7.1fkb -- Memory saved by allocating buffers from pools\n", nOldMemWasted / KB ); + Msg( " -- %5d -- Number of VBs allocated from pools\n", nVBsInPools ); + Msg( " -- %5d -- Number of IBs allocated from pools\n", nIBsInPools ); + Msg( " -- %5d -- Number of freed buffers in pools (should be zero during gameplay)\n", nFreedBuffers ); + Msg( " -- %7.1fkb -- Memory used by freed buffers in pools\n", nFreedBufferMem / KB ); + Msg( " -- %7.1fkb -- Mem allocated for NON-pooled VBs (%d VBs)\n", g_SizeIndividualVBPhysAllocs / KB, g_NumIndividualVBPhysAllocs ); + Msg( " -- %7.1fkb -- Mem allocated for NON-pooled IBs (%d IBs)\n", g_SizeIndividualIBPhysAllocs / KB, g_NumIndividualVBPhysAllocs ); + Msg( "\n" ); + } +} + + +//============================================================================= +//============================================================================= +// CGPUBufferPool +//============================================================================= +//============================================================================= + +CGPUBufferPool::CGPUBufferPool( int nSize ) + : m_PoolEntries( POOL_ENTRIES_GROW_SIZE, POOL_ENTRIES_INIT_SIZE ), + m_nSize( 0 ), + m_nBytesUsed( 0 ) +{ + // NOTE: write-combining (PAGE_WRITECOMBINE) is deliberately not used, since it slows down 'Compact' hugely (and doesn't noticeably benefit load times) + m_pMemory = (byte*)XPhysicalAlloc( nSize, MAXULONG_PTR, 0, PAGE_READWRITE ); + if ( m_pMemory ) + { + MemAlloc_RegisterExternalAllocation( XMem_CGPUBufferPool, m_pMemory, XPhysicalSize( m_pMemory ) ); + m_nSize = nSize; + } +} + +CGPUBufferPool::~CGPUBufferPool( void ) +{ + for ( int i = 0; i < m_PoolEntries.Count(); i++ ) + { + if ( m_PoolEntries[ i ].pVertexBuffer ) + { + // Buffers should be cleaned up before the CGPUBufferAllocator is shut down! + Assert( 0 ); + Warning( "ERROR: Un-freed %s in CGPUBufferPool on shut down! (%6.1fKB\n", + ( m_PoolEntries[ i ].bIsVertexBuffer ? "VB" : "IB" ), ( m_PoolEntries[ i ].nSize / 1024.0f ) ); + break; + } + } + + if ( m_pMemory ) + { + MemAlloc_RegisterExternalDeallocation( XMem_CGPUBufferPool, m_pMemory, XPhysicalSize( m_pMemory ) ); + XPhysicalFree( m_pMemory ); + m_pMemory = 0; + } + + m_nSize = m_nBytesUsed = 0; +} + +//----------------------------------------------------------------------------- +// Attempt to allocate a buffer of the given size in this pool +//----------------------------------------------------------------------------- +int CGPUBufferPool::Allocate( int nBufferSize, bool bIsVertexBuffer, void *pObject ) +{ + // Align the buffer size + nBufferSize = ALIGN_VALUE( nBufferSize, POOL_ENTRY_ALIGNMENT ); + + // Check available space + if ( ( m_nBytesUsed + nBufferSize ) > m_nSize ) + return -1; + + int nPoolEntry = m_PoolEntries.AddToTail(); + GPUBufferPoolEntry_t &poolEntry = m_PoolEntries[ nPoolEntry ]; + poolEntry.nOffset = m_nBytesUsed; + poolEntry.nSize = nBufferSize; + poolEntry.bIsVertexBuffer = bIsVertexBuffer; + poolEntry.pVertexBuffer = (CVertexBuffer *)pObject; + + // Update 'used space' high watermark + m_nBytesUsed += nBufferSize; + + return nPoolEntry; +} + +//----------------------------------------------------------------------------- +// Deallocate the given entry from this pool +//----------------------------------------------------------------------------- +void CGPUBufferPool::Deallocate( const GPUBufferHandle_t *pHandle ) +{ + Assert( m_PoolEntries.IsValidIndex( pHandle->nPoolEntry ) ); + if ( m_PoolEntries.IsValidIndex( pHandle->nPoolEntry ) ) + { + Assert( m_PoolEntries[ pHandle->nPoolEntry ].pVertexBuffer ); + m_PoolEntries[ pHandle->nPoolEntry ].pVertexBuffer = NULL; + } +} + +#endif // _X360 diff --git a/materialsystem/shaderapidx9/gpubufferallocator.h b/materialsystem/shaderapidx9/gpubufferallocator.h new file mode 100644 index 0000000..cffd96d --- /dev/null +++ b/materialsystem/shaderapidx9/gpubufferallocator.h @@ -0,0 +1,146 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: CGPUBufferAllocator manages allocation of VBs/IBs from shared memory pools. +// Avoids 4KB physical alloc alignment overhead per VB/IB. +// +// $NoKeywords: $ +// +//===========================================================================// + + +#ifndef GPUBUFFERALLOCATOR_H +#define GPUBUFFERALLOCATOR_H + +#ifdef _WIN32 +#pragma once +#endif + +#ifdef _X360 + +#include "tier1/utlvector.h" +#include "tier1/convar.h" + +class CVertexBuffer; +class CIndexBuffer; + + +// Only active on X360 atm +#define USE_GPU_BUFFER_ALLOCATOR ( IsX360() ) + + +//----------------------------------------------------------------------------- +// A handle to a buffer pool allocation, held by the allocated VB/IB +//----------------------------------------------------------------------------- +struct GPUBufferHandle_t +{ + GPUBufferHandle_t( void ) : nPoolNum( -1 ), pMemory( NULL ), nPoolEntry( -1 ) {} + bool IsValid( void ) { return ( pMemory != NULL ); } + byte * pMemory; // Physical address of the allocation + int nPoolNum; // Identifies the pool + int nPoolEntry; // Identifies this allocation within the pool +}; + +//----------------------------------------------------------------------------- +// Describes an entry in a CGPUBufferPool +//----------------------------------------------------------------------------- +struct GPUBufferPoolEntry_t +{ + int nOffset; + int nSize; + bool bIsVertexBuffer; + union + { + // These are set to NULL by CGPUBufferPool::Free() (called when the VB/IB is destroyed) + CVertexBuffer *pVertexBuffer; + CIndexBuffer *pIndexBuffer; + }; +}; + +//----------------------------------------------------------------------------- +// A single memory block out of which individual VBs/IBs are allocated +//----------------------------------------------------------------------------- +class CGPUBufferPool +{ +public: + CGPUBufferPool( int nSize ); + virtual ~CGPUBufferPool( void ); + + // Returns the index (-1 on failure) of a new allocation in the pool, for a buffer of the given size. + int Allocate( int nSize, bool bIsVertexBuffer, void *pObject ); + // Frees a given entry (just marks it as freed, the memory will not be reused by Allocate() until CGPUBufferAllocator::Defrag() is called ) + void Deallocate( const GPUBufferHandle_t *pHandle ); + +private: + // NOTE: these values are specialized for X360 and should be #ifdef'd for other target platforms + static const int POOL_ENTRIES_INIT_SIZE = 256; + static const int POOL_ENTRIES_GROW_SIZE = 256; + static const int POOL_ENTRY_ALIGNMENT = 4; // 4-byte alignment required for VB/IB data on XBox360 + + byte * m_pMemory; // Pointer to the (physical) address of the pool's memory + int m_nSize; // Total size of the pool + int m_nBytesUsed; // High watermark of used memory in the pool + CUtlVectorm_PoolEntries; // Memory-order array of items allocated in the pool + + // CGPUBufferAllocator is a friend so that CGPUBufferAllocator::Defrag() can shuffle allocations around + friend class CGPUBufferAllocator; +}; + +//----------------------------------------------------------------------------- +// Manages a set of memory blocks out of which individual VBs/IBs are allocated +//----------------------------------------------------------------------------- +class CGPUBufferAllocator +{ +public: + CGPUBufferAllocator( void ); + virtual ~CGPUBufferAllocator( void ); + + // (De)Allocates memory for a vertex/index buffer: + bool AllocateVertexBuffer( CVertexBuffer *pVertexBuffer, int nBufferSize ); + bool AllocateIndexBuffer( CIndexBuffer *pIndexBuffer, int nBufferSize ); + void DeallocateVertexBuffer( CVertexBuffer *pVertexBuffer ); + void DeallocateIndexBuffer( CIndexBuffer *pIndexBuffer ); + + // Compact memory to account for freed buffers + // NOTE: this must only be called during map transitions, no rendering must be in flight and everything must be single-threaded! + void Compact(); + + // Spew statistics about pooled buffer allocations + void SpewStats( bool bBrief = false ); + + +private: + // NOTE: these values are specialized for X360 and should be #ifdef'd for other target platforms + static const int INITIAL_POOL_SIZE = 57*1024*1024 + 256*1024; + static const int ADDITIONAL_POOL_SIZE = 2*1024*1024; + static const int MAX_POOLS = 8; + static const int MAX_BUFFER_SIZE = ADDITIONAL_POOL_SIZE; // 256*1024; + + + // Allocate a new CGPUBufferPool + bool AllocatePool( int nPoolSize ); + // Allocate/deallocate a buffer (type-agnostic) + bool AllocateBuffer( GPUBufferHandle_t *pHandle, int nBufferSize, void *pObject, bool bIsVertexBuffer ); + void DeallocateBuffer( const GPUBufferHandle_t *pHandle ); + // Make a handle for a given allocation + GPUBufferHandle_t MakeGPUBufferHandle( int nPoolNum, int nPoolEntry ); + // Helper for Compact + void MoveBufferMemory( int nDstPool, int *pnDstEntry, int *pnDstOffset, CGPUBufferPool &srcPool, GPUBufferPoolEntry_t &srcEntry ); + + + CGPUBufferPool *m_BufferPools[ MAX_POOLS ]; + int m_nBufferPools; + bool m_bEnabled; + + CThreadFastMutex m_mutex; +}; + + +// Track non-pooled physallocs, to help tune CGPUBufferAllocator usage: +extern CInterlockedInt g_NumIndividualIBPhysAllocs; +extern CInterlockedInt g_SizeIndividualIBPhysAllocs; +extern CInterlockedInt g_NumIndividualVBPhysAllocs; +extern CInterlockedInt g_SizeIndividualVBPhysAllocs; + +#endif // _X360 + +#endif // GPUBUFFERALLOCATOR_H diff --git a/materialsystem/shaderapidx9/hardwareconfig.cpp b/materialsystem/shaderapidx9/hardwareconfig.cpp new file mode 100644 index 0000000..9239630 --- /dev/null +++ b/materialsystem/shaderapidx9/hardwareconfig.cpp @@ -0,0 +1,1323 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// + +#include "togl/rendermechanism.h" +#include "hardwareconfig.h" +#include "shaderapi/ishaderutil.h" +#include "shaderapi_global.h" +#include "materialsystem/materialsystem_config.h" +#include "tier1/convar.h" +#include "shaderdevicebase.h" +#include "tier0/icommandline.h" + + +//----------------------------------------------------------------------------- +// +// Hardware Config! +// +//----------------------------------------------------------------------------- +static CHardwareConfig s_HardwareConfig; +CHardwareConfig *g_pHardwareConfig = &s_HardwareConfig; + +EXPOSE_SINGLE_INTERFACE_GLOBALVAR( CHardwareConfig, IMaterialSystemHardwareConfig, + MATERIALSYSTEM_HARDWARECONFIG_INTERFACE_VERSION, s_HardwareConfig ) + +template +static void ccs_create_convar_from_hwconfig( const T& val, const char *pName ) +{ + int nValue = static_cast( val ); + + ConVar *pConVar = g_pCVar->FindVar( pName ); + if ( pConVar ) + { + pConVar->SetValue( nValue ); + } + else + { + // Don't care if this leaks - this is only used for development + pConVar = new ConVar( pName, "" ); + } + if ( !nValue ) + pConVar->SetValue( "0" ); + else + pConVar->SetValue( nValue ); +} + +static void ccs_create_convar_from_hwconfig( const char *pVal, const char *pName ) +{ + ConVar *pConVar = g_pCVar->FindVar( pName ); + if ( pConVar ) + { + pConVar->SetValue( pVal ); + } + else + { + // Don't care if this leaks - this is only used for development + pConVar = new ConVar( pName, "" ); + } + pConVar->SetValue( pVal ); +} + +CON_COMMAND_F( ccs_create_convars_from_hwconfig, "Create convars from the current hardware config, useful for diffing purposes", FCVAR_CHEAT ) +{ + if ( !g_pHardwareConfig ) + return; + + const HardwareCaps_t &caps = g_pHardwareConfig->CapsForEdit(); + +#define HWCFG( Name ) ccs_create_convar_from_hwconfig( caps.Name, #Name ) + HWCFG( m_NumTextureStages ); + HWCFG( m_nMaxAnisotropy ); + HWCFG( m_MaxTextureWidth ); + HWCFG( m_MaxTextureHeight ); + HWCFG( m_MaxTextureDepth ); + HWCFG( m_MaxTextureAspectRatio ); + HWCFG( m_MaxPrimitiveCount ); + HWCFG( m_NumPixelShaderConstants ); + HWCFG( m_NumBooleanPixelShaderConstants ); + HWCFG( m_NumIntegerPixelShaderConstants ); + HWCFG( m_NumVertexShaderConstants ); + HWCFG( m_NumBooleanVertexShaderConstants ); + HWCFG( m_NumIntegerVertexShaderConstants ); + HWCFG( m_TextureMemorySize ); + HWCFG( m_MaxNumLights ); + HWCFG( m_MaxBlendMatrices ); + HWCFG( m_MaxBlendMatrixIndices ); + HWCFG( m_MaxVertexShaderBlendMatrices ); + HWCFG( m_MaxUserClipPlanes ); + HWCFG( m_HDRType ); + HWCFG( m_pShaderDLL ); + HWCFG( m_ShadowDepthTextureFormat ); + HWCFG( m_NullTextureFormat ); + HWCFG( m_nVertexTextureCount ); + HWCFG( m_nMaxVertexTextureDimension ); + HWCFG( m_AlphaToCoverageState ); + HWCFG( m_AlphaToCoverageEnableValue ); + HWCFG( m_AlphaToCoverageDisableValue ); + HWCFG( m_nMaxViewports ); + HWCFG( m_flMinGammaControlPoint ); + HWCFG( m_flMaxGammaControlPoint ); + HWCFG( m_nGammaControlPointCount ); + HWCFG( m_MaxVertexShader30InstructionSlots ); + HWCFG( m_MaxPixelShader30InstructionSlots ); + HWCFG( m_MaxSimultaneousRenderTargets ); + HWCFG( m_bDeviceOk ); + HWCFG( m_HasSetDeviceGammaRamp ); + HWCFG( m_SupportsVertexShaders ); + HWCFG( m_SupportsVertexShaders_2_0 ); + HWCFG( m_SupportsPixelShaders ); + HWCFG( m_SupportsPixelShaders_1_4 ); + HWCFG( m_SupportsPixelShaders_2_0 ); + HWCFG( m_SupportsPixelShaders_2_b ); + HWCFG( m_SupportsShaderModel_3_0 ); + HWCFG( m_bSupportsAnisotropicFiltering ); + HWCFG( m_bSupportsMagAnisotropicFiltering ); + HWCFG( m_bSupportsVertexTextures ); + HWCFG( m_ZBiasAndSlopeScaledDepthBiasSupported ); + HWCFG( m_SupportsMipmapping ); + HWCFG( m_SupportsOverbright ); + HWCFG( m_SupportsCubeMaps ); + HWCFG( m_SupportsHardwareLighting ); + HWCFG( m_SupportsMipmappedCubemaps ); + HWCFG( m_SupportsNonPow2Textures ); + HWCFG( m_PreferDynamicTextures ); + HWCFG( m_HasProjectedBumpEnv ); + HWCFG( m_SupportsSRGB ); + HWCFG( m_bSupportsSpheremapping ); + HWCFG( m_UseFastClipping ); + HWCFG( m_bNeedsATICentroidHack ); + HWCFG( m_bDisableShaderOptimizations ); + HWCFG( m_bColorOnSecondStream ); + HWCFG( m_bSupportsStreamOffset ); + HWCFG( m_bFogColorSpecifiedInLinearSpace ); + HWCFG( m_bFogColorAlwaysLinearSpace ); + HWCFG( m_bSupportsAlphaToCoverage ); + HWCFG( m_bSupportsShadowDepthTextures ); + HWCFG( m_bSupportsFetch4 ); + HWCFG( m_bSoftwareVertexProcessing ); + HWCFG( m_bScissorSupported ); + HWCFG( m_bSupportsFloat32RenderTargets ); + HWCFG( m_bSupportsBorderColor ); + HWCFG( m_bDX10Card ); + HWCFG( m_bDX10Blending ); + HWCFG( m_bSupportsStaticControlFlow ); + HWCFG( m_FakeSRGBWrite ); + HWCFG( m_CanDoSRGBReadFromRTs ); + HWCFG( m_bSupportsGLMixedSizeTargets ); + HWCFG( m_bCanStretchRectFromTextures ); + + HWCFG( m_MaxHDRType ); +#undef HWCFG +} + +CHardwareConfig::CHardwareConfig() +{ + memset( &m_Caps, 0, sizeof( HardwareCaps_t ) ); + memset( &m_ActualCaps, 0, sizeof( HardwareCaps_t ) ); + memset( &m_UnOverriddenCaps, 0, sizeof( HardwareCaps_t ) ); + +#ifdef POSIX + GLMPRINTF((" CHardwareConfig::CHardwareConfig setting m_bHDREnabled to false on %8x", this )); +#endif + + m_bHDREnabled = false; + + // FIXME: This is kind of a hack to deal with DX8 worldcraft startup. + // We can at least have this much texture + m_Caps.m_MaxTextureWidth = m_Caps.m_MaxTextureHeight = m_Caps.m_MaxTextureDepth = 256; +} + + +//----------------------------------------------------------------------------- + +bool CHardwareConfig::GetHDREnabled( void ) const +{ +// printf("\n CHardwareConfig::GetHDREnabled returning m_bHDREnabled value of %s on %8x", m_bHDREnabled?"true":"false", this ); + return m_bHDREnabled; +} + +void CHardwareConfig::SetHDREnabled( bool bEnable ) +{ +// printf("\n CHardwareConfig::SetHDREnabled setting m_bHDREnabled to value of %s on %8x", bEnable?"true":"false", this ); + m_bHDREnabled = bEnable; +} + + +//----------------------------------------------------------------------------- +// Gets the recommended configuration associated with a particular dx level +//----------------------------------------------------------------------------- +void CHardwareConfig::ForceCapsToDXLevel( HardwareCaps_t *pCaps, int nDxLevel, const HardwareCaps_t &actualCaps ) +{ + if ( !IsPC() || nDxLevel >= 100 ) + return; + + pCaps->m_nDXSupportLevel = nDxLevel; + switch( nDxLevel ) + { + case 60: + // NOTE: Prior to dx9, numsamplers = num texture stages + pCaps->m_NumTextureStages = min( 2, actualCaps.m_NumTextureStages ); + pCaps->m_NumSamplers = pCaps->m_NumTextureStages; + pCaps->m_SupportsVertexShaders = false; + pCaps->m_SupportsVertexShaders_2_0 = false; + pCaps->m_SupportsPixelShaders = false; + pCaps->m_SupportsPixelShaders_1_4 = false; + pCaps->m_SupportsPixelShaders_2_0 = false; + pCaps->m_SupportsPixelShaders_2_b = false; + pCaps->m_SupportsShaderModel_3_0 = false; + pCaps->m_bSupportsStaticControlFlow = false; + pCaps->m_SupportsCompressedTextures = COMPRESSED_TEXTURES_OFF; + pCaps->m_SupportsCompressedVertices = VERTEX_COMPRESSION_NONE; + pCaps->m_bSupportsAnisotropicFiltering = false; + pCaps->m_bSupportsMagAnisotropicFiltering = false; + pCaps->m_nMaxAnisotropy = 1; + pCaps->m_MaxTextureWidth = max( 256, pCaps->m_MaxTextureWidth ); + pCaps->m_MaxTextureHeight = max( 256, pCaps->m_MaxTextureHeight ); + pCaps->m_MaxTextureDepth = max( 256, pCaps->m_MaxTextureDepth ); + // m_MaxTextureAspectRatio; + // int m_MaxPrimitiveCount; + pCaps->m_ZBiasAndSlopeScaledDepthBiasSupported = false; + // pCaps->m_SupportsMipmapping = + // bool m_SupportsOverbright; + pCaps->m_SupportsCubeMaps = false; + pCaps->m_NumPixelShaderConstants = 0; + pCaps->m_NumBooleanPixelShaderConstants = 0; + pCaps->m_NumIntegerPixelShaderConstants = 0; + pCaps->m_NumVertexShaderConstants = 0; + pCaps->m_NumBooleanVertexShaderConstants = 0; + pCaps->m_NumIntegerVertexShaderConstants = 0; + pCaps->m_NumBooleanPixelShaderConstants = 0; + pCaps->m_NumIntegerPixelShaderConstants = 0; + pCaps->m_TextureMemorySize = 32 * 1024 * 1024; + pCaps->m_MaxNumLights = 0; + pCaps->m_SupportsHardwareLighting = 0; + pCaps->m_MaxBlendMatrices = 0; + pCaps->m_MaxBlendMatrixIndices = 0; + pCaps->m_MaxVertexShaderBlendMatrices = 0; + pCaps->m_SupportsMipmappedCubemaps = false; + pCaps->m_SupportsNonPow2Textures = false; + // pCaps->m_DXSupportLevel = 60; + pCaps->m_PreferDynamicTextures = false; + pCaps->m_HasProjectedBumpEnv = false; + pCaps->m_MaxUserClipPlanes = 0; + pCaps->m_SupportsSRGB = false; + pCaps->m_FakeSRGBWrite = false; + pCaps->m_CanDoSRGBReadFromRTs = true; + pCaps->m_bSupportsGLMixedSizeTargets = false; + pCaps->m_HDRType = HDR_TYPE_NONE; + // pCaps->m_bSupportsSpheremapping = true; + pCaps->m_UseFastClipping = true; + pCaps->m_bNeedsATICentroidHack = false; + pCaps->m_bColorOnSecondStream = false; + pCaps->m_bSupportsStreamOffset = false; + pCaps->m_bFogColorSpecifiedInLinearSpace = false; + pCaps->m_bFogColorAlwaysLinearSpace = false; + pCaps->m_bSupportsAlphaToCoverage = false; + pCaps->m_bSupportsShadowDepthTextures = false; + pCaps->m_bSupportsFetch4 = false; + pCaps->m_bSupportsNormalMapCompression = false; + pCaps->m_bSupportsBorderColor = false; + // m_bSoftwareVertexProcessing + pCaps->m_nVertexTextureCount = 0; + pCaps->m_nMaxVertexTextureDimension = 0; + pCaps->m_bSupportsVertexTextures = false; + pCaps->m_nMaxViewports = 1; + // m_bScissorSupported + pCaps->m_bSupportsFloat32RenderTargets = false; + // ImageFormat m_ShadowDepthTextureFormat; + // ImageFormat m_NullTextureFormat; + // m_AlphaToCoverageState; + // m_AlphaToCoverageEnableValue; + // m_AlphaToCoverageDisableValue; + // m_flMinGammaControlPoint + // m_flMaxGammaControlPoint + // m_nGammaControlPointCount + pCaps->m_MaxVertexShader30InstructionSlots = 0; + pCaps->m_MaxPixelShader30InstructionSlots = 0; + pCaps->m_bCanStretchRectFromTextures = false; + break; + + case 70: + // NOTE: Prior to dx9, numsamplers = num texture stages + pCaps->m_NumTextureStages = min( 2, actualCaps.m_NumTextureStages ); + pCaps->m_NumSamplers = pCaps->m_NumTextureStages; + pCaps->m_SupportsVertexShaders = false; + pCaps->m_SupportsVertexShaders_2_0 = false; + pCaps->m_SupportsPixelShaders = false; + pCaps->m_SupportsPixelShaders_1_4 = false; + pCaps->m_SupportsPixelShaders_2_0 = false; + pCaps->m_SupportsPixelShaders_2_b = false; + pCaps->m_SupportsShaderModel_3_0 = false; + pCaps->m_bSupportsStaticControlFlow = false; + // pCaps->m_SupportsCompressedTextures = true; + pCaps->m_SupportsCompressedVertices = VERTEX_COMPRESSION_NONE; + pCaps->m_bSupportsAnisotropicFiltering = false; + pCaps->m_bSupportsMagAnisotropicFiltering = false; + pCaps->m_nMaxAnisotropy = 1; + // pCaps->m_MaxTextureWidth = max( 256, pCaps->m_MaxTextureWidth ); + // pCaps->m_MaxTextureHeight = max( 256, pCaps->m_MaxTextureHeight ); +// pCaps->m_MaxTextureDepth = max( 256, pCaps->m_MaxTextureDepth ); + // m_MaxTextureAspectRatio; + // int m_MaxPrimitiveCount; + pCaps->m_ZBiasAndSlopeScaledDepthBiasSupported = false; + // pCaps->m_SupportsMipmapping = + // bool m_SupportsOverbright; + // pCaps->m_SupportsCubeMaps = false; + pCaps->m_NumPixelShaderConstants = 0; + pCaps->m_NumBooleanPixelShaderConstants = 0; + pCaps->m_NumIntegerPixelShaderConstants = 0; + pCaps->m_NumBooleanPixelShaderConstants = 0; + pCaps->m_NumIntegerPixelShaderConstants = 0; + pCaps->m_NumVertexShaderConstants = 0; + pCaps->m_NumBooleanVertexShaderConstants = 0; + pCaps->m_NumIntegerVertexShaderConstants = 0; + pCaps->m_TextureMemorySize = 32 * 1024 * 1024; + pCaps->m_MaxNumLights = 2; + pCaps->m_SupportsHardwareLighting = 1; + pCaps->m_MaxBlendMatrices = 0; + pCaps->m_MaxBlendMatrixIndices = 0; + pCaps->m_MaxVertexShaderBlendMatrices = 0; + pCaps->m_SupportsMipmappedCubemaps = false; + pCaps->m_SupportsNonPow2Textures = false; + pCaps->m_nDXSupportLevel = 70; + pCaps->m_PreferDynamicTextures = false; + pCaps->m_HasProjectedBumpEnv = false; + pCaps->m_MaxUserClipPlanes = 0; + pCaps->m_SupportsSRGB = false; + pCaps->m_FakeSRGBWrite = false; + pCaps->m_CanDoSRGBReadFromRTs = true; + pCaps->m_bSupportsGLMixedSizeTargets = false; + pCaps->m_HDRType = HDR_TYPE_NONE; + // pCaps->m_bSupportsSpheremapping = true; + pCaps->m_UseFastClipping = true; + pCaps->m_bNeedsATICentroidHack = false; + // pCaps->m_bColorOnSecondStream = false; // dont' force this! + pCaps->m_bSupportsStreamOffset = false; + pCaps->m_bFogColorSpecifiedInLinearSpace = false; + pCaps->m_bFogColorAlwaysLinearSpace = false; + pCaps->m_bSupportsAlphaToCoverage = false; + pCaps->m_bSupportsShadowDepthTextures = false; + pCaps->m_bSupportsFetch4 = false; + pCaps->m_bSupportsNormalMapCompression = false; + pCaps->m_bSupportsBorderColor = false; + // m_bSoftwareVertexProcessing + pCaps->m_nVertexTextureCount = 0; + pCaps->m_nMaxVertexTextureDimension = 0; + pCaps->m_bSupportsVertexTextures = false; + pCaps->m_nMaxViewports = 1; + // m_bScissorSupported + pCaps->m_bSupportsFloat32RenderTargets = false; + pCaps->m_bDX10Card = false; + pCaps->m_bDX10Blending = false; + pCaps->m_bCanStretchRectFromTextures = false; + break; + + case 80: + // NOTE: Prior to dx9, numsamplers = num texture stages + // We clamp num texture stages to 2, though, since we never use + // fixed-function shaders with more than 2 texture stages + pCaps->m_NumTextureStages = min( 2, actualCaps.m_NumTextureStages ); + pCaps->m_NumSamplers = min( 4, actualCaps.m_NumTextureStages ); + // pCaps->m_SupportsVertexShaders = true; + pCaps->m_SupportsVertexShaders_2_0 = false; + // pCaps->m_SupportsPixelShaders = false; + pCaps->m_SupportsPixelShaders_1_4 = false; + pCaps->m_SupportsPixelShaders_2_0 = false; + pCaps->m_SupportsPixelShaders_2_b = false; + pCaps->m_SupportsShaderModel_3_0 = false; + pCaps->m_bSupportsStaticControlFlow = false; + // pCaps->m_SupportsCompressedTextures = true; + pCaps->m_SupportsCompressedVertices = VERTEX_COMPRESSION_NONE; + // pCaps->m_bSupportsAnisotropicFiltering = false; + // pCaps->m_bSupportsMagAnisotropicFiltering = false; + // pCaps->m_nMaxAnisotropy = 1; + // pCaps->m_MaxTextureWidth = max( 256, pCaps->m_MaxTextureWidth ); + // pCaps->m_MaxTextureHeight = max( 256, pCaps->m_MaxTextureHeight ); + // pCaps->m_MaxTextureDepth = max( 256, pCaps->m_MaxTextureDepth ); + // m_MaxTextureAspectRatio; + // int m_MaxPrimitiveCount; + // pCaps->m_ZBiasAndSlopeScaledDepthBiasSupported = false; + // pCaps->m_SupportsMipmapping = + // bool m_SupportsOverbright; + // pCaps->m_SupportsCubeMaps = false; + pCaps->m_NumPixelShaderConstants = 8; + pCaps->m_NumVertexShaderConstants = min( 96, pCaps->m_NumVertexShaderConstants ); + pCaps->m_NumBooleanVertexShaderConstants = 0; + pCaps->m_NumIntegerVertexShaderConstants = 0; + pCaps->m_NumBooleanPixelShaderConstants = 0; + pCaps->m_NumIntegerPixelShaderConstants = 0; + // pCaps->m_TextureMemorySize = 32 * 1024 * 1024; + // pCaps->m_MaxNumLights = 0; + // pCaps->m_SupportsHardwareLighting = 0; + // pCaps->m_MaxBlendMatrices = 0; + // pCaps->m_MaxBlendMatrixIndices = 0; + pCaps->m_MaxVertexShaderBlendMatrices = min( 16, pCaps->m_MaxVertexShaderBlendMatrices ); + // pCaps->m_SupportsMipmappedCubemaps = false; + // pCaps->m_SupportsNonPow2Textures = false; + pCaps->m_nDXSupportLevel = 80; + // pCaps->m_PreferDynamicTextures = false; + // pCaps->m_HasProjectedBumpEnv = false; + // pCaps->m_MaxUserClipPlanes = 0; + pCaps->m_bSupportsGLMixedSizeTargets = false; + pCaps->m_HDRType = HDR_TYPE_NONE; + // pCaps->m_bSupportsSpheremapping = true; + // pCaps->m_UseFastClipping = true; + // pCaps->m_bNeedsATICentroidHack = false; + // pCaps->m_bColorOnSecondStream = false; + pCaps->m_bSupportsStreamOffset = false; + pCaps->m_bFogColorSpecifiedInLinearSpace = false; + pCaps->m_bFogColorAlwaysLinearSpace = false; + pCaps->m_bSupportsAlphaToCoverage = false; + pCaps->m_bSupportsShadowDepthTextures = false; + pCaps->m_bSupportsFetch4 = false; + pCaps->m_bSupportsNormalMapCompression = false; + // m_bSoftwareVertexProcessing + pCaps->m_nVertexTextureCount = 0; + pCaps->m_nMaxVertexTextureDimension = 0; + pCaps->m_bSupportsVertexTextures = false; + pCaps->m_MaxNumLights = 2; + pCaps->m_nMaxViewports = 1; + // m_bScissorSupported + pCaps->m_SupportsSRGB = false; + pCaps->m_FakeSRGBWrite = false; + pCaps->m_CanDoSRGBReadFromRTs = true; + pCaps->m_bSupportsGLMixedSizeTargets = false; + pCaps->m_bSupportsFetch4 = false; + pCaps->m_bSupportsBorderColor = false; + pCaps->m_bSupportsFloat32RenderTargets = false; + // ImageFormat m_ShadowDepthTextureFormat; + // ImageFormat m_NullTextureFormat; + // m_AlphaToCoverageState; + // m_AlphaToCoverageEnableValue; + // m_AlphaToCoverageDisableValue; + // m_flMinGammaControlPoint + // m_flMaxGammaControlPoint + // m_nGammaControlPointCount + pCaps->m_MaxVertexShader30InstructionSlots = 0; + pCaps->m_MaxPixelShader30InstructionSlots = 0; + pCaps->m_bDX10Card = false; + pCaps->m_bDX10Blending = false; + pCaps->m_bCanStretchRectFromTextures = false; + break; + + case 81: + // NOTE: Prior to dx9, numsamplers = num texture stages + // We clamp num texture stages to 2, though, since we never use + // fixed-function shaders with more than 2 texture stages + pCaps->m_NumTextureStages = min( 2, actualCaps.m_NumTextureStages ); + pCaps->m_NumSamplers = min( 6, actualCaps.m_NumTextureStages ); + // pCaps->m_SupportsVertexShaders = true; + pCaps->m_SupportsVertexShaders_2_0 = false; + // pCaps->m_SupportsPixelShaders = false; + pCaps->m_SupportsPixelShaders_1_4 = true; + pCaps->m_SupportsPixelShaders_2_0 = false; + pCaps->m_SupportsPixelShaders_2_b = false; + pCaps->m_SupportsShaderModel_3_0 = false; + pCaps->m_bSupportsStaticControlFlow = false; + // pCaps->m_SupportsCompressedTextures = true; + pCaps->m_SupportsCompressedVertices = VERTEX_COMPRESSION_NONE; + // pCaps->m_bSupportsAnisotropicFiltering = false; + // pCaps->m_bSupportsMagAnisotropicFiltering = false; + // pCaps->m_nMaxAnisotropy = 1; + // pCaps->m_MaxTextureWidth = max( 256, pCaps->m_MaxTextureWidth ); + // pCaps->m_MaxTextureHeight = max( 256, pCaps->m_MaxTextureHeight ); + // pCaps->m_MaxTextureDepth = max( 256, pCaps->m_MaxTextureDepth ); + // m_MaxTextureAspectRatio; + // int m_MaxPrimitiveCount; + // pCaps->m_ZBiasAndSlopeScaledDepthBiasSupported = false; + // pCaps->m_SupportsMipmapping = + // bool m_SupportsOverbright; + // pCaps->m_SupportsCubeMaps = false; + pCaps->m_NumPixelShaderConstants = 8; + pCaps->m_NumVertexShaderConstants = min( 96, pCaps->m_NumVertexShaderConstants ); + pCaps->m_NumBooleanVertexShaderConstants = 0; + pCaps->m_NumIntegerVertexShaderConstants = 0; + pCaps->m_NumBooleanPixelShaderConstants = 0; + pCaps->m_NumIntegerPixelShaderConstants = 0; + // pCaps->m_TextureMemorySize = 32 * 1024 * 1024; + pCaps->m_MaxNumLights = 2; + // pCaps->m_SupportsHardwareLighting = 0; + // pCaps->m_MaxBlendMatrices = 0; + // pCaps->m_MaxBlendMatrixIndices = 0; + pCaps->m_MaxVertexShaderBlendMatrices = min( 16, pCaps->m_MaxVertexShaderBlendMatrices ); + // pCaps->m_SupportsMipmappedCubemaps = false; + // pCaps->m_SupportsNonPow2Textures = false; + pCaps->m_nDXSupportLevel = 81; + // pCaps->m_PreferDynamicTextures = false; + // pCaps->m_HasProjectedBumpEnv = false; + // pCaps->m_MaxUserClipPlanes = 0; + pCaps->m_SupportsSRGB = false; + pCaps->m_FakeSRGBWrite = false; + pCaps->m_CanDoSRGBReadFromRTs = true; + pCaps->m_bSupportsGLMixedSizeTargets = false; + pCaps->m_HDRType = HDR_TYPE_NONE; + // pCaps->m_bSupportsSpheremapping = true; + // pCaps->m_UseFastClipping = true; + // pCaps->m_bNeedsATICentroidHack = false; + // pCaps->m_bColorOnSecondStream = false; + pCaps->m_bSupportsStreamOffset = false; + pCaps->m_bFogColorSpecifiedInLinearSpace = false; + pCaps->m_bFogColorAlwaysLinearSpace = false; + pCaps->m_bSupportsAlphaToCoverage = false; + pCaps->m_bSupportsShadowDepthTextures = false; + pCaps->m_bSupportsFetch4 = false; + pCaps->m_bSupportsNormalMapCompression = false; + pCaps->m_bSupportsBorderColor = false; + // m_bSoftwareVertexProcessing + pCaps->m_nVertexTextureCount = 0; + pCaps->m_nMaxVertexTextureDimension = 0; + pCaps->m_bSupportsVertexTextures = false; + pCaps->m_nMaxViewports = 1; + // m_bScissorSupported + pCaps->m_bSupportsFloat32RenderTargets = false; + // ImageFormat m_ShadowDepthTextureFormat; + // ImageFormat m_NullTextureFormat; + // m_AlphaToCoverageState; + // m_AlphaToCoverageEnableValue; + // m_AlphaToCoverageDisableValue; + // m_flMinGammaControlPoint + // m_flMaxGammaControlPoint + // m_nGammaControlPointCount + pCaps->m_MaxVertexShader30InstructionSlots = 0; + pCaps->m_MaxPixelShader30InstructionSlots = 0; + pCaps->m_bDX10Card = false; + pCaps->m_bDX10Blending = false; + pCaps->m_bCanStretchRectFromTextures = false; + break; + + case 90: + pCaps->m_nVertexTextureCount = 0; + pCaps->m_nMaxVertexTextureDimension = 0; + pCaps->m_bSupportsVertexTextures = false; + pCaps->m_bSupportsStreamOffset = true; + pCaps->m_bSupportsGLMixedSizeTargets = true; + + if ( IsOpenGL() ) + { + //FIXME this is way too complicated, we should just check the caps bit from GLM + + pCaps->m_bSupportsStaticControlFlow = false; + + if (1) //(CommandLine()->FindParm("-glslmode")) + { + // rbarris 03Feb10: this is now hardwired because we are defaulting GLSL mode "on". + // so this will mean that the engine will always ask for user clip planes. + // this will misbehave under ARB mode, since ARB shaders won't respect that state. + // it's difficult to make this fluid without teaching the engine about a cap that could change during run. + + pCaps->m_MaxUserClipPlanes = 2; + pCaps->m_UseFastClipping = false; + } + else + { + pCaps->m_MaxUserClipPlanes = 0; + pCaps->m_UseFastClipping = true; + } + + pCaps->m_MaxNumLights = 2; + } + else + { + pCaps->m_bSupportsStaticControlFlow = true; + pCaps->m_MaxNumLights = pCaps->m_SupportsPixelShaders_2_b ? 4 : 2; // 2b gets four lights, 2.0 gets two... + } + + pCaps->m_bSupportsNormalMapCompression = false; + pCaps->m_bSupportsBorderColor = false; + pCaps->m_nMaxViewports = 1; + pCaps->m_NumPixelShaderConstants = 32; + pCaps->m_nMaxVertexTextureDimension = 0; + pCaps->m_MaxVertexShader30InstructionSlots = 0; + pCaps->m_MaxPixelShader30InstructionSlots = 0; + pCaps->m_bCanStretchRectFromTextures = false; + break; + + case 92: + pCaps->m_nVertexTextureCount = 0; + pCaps->m_nMaxVertexTextureDimension = 0; + pCaps->m_bSupportsVertexTextures = false; + pCaps->m_bSupportsBorderColor = false; + // pCaps->m_bSupportsNormalMapCompression = false; + + // 2b gets four lights, 2.0 gets two... + pCaps->m_SupportsShaderModel_3_0 = false; + if ( IsOpenGL() ) + { + if ( IsOSX() ) + { + pCaps->m_bSupportsStaticControlFlow = CommandLine()->CheckParm( "-glslcontrolflow" ) != NULL; + } + else + { + pCaps->m_bSupportsStaticControlFlow = !CommandLine()->CheckParm( "-noglslcontrolflow" ); + } + + pCaps->m_MaxUserClipPlanes = 2; + pCaps->m_UseFastClipping = false; + pCaps->m_MaxNumLights = pCaps->m_bSupportsStaticControlFlow ? MAX_NUM_LIGHTS : ( MAX_NUM_LIGHTS - 2 ); + } + else + { + pCaps->m_MaxNumLights = MAX_NUM_LIGHTS; + } + + pCaps->m_nMaxViewports = 1; + pCaps->m_NumPixelShaderConstants = 32; + pCaps->m_nMaxVertexTextureDimension = 0; + pCaps->m_bDX10Card = false; + pCaps->m_bDX10Blending = false; + pCaps->m_MaxVertexShader30InstructionSlots = 0; + pCaps->m_MaxPixelShader30InstructionSlots = 0; + pCaps->m_bCanStretchRectFromTextures = false; + break; + + case 95: + pCaps->m_bSupportsStreamOffset = true; + pCaps->m_bSupportsStaticControlFlow = true; + pCaps->m_bDX10Card = false; + pCaps->m_bDX10Blending = false; + pCaps->m_MaxNumLights = MAX_NUM_LIGHTS; + pCaps->m_nMaxViewports = 1; + pCaps->m_bSupportsBorderColor = false; + pCaps->m_bCanStretchRectFromTextures = false; + // pCaps->m_bSupportsNormalMapCompression = false; + break; + + case 100: + break; + + default: + Assert( 0 ); + break; + } +} + + +//----------------------------------------------------------------------------- +// Sets up the hardware caps given the specified DX level +//----------------------------------------------------------------------------- +void CHardwareConfig::SetupHardwareCaps( int nDXLevel, const HardwareCaps_t &actualCaps ) +{ + Assert( nDXLevel != 0 ); + + memcpy( &m_Caps, &actualCaps, sizeof(HardwareCaps_t) ); + memcpy( &m_UnOverriddenCaps, &actualCaps, sizeof(HardwareCaps_t) ); + + // Don't bother with fallbacks for DX10 or consoles +#ifdef DX_TO_GL_ABSTRACTION + if ( nDXLevel >= 100 ) +#else + if ( !( IsPC() || IsPosix() ) || ( nDXLevel >= 100 ) ) +#endif + return; + + // Slam the support level to what we were requested + m_Caps.m_nDXSupportLevel = nDXLevel; + if ( m_Caps.m_nDXSupportLevel != m_Caps.m_nMaxDXSupportLevel || CommandLine()->ParmValue( "-maxdxlevel", 0 ) > 0 ) + { + // We're falling back to some other dx level + ForceCapsToDXLevel( &m_Caps, m_Caps.m_nDXSupportLevel, m_ActualCaps ); + } + + // Clamp num texture stages to 2, since it's only used for fixed function + m_Caps.m_NumTextureStages = min( 2, m_Caps.m_NumTextureStages ); + + // Read dxsupport.cfg which has config overrides for particular cards. + g_pShaderDeviceMgr->ReadHardwareCaps( m_Caps, m_Caps.m_nDXSupportLevel ); + + // This is the spot to validate read in caps versus actual caps. + if ( m_Caps.m_MaxUserClipPlanes > m_ActualCaps.m_MaxUserClipPlanes ) + { + m_Caps.m_MaxUserClipPlanes = m_ActualCaps.m_MaxUserClipPlanes; + } + if ( m_Caps.m_MaxUserClipPlanes == 0 ) + { + m_Caps.m_UseFastClipping = true; + } + + if ( IsOpenGL() ) + { + m_Caps.m_MaxNumLights = MIN( m_Caps.m_MaxNumLights, ( m_Caps.m_bSupportsStaticControlFlow && m_Caps.m_SupportsPixelShaders_2_b ) ? MAX_NUM_LIGHTS : ( MAX_NUM_LIGHTS - 2 ) ); + m_Caps.m_bSupportsShadowDepthTextures = true; + } + else // not POSIX or emulated + { + // 2b supports more lights than just 2.0 + if ( m_Caps.m_SupportsPixelShaders_2_b ) + { + m_Caps.m_MaxNumLights = MIN( m_Caps.m_MaxNumLights, MAX_NUM_LIGHTS ); + } + else + { + m_Caps.m_MaxNumLights = MIN( m_Caps.m_MaxNumLights, MAX_NUM_LIGHTS - 2 ); + } + } + + m_Caps.m_MaxNumLights = min( m_Caps.m_MaxNumLights, (int)MAX_NUM_LIGHTS ); + + memcpy( &m_UnOverriddenCaps, &m_Caps, sizeof(HardwareCaps_t) ); +} + + +//----------------------------------------------------------------------------- +// Sets up the hardware caps given the specified DX level +//----------------------------------------------------------------------------- +void CHardwareConfig::SetupHardwareCaps( const ShaderDeviceInfo_t& mode, const HardwareCaps_t &actualCaps ) +{ + memcpy( &m_ActualCaps, &actualCaps, sizeof(HardwareCaps_t) ); + SetupHardwareCaps( mode.m_nDXLevel, actualCaps ); +} + + +void CHardwareConfig::OverrideStreamOffsetSupport( bool bOverrideEnabled, bool bEnableSupport ) +{ + if ( bOverrideEnabled ) + { + m_Caps.m_bSupportsStreamOffset = bEnableSupport; + if ( !m_ActualCaps.m_bSupportsStreamOffset ) + { + m_Caps.m_bSupportsStreamOffset = false; + } + } + else + { + // Go back to default + m_Caps.m_bSupportsStreamOffset = m_UnOverriddenCaps.m_bSupportsStreamOffset; + } +} + + +//----------------------------------------------------------------------------- +// Implementation of IMaterialSystemHardwareConfig +//----------------------------------------------------------------------------- +bool CHardwareConfig::HasDestAlphaBuffer() const +{ + if ( !g_pShaderDevice ) + return false; + return (g_pShaderDevice->GetBackBufferFormat() == IMAGE_FORMAT_BGRA8888); +} + +bool CHardwareConfig::HasStencilBuffer() const +{ + return StencilBufferBits() > 0; +} + +int CHardwareConfig::GetFrameBufferColorDepth() const +{ + if ( !g_pShaderDevice ) + return 0; + return ShaderUtil()->ImageFormatInfo( g_pShaderDevice->GetBackBufferFormat() ).m_NumBytes; +} + +int CHardwareConfig::GetSamplerCount() const +{ + return m_Caps.m_NumSamplers; +} + +bool CHardwareConfig::HasSetDeviceGammaRamp() const +{ + return m_Caps.m_HasSetDeviceGammaRamp; +} + +bool CHardwareConfig::SupportsCompressedTextures() const +{ + Assert( m_Caps.m_SupportsCompressedTextures != COMPRESSED_TEXTURES_NOT_INITIALIZED ); + return m_Caps.m_SupportsCompressedTextures == COMPRESSED_TEXTURES_ON; +} + +VertexCompressionType_t CHardwareConfig::SupportsCompressedVertices() const +{ + return m_Caps.m_SupportsCompressedVertices; +} + +bool CHardwareConfig::SupportsNormalMapCompression() const +{ + return m_Caps.m_bSupportsNormalMapCompression; +} + +bool CHardwareConfig::SupportsBorderColor() const +{ + return m_Caps.m_bSupportsBorderColor; +} + +bool CHardwareConfig::SupportsFetch4() const +{ + return m_Caps.m_bSupportsFetch4; +} + +bool CHardwareConfig::CanStretchRectFromTextures() const +{ + return m_Caps.m_bCanStretchRectFromTextures; +} + +bool CHardwareConfig::SupportsVertexAndPixelShaders() const +{ + if ( (ShaderUtil()->GetConfig().dxSupportLevel != 0) && (GetDXSupportLevel() < 80) ) + return false; + + return m_Caps.m_SupportsPixelShaders; +} + +bool CHardwareConfig::SupportsPixelShaders_1_4() const +{ + if ( (ShaderUtil()->GetConfig().dxSupportLevel != 0) && (GetDXSupportLevel() < 81) ) + return false; + + return m_Caps.m_SupportsPixelShaders_1_4; +} + +bool CHardwareConfig::SupportsPixelShaders_2_0() const +{ + if ( (ShaderUtil()->GetConfig().dxSupportLevel != 0) && (GetDXSupportLevel() < 90) ) + return false; + + return m_Caps.m_SupportsPixelShaders_2_0; +} + +bool CHardwareConfig::SupportsPixelShaders_2_b() const +{ + if ((ShaderUtil()->GetConfig().dxSupportLevel != 0) && + (GetDXSupportLevel() < 90)) + return false; + + return m_Caps.m_SupportsPixelShaders_2_b; +} + +bool CHardwareConfig::SupportsVertexShaders_2_0() const +{ + if ((ShaderUtil()->GetConfig().dxSupportLevel != 0) && + (GetDXSupportLevel() < 90)) + return false; + + return m_Caps.m_SupportsVertexShaders_2_0; +} + +bool CHardwareConfig::SupportsStaticControlFlow() const +{ + return m_Caps.m_bSupportsStaticControlFlow; +} + + +bool CHardwareConfig::SupportsShaderModel_3_0() const +{ + if ((ShaderUtil()->GetConfig().dxSupportLevel != 0) && + (GetDXSupportLevel() < 95)) + return false; + + return m_Caps.m_SupportsShaderModel_3_0; +} + +// If you change these, make the corresponding change in common_ps_fxc.h +#define NVIDIA_PCF_POISSON 0 +#define ATI_NOPCF 1 +#define ATI_NO_PCF_FETCH4 2 + +int CHardwareConfig::GetShadowFilterMode() const +{ + +#ifdef DX_TO_GL_ABSTRACTION + if ( !m_Caps.m_bSupportsShadowDepthTextures ) + return 0; +#else + if ( !m_Caps.m_bSupportsShadowDepthTextures || !ShaderUtil()->GetConfig().ShadowDepthTexture() ) + return 0; +#endif + + switch ( m_Caps.m_ShadowDepthTextureFormat ) + { + case IMAGE_FORMAT_NV_DST16: + case IMAGE_FORMAT_NV_DST24: + + return NVIDIA_PCF_POISSON; // NVIDIA hardware bilinear PCF + + case IMAGE_FORMAT_ATI_DST16: + case IMAGE_FORMAT_ATI_DST24: + + if ( m_Caps.m_bSupportsFetch4 ) + return ATI_NO_PCF_FETCH4; // ATI fetch4 depth texture sampling + + return ATI_NOPCF; // ATI vanilla depth texture sampling + +#if defined( _X360 ) + case IMAGE_FORMAT_X360_DST16: + case IMAGE_FORMAT_X360_DST24: + case IMAGE_FORMAT_X360_DST24F: + return 0; +#endif + + default: + return 0; + } + + return 0; +} + +static ConVar r_shader_srgb( "r_shader_srgb", "0", FCVAR_CHEAT, "-1 = use hardware caps. 0 = use hardware srgb. 1 = use shader srgb(software lookup)" ); // -1=use caps 0=off 1=on + +int CHardwareConfig::NeedsShaderSRGBConversion() const +{ + if ( IsX360() ) + { + // 360 always now uses a permanent hw solution + return false; + } + + int cValue = r_shader_srgb.GetInt(); + switch( cValue ) + { + case 0: + return false; + + case 1: + return true; + + default: + return m_Caps.m_bDX10Blending; // !!! change to return false after portal deport built!!!!! + } +} + +bool CHardwareConfig::UsesSRGBCorrectBlending() const +{ + int cValue = r_shader_srgb.GetInt(); + return ( cValue == 0 ) && ( ( m_Caps.m_bDX10Blending ) || IsX360() ); +} + +static ConVar mat_disablehwmorph( "mat_disablehwmorph", "0", FCVAR_CHEAT, "Disables HW morphing for particular mods" ); +bool CHardwareConfig::HasFastVertexTextures() const +{ + static int bEnableFastVertexTextures = -1; + static bool bDisableHWMorph = false; + if ( bEnableFastVertexTextures < 0 ) + { + bEnableFastVertexTextures = 1; + if ( CommandLine()->FindParm( "-disallowhwmorph" ) ) + { + bEnableFastVertexTextures = 0; + } + bDisableHWMorph = ( mat_disablehwmorph.GetInt() != 0 ); + } + + // JasonM - turned this off for Orange Box release... + return false; + +// return m_Caps.m_bDX10Card && ( GetDXSupportLevel() >= 95 ) && ( bEnableFastVertexTextures != 0 ) && ( !bDisableHWMorph ); +} + +int CHardwareConfig::MaxHWMorphBatchCount() const +{ + return ShaderUtil()->MaxHWMorphBatchCount(); +} + +int CHardwareConfig::MaximumAnisotropicLevel() const +{ + return m_Caps.m_nMaxAnisotropy; +} + +int CHardwareConfig::MaxTextureWidth() const +{ + return m_Caps.m_MaxTextureWidth; +} + +int CHardwareConfig::MaxTextureHeight() const +{ + return m_Caps.m_MaxTextureHeight; +} + +int CHardwareConfig::TextureMemorySize() const +{ + return m_Caps.m_TextureMemorySize; +} + +bool CHardwareConfig::SupportsOverbright() const +{ + return m_Caps.m_SupportsOverbright; +} + +bool CHardwareConfig::SupportsCubeMaps() const +{ + if ( (ShaderUtil()->GetConfig().dxSupportLevel > 0) && (GetDXSupportLevel() < 70) ) + return false; + + return m_Caps.m_SupportsCubeMaps; +} + +bool CHardwareConfig::SupportsMipmappedCubemaps() const +{ + if ( (ShaderUtil()->GetConfig().dxSupportLevel > 0) && (GetDXSupportLevel() < 70) ) + return false; + + return m_Caps.m_SupportsMipmappedCubemaps; +} + +bool CHardwareConfig::SupportsNonPow2Textures() const +{ + return m_Caps.m_SupportsNonPow2Textures; +} + +int CHardwareConfig::GetTextureStageCount() const +{ + return m_Caps.m_NumTextureStages; +} + +int CHardwareConfig::NumVertexShaderConstants() const +{ + return m_Caps.m_NumVertexShaderConstants; +} + +int CHardwareConfig::NumBooleanVertexShaderConstants() const +{ + return m_Caps.m_NumBooleanVertexShaderConstants; +} + +int CHardwareConfig::NumIntegerVertexShaderConstants() const +{ + return m_Caps.m_NumIntegerVertexShaderConstants; +} + +int CHardwareConfig::NumPixelShaderConstants() const +{ + return m_Caps.m_NumPixelShaderConstants; +} + +int CHardwareConfig::NumBooleanPixelShaderConstants() const +{ + return m_Caps.m_NumBooleanPixelShaderConstants; +} + +int CHardwareConfig::NumIntegerPixelShaderConstants() const +{ + return m_Caps.m_NumIntegerPixelShaderConstants; +} + +int CHardwareConfig::MaxNumLights() const +{ + return m_Caps.m_MaxNumLights; +} + +bool CHardwareConfig::SupportsHardwareLighting() const +{ + if ( (ShaderUtil()->GetConfig().dxSupportLevel > 0) && (GetDXSupportLevel() < 70) ) + return false; + + return m_Caps.m_SupportsHardwareLighting; +} + +int CHardwareConfig::MaxBlendMatrices() const +{ + if ( (ShaderUtil()->GetConfig().dxSupportLevel > 0) && (GetDXSupportLevel() < 70) ) + return 1; + + return m_Caps.m_MaxBlendMatrices; +} + +int CHardwareConfig::MaxBlendMatrixIndices() const +{ + if ( (ShaderUtil()->GetConfig().dxSupportLevel > 0) && (GetDXSupportLevel() < 70) ) + return 1; + + return m_Caps.m_MaxBlendMatrixIndices; +} + +int CHardwareConfig::MaxTextureAspectRatio() const +{ + return m_Caps.m_MaxTextureAspectRatio; +} + +int CHardwareConfig::MaxVertexShaderBlendMatrices() const +{ + if ( (ShaderUtil()->GetConfig().dxSupportLevel > 0) && (GetDXSupportLevel() < 70) ) + return 1; + + return m_Caps.m_MaxVertexShaderBlendMatrices; +} + +// Useful for testing fastclip on Windows +extern ConVar mat_fastclip; + +int CHardwareConfig::MaxUserClipPlanes() const +{ + if ( mat_fastclip.GetBool() ) + return 0; + + return m_Caps.m_MaxUserClipPlanes; +} + +bool CHardwareConfig::UseFastClipping() const +{ + // rbarris broke this up for easier view of outcome in debugger + bool fastclip = mat_fastclip.GetBool(); + + bool result = m_Caps.m_UseFastClipping || fastclip; + + return result; +} + +int CHardwareConfig::MaxTextureDepth() const +{ + return m_Caps.m_MaxTextureDepth; +} + +int CHardwareConfig::GetDXSupportLevel() const +{ + if ( ShaderUtil()->GetConfig().dxSupportLevel != 0 ) + { + return min( ShaderUtil()->GetConfig().dxSupportLevel, m_Caps.m_nDXSupportLevel ); + } + + return m_Caps.m_nDXSupportLevel; +} + +const char *CHardwareConfig::GetShaderDLLName() const +{ + return ( m_Caps.m_pShaderDLL && m_Caps.m_pShaderDLL[0] ) ? m_Caps.m_pShaderDLL : "DEFAULT"; +} + +bool CHardwareConfig::ReadPixelsFromFrontBuffer() const +{ + if ( IsX360() ) + { + // future proof safety, not allowing the front read path + return false; + } + + // GR - in DX 9.0a can blit from MSAA back buffer + return false; +} + +bool CHardwareConfig::PreferDynamicTextures() const +{ + if ( IsX360() ) + { + // future proof safety, not allowing these + return false; + } + + return m_Caps.m_PreferDynamicTextures; +} + +bool CHardwareConfig::SupportsHDR() const +{ + // This is a deprecated function. . use GetHDRType instead. For shipping HL2, this always being false is correct. + Assert( 0 ); + return false; +} + +bool CHardwareConfig::SupportsHDRMode( HDRType_t nHDRType ) const +{ + switch( nHDRType ) + { + case HDR_TYPE_NONE: + return true; + + case HDR_TYPE_INTEGER: + return ( m_Caps.m_MaxHDRType == HDR_TYPE_INTEGER ) || ( m_Caps.m_MaxHDRType == HDR_TYPE_FLOAT ); + + case HDR_TYPE_FLOAT: + return ( m_Caps.m_MaxHDRType == HDR_TYPE_FLOAT ); + + } + return false; + +} + +bool CHardwareConfig::HasProjectedBumpEnv() const +{ + return m_Caps.m_HasProjectedBumpEnv; +} + +bool CHardwareConfig::SupportsSpheremapping() const +{ + return m_Caps.m_bSupportsSpheremapping; +} + +bool CHardwareConfig::NeedsAAClamp() const +{ + return false; +} + +bool CHardwareConfig::NeedsATICentroidHack() const +{ + return m_Caps.m_bNeedsATICentroidHack; +} + +bool CHardwareConfig::SupportsColorOnSecondStream() const +{ + return m_Caps.m_bColorOnSecondStream; +} + +bool CHardwareConfig::SupportsStaticPlusDynamicLighting() const +{ + return GetDXSupportLevel() >= 80; +} + +bool CHardwareConfig::PreferReducedFillrate() const +{ + return ShaderUtil()->GetConfig().ReduceFillrate(); +} + +// This is the max dx support level supported by the card +int CHardwareConfig::GetMaxDXSupportLevel() const +{ + return m_ActualCaps.m_nMaxDXSupportLevel; +} + +bool CHardwareConfig::SpecifiesFogColorInLinearSpace() const +{ + return m_Caps.m_bFogColorSpecifiedInLinearSpace; +} + +bool CHardwareConfig::SupportsSRGB() const +{ + return m_Caps.m_SupportsSRGB; +} + +bool CHardwareConfig::FakeSRGBWrite() const +{ + return m_Caps.m_FakeSRGBWrite; +} + +bool CHardwareConfig::CanDoSRGBReadFromRTs() const +{ + return m_Caps.m_CanDoSRGBReadFromRTs; +} + +bool CHardwareConfig::SupportsGLMixedSizeTargets() const +{ + return m_Caps.m_bSupportsGLMixedSizeTargets; +} + +bool CHardwareConfig::IsAAEnabled() const +{ + return g_pShaderDevice ? g_pShaderDevice->IsAAEnabled() : false; +// bool bAntialiasing = ( m_PresentParameters.MultiSampleType != D3DMULTISAMPLE_NONE ); +// return bAntialiasing; +} + +int CHardwareConfig::GetVertexTextureCount() const +{ + return m_Caps.m_nVertexTextureCount; +} + +int CHardwareConfig::GetMaxVertexTextureDimension() const +{ + return m_Caps.m_nMaxVertexTextureDimension; +} + +HDRType_t CHardwareConfig::GetHDRType() const +{ + bool enabled = m_bHDREnabled; + int dxlev = GetDXSupportLevel(); + int dxsupp = dxlev >= 90; + HDRType_t caps_hdr = m_Caps.m_HDRType; + HDRType_t result = HDR_TYPE_NONE; + + //printf("\nCHardwareConfig::GetHDRType..."); + if (enabled) + { + //printf("-> enabled..."); + if (dxsupp) + { + //printf("-> supported..."); + result = caps_hdr; + } + } + + //printf("-> result is %d.\n", result); + return result; + +/* + if ( m_bHDREnabled && ( GetDXSupportLevel() >= 90 ) ) + return m_Caps.m_HDRType; + return HDR_TYPE_NONE; +*/ +} + +HDRType_t CHardwareConfig::GetHardwareHDRType() const +{ + return m_Caps.m_HDRType; +} + +bool CHardwareConfig::SupportsStreamOffset() const +{ + return ( (GetDXSupportLevel() >= 90) && m_Caps.m_bSupportsStreamOffset ); +} + +int CHardwareConfig::StencilBufferBits() const +{ + return g_pShaderDevice ? g_pShaderDevice->StencilBufferBits() : 0; +} + +int CHardwareConfig:: MaxViewports() const +{ + return m_Caps.m_nMaxViewports; +} + +int CHardwareConfig::GetActualSamplerCount() const +{ + return m_ActualCaps.m_NumSamplers; +} + +int CHardwareConfig::GetActualTextureStageCount() const +{ + return m_ActualCaps.m_NumTextureStages; +} + +const char *CHardwareConfig::GetHWSpecificShaderDLLName() const +{ + return m_Caps.m_pShaderDLL && m_Caps.m_pShaderDLL[0] ? m_Caps.m_pShaderDLL : NULL; +} + +bool CHardwareConfig::SupportsMipmapping() const +{ + return m_Caps.m_SupportsMipmapping; +} + +bool CHardwareConfig::ActuallySupportsPixelShaders_2_b() const +{ + return m_ActualCaps.m_SupportsPixelShaders_2_b; +} diff --git a/materialsystem/shaderapidx9/hardwareconfig.h b/materialsystem/shaderapidx9/hardwareconfig.h new file mode 100644 index 0000000..47e5492 --- /dev/null +++ b/materialsystem/shaderapidx9/hardwareconfig.h @@ -0,0 +1,288 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//===========================================================================// + +#ifndef HARDWARECONFIG_H +#define HARDWARECONFIG_H + +#ifdef _WIN32 +#pragma once +#endif + + +#include "materialsystem/imaterialsystemhardwareconfig.h" +#include "IHardwareConfigInternal.h" +#include "bitmap/imageformat.h" +#include "materialsystem/imaterialsystem.h" + + +//----------------------------------------------------------------------------- +// Forward declarations +//----------------------------------------------------------------------------- +struct ShaderDeviceInfo_t; + + +//----------------------------------------------------------------------------- +// Vendor IDs sometimes needed for vendor-specific code +//----------------------------------------------------------------------------- +#define VENDORID_NVIDIA 0x10DE +#define VENDORID_ATI 0x1002 +#define VENDORID_INTEL 0x8086 + +//----------------------------------------------------------------------------- +// ShaderAPI constants +//----------------------------------------------------------------------------- +enum +{ +#if defined( DX_TO_GL_ABSTRACTION ) + MAXUSERCLIPPLANES = 2, +#else + MAXUSERCLIPPLANES = 6, +#endif + MAX_NUM_LIGHTS = 4, + MAX_OUTPUTS = 3, +}; + + +//----------------------------------------------------------------------------- +// Hardware caps structures +//----------------------------------------------------------------------------- +enum CompressedTextureState_t +{ + COMPRESSED_TEXTURES_ON, + COMPRESSED_TEXTURES_OFF, + COMPRESSED_TEXTURES_NOT_INITIALIZED +}; + +struct HardwareCaps_t : public MaterialAdapterInfo_t +{ + // *****************************NOTE********************************************* + // If you change any members, make sure and reflect the changes in CHardwareConfig::ForceCapsToDXLevel for every dxlevel!!!!! + // If you change any members, make sure and reflect the changes in CHardwareConfig::ForceCapsToDXLevel for every dxlevel!!!!! + // If you change any members, make sure and reflect the changes in CHardwareConfig::ForceCapsToDXLevel for every dxlevel!!!!! + // If you change any members, make sure and reflect the changes in CHardwareConfig::ForceCapsToDXLevel for every dxlevel!!!!! + // If you change any members, make sure and reflect the changes in CHardwareConfig::ForceCapsToDXLevel for every dxlevel!!!!! + // *****************************NOTE********************************************* + // + // NOTE: Texture stages are an obsolete concept; used by fixed-function hardware + // Samplers are dx9+, indicating how many textures we can simultaneously bind + // In Dx8, samplers didn't exist and texture stages were used to indicate the + // number of simultaneously bound textures; we'll emulate that by slamming + // the number of samplers to == the number of texture stages. + CompressedTextureState_t m_SupportsCompressedTextures; + VertexCompressionType_t m_SupportsCompressedVertices; + int m_NumSamplers; + int m_NumTextureStages; + int m_nMaxAnisotropy; + int m_MaxTextureWidth; + int m_MaxTextureHeight; + int m_MaxTextureDepth; + int m_MaxTextureAspectRatio; + int m_MaxPrimitiveCount; + int m_NumPixelShaderConstants; + int m_NumBooleanPixelShaderConstants; + int m_NumIntegerPixelShaderConstants; + int m_NumVertexShaderConstants; + int m_NumBooleanVertexShaderConstants; + int m_NumIntegerVertexShaderConstants; + int m_TextureMemorySize; + int m_MaxNumLights; + int m_MaxBlendMatrices; + int m_MaxBlendMatrixIndices; + int m_MaxVertexShaderBlendMatrices; + int m_MaxUserClipPlanes; + HDRType_t m_HDRType; + char m_pShaderDLL[32]; + ImageFormat m_ShadowDepthTextureFormat; + ImageFormat m_NullTextureFormat; + int m_nVertexTextureCount; + int m_nMaxVertexTextureDimension; + unsigned long m_AlphaToCoverageState; // State to ping to toggle Alpha To Coverage (vendor-dependent) + unsigned long m_AlphaToCoverageEnableValue; // Value to set above state to turn on Alpha To Coverage (vendor-dependent) + unsigned long m_AlphaToCoverageDisableValue; // Value to set above state to turn off Alpha To Coverage (vendor-dependent) + int m_nMaxViewports; + float m_flMinGammaControlPoint; + float m_flMaxGammaControlPoint; + int m_nGammaControlPointCount; + int m_MaxVertexShader30InstructionSlots; + int m_MaxPixelShader30InstructionSlots; + int m_MaxSimultaneousRenderTargets; + + bool m_bDeviceOk : 1; + bool m_HasSetDeviceGammaRamp : 1; + bool m_SupportsVertexShaders : 1; + bool m_SupportsVertexShaders_2_0 : 1; + bool m_SupportsPixelShaders : 1; + bool m_SupportsPixelShaders_1_4 : 1; + bool m_SupportsPixelShaders_2_0 : 1; + bool m_SupportsPixelShaders_2_b : 1; + bool m_SupportsShaderModel_3_0 : 1; + bool m_bSupportsAnisotropicFiltering : 1; + bool m_bSupportsMagAnisotropicFiltering : 1; + bool m_bSupportsVertexTextures : 1; + bool m_ZBiasAndSlopeScaledDepthBiasSupported : 1; + bool m_SupportsMipmapping : 1; + bool m_SupportsOverbright : 1; + bool m_SupportsCubeMaps : 1; + bool m_SupportsHardwareLighting : 1; + bool m_SupportsMipmappedCubemaps : 1; + bool m_SupportsNonPow2Textures : 1; + bool m_PreferDynamicTextures : 1; + bool m_HasProjectedBumpEnv : 1; + bool m_SupportsSRGB : 1; // Means both read and write + bool m_bSupportsSpheremapping : 1; + bool m_UseFastClipping : 1; + bool m_bNeedsATICentroidHack : 1; + bool m_bDisableShaderOptimizations : 1; + bool m_bColorOnSecondStream : 1; + bool m_bSupportsStreamOffset : 1; + bool m_bFogColorSpecifiedInLinearSpace : 1; + bool m_bFogColorAlwaysLinearSpace : 1; + bool m_bSupportsAlphaToCoverage : 1; + bool m_bSupportsShadowDepthTextures : 1; + bool m_bSupportsFetch4 : 1; + bool m_bSoftwareVertexProcessing : 1; + bool m_bScissorSupported : 1; + bool m_bSupportsFloat32RenderTargets : 1; + bool m_bSupportsNormalMapCompression : 1; + bool m_bSupportsBorderColor : 1; + bool m_bDX10Card : 1; // Indicates DX10 part with performant vertex textures running DX9 path + bool m_bDX10Blending : 1; // Indicates DX10 part that does DX10 blending (but may not have performant vertex textures, such as Intel parts) + bool m_bSupportsStaticControlFlow : 1; // Useful on OpenGL, where we have a mix of support... + bool m_FakeSRGBWrite : 1; // Gotta do this on OpenGL. Mostly hidden, but some high level code needs to know + bool m_CanDoSRGBReadFromRTs : 1; // Gotta do this on OpenGL. Mostly hidden, but some high level code needs to know + bool m_bSupportsGLMixedSizeTargets : 1; // on OpenGL, are mixed size depth buffers supported - aka ARB_framebuffer_object + bool m_bCanStretchRectFromTextures : 1; // Does the device expose D3DDEVCAPS2_CAN_STRETCHRECT_FROM_TEXTURES (or is it >DX9?) + + HDRType_t m_MaxHDRType; +}; + + +//----------------------------------------------------------------------------- +// Contains the hardware configuration for the current device +//----------------------------------------------------------------------------- +class CHardwareConfig : public IHardwareConfigInternal +{ +public: + CHardwareConfig(); + + // Sets up the hardware caps given the specified DX level + void SetupHardwareCaps( const ShaderDeviceInfo_t& mode, const HardwareCaps_t &actualCaps ); + + // FIXME: This is for backward compat only.. don't use these + void SetupHardwareCaps( int nDXLevel, const HardwareCaps_t &actualCaps ); + HardwareCaps_t& ActualCapsForEdit() { return m_ActualCaps; } + HardwareCaps_t& CapsForEdit() { return m_Caps; } + + // Members of IMaterialSystemHardwareConfig + virtual bool HasDestAlphaBuffer() const; + virtual bool HasStencilBuffer() const; + virtual int GetFrameBufferColorDepth() const; + virtual int GetSamplerCount() const; + virtual bool HasSetDeviceGammaRamp() const; + virtual bool SupportsCompressedTextures() const; + virtual VertexCompressionType_t SupportsCompressedVertices() const; + virtual bool SupportsNormalMapCompression() const; + virtual bool SupportsBorderColor() const; + virtual bool SupportsFetch4() const; + virtual bool CanStretchRectFromTextures() const; + virtual bool SupportsVertexAndPixelShaders() const; + virtual bool SupportsPixelShaders_1_4() const; + virtual bool SupportsPixelShaders_2_0() const; + virtual bool SupportsStaticControlFlow() const; + virtual bool SupportsVertexShaders_2_0() const; + virtual int MaximumAnisotropicLevel() const; + virtual int MaxTextureWidth() const; + virtual int MaxTextureHeight() const; + virtual int TextureMemorySize() const; + virtual bool SupportsOverbright() const; + virtual bool SupportsCubeMaps() const; + virtual bool SupportsMipmappedCubemaps() const; + virtual bool SupportsNonPow2Textures() const; + virtual int GetTextureStageCount() const; + virtual int NumVertexShaderConstants() const; + virtual int NumBooleanVertexShaderConstants() const; + virtual int NumIntegerVertexShaderConstants() const; + virtual int NumPixelShaderConstants() const; + virtual int NumBooleanPixelShaderConstants() const; + virtual int NumIntegerPixelShaderConstants() const; + virtual int MaxNumLights() const; + virtual bool SupportsHardwareLighting() const; + virtual int MaxBlendMatrices() const; + virtual int MaxBlendMatrixIndices() const; + virtual int MaxTextureAspectRatio() const; + virtual int MaxVertexShaderBlendMatrices() const; + virtual int MaxUserClipPlanes() const; + virtual bool UseFastClipping() const; + virtual int GetDXSupportLevel() const; + virtual const char *GetShaderDLLName() const; + virtual bool ReadPixelsFromFrontBuffer() const; + virtual bool PreferDynamicTextures() const; + virtual bool SupportsHDR() const; + virtual bool HasProjectedBumpEnv() const; + virtual bool SupportsSpheremapping() const; + virtual bool NeedsAAClamp() const; + virtual bool NeedsATICentroidHack() const; + virtual bool SupportsColorOnSecondStream() const; + virtual bool SupportsStaticPlusDynamicLighting() const; + virtual bool PreferReducedFillrate() const; + virtual int GetMaxDXSupportLevel() const; + virtual bool SpecifiesFogColorInLinearSpace() const; + virtual bool SupportsSRGB() const; + virtual bool FakeSRGBWrite() const; + virtual bool CanDoSRGBReadFromRTs() const; + virtual bool SupportsGLMixedSizeTargets() const; + virtual bool IsAAEnabled() const; + virtual int GetVertexTextureCount() const; + virtual int GetMaxVertexTextureDimension() const; + virtual int MaxTextureDepth() const; + virtual HDRType_t GetHDRType() const; + virtual HDRType_t GetHardwareHDRType() const; + virtual bool SupportsPixelShaders_2_b() const; + virtual bool SupportsShaderModel_3_0() const; + virtual bool SupportsStreamOffset() const; + virtual int StencilBufferBits() const; + virtual int MaxViewports() const; + virtual void OverrideStreamOffsetSupport( bool bOverrideEnabled, bool bEnableSupport ); + virtual int GetShadowFilterMode() const; + virtual int NeedsShaderSRGBConversion() const; + virtual bool UsesSRGBCorrectBlending() const; + virtual bool HasFastVertexTextures() const; + virtual int MaxHWMorphBatchCount() const; + + const char *GetHWSpecificShaderDLLName() const; + int GetActualSamplerCount() const; + int GetActualTextureStageCount() const; + bool SupportsMipmapping() const; + virtual bool ActuallySupportsPixelShaders_2_b() const; + + virtual bool SupportsHDRMode( HDRType_t nHDRMode ) const; + + const HardwareCaps_t& ActualCaps() const { return m_ActualCaps; } + const HardwareCaps_t& Caps() const { return m_Caps; } + virtual bool GetHDREnabled( void ) const; + virtual void SetHDREnabled( bool bEnable ); + +protected: + // Gets the recommended configuration associated with a particular dx level + void ForceCapsToDXLevel( HardwareCaps_t *pCaps, int nDxLevel, const HardwareCaps_t &actualCaps ); + + // Members related to capabilities + HardwareCaps_t m_ActualCaps; + HardwareCaps_t m_Caps; + HardwareCaps_t m_UnOverriddenCaps; + bool m_bHDREnabled; +}; + + +//----------------------------------------------------------------------------- +// Singleton hardware config +//----------------------------------------------------------------------------- +extern CHardwareConfig *g_pHardwareConfig; + + +#endif // HARDWARECONFIG_H diff --git a/materialsystem/shaderapidx9/imeshdx8.h b/materialsystem/shaderapidx9/imeshdx8.h new file mode 100644 index 0000000..96697a7 --- /dev/null +++ b/materialsystem/shaderapidx9/imeshdx8.h @@ -0,0 +1,98 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//===========================================================================// + +#ifndef IMESHDX8_H +#define IMESHDX8_H + +#ifdef _WIN32 +#pragma once +#endif + + +#include "meshbase.h" +#include "shaderapi/ishaderapi.h" + + +abstract_class IMeshMgr +{ +public: + // Initialize, shutdown + virtual void Init() = 0; + virtual void Shutdown() = 0; + + // Task switch... + virtual void ReleaseBuffers() = 0; + virtual void RestoreBuffers() = 0; + + // Releases all dynamic vertex buffers + virtual void DestroyVertexBuffers() = 0; + + // Flushes the dynamic mesh. Should be called when state changes + virtual void Flush() = 0; + + // Discards the dynamic vertex and index buffer + virtual void DiscardVertexBuffers() = 0; + + // Creates, destroys static meshes + virtual IMesh* CreateStaticMesh( VertexFormat_t vertexFormat, const char *pTextureBudgetGroup, IMaterial *pMaterial = NULL ) = 0; + virtual void DestroyStaticMesh( IMesh* pMesh ) = 0; + + // Gets at the dynamic mesh + virtual IMesh* GetDynamicMesh( IMaterial* pMaterial, VertexFormat_t vertexFormat, int nHWSkinBoneCount, bool buffered = true, + IMesh* pVertexOverride = 0, IMesh* pIndexOverride = 0) = 0; + +// ------------ New Vertex/Index Buffer interface ---------------------------- + // Do we need support for bForceTempMesh and bSoftwareVertexShader? + // I don't think we use bSoftwareVertexShader anymore. .need to look into bForceTempMesh. + virtual IVertexBuffer *CreateVertexBuffer( ShaderBufferType_t type, VertexFormat_t fmt, int nVertexCount, const char *pBudgetGroup ) = 0; + virtual IIndexBuffer *CreateIndexBuffer( ShaderBufferType_t indexBufferType, MaterialIndexFormat_t fmt, int nIndexCount, const char *pBudgetGroup ) = 0; + virtual void DestroyVertexBuffer( IVertexBuffer * ) = 0; + virtual void DestroyIndexBuffer( IIndexBuffer * ) = 0; + // Do we need to specify the stream here in the case of locking multiple dynamic VBs on different streams? + virtual IVertexBuffer *GetDynamicVertexBuffer( int streamID, VertexFormat_t vertexFormat, bool bBuffered = true ) = 0; + virtual IIndexBuffer *GetDynamicIndexBuffer( MaterialIndexFormat_t fmt, bool bBuffered = true ) = 0; + virtual void BindVertexBuffer( int streamID, IVertexBuffer *pVertexBuffer, int nOffsetInBytes, int nFirstVertex, int nVertexCount, VertexFormat_t fmt, int nRepetitions = 1 ) = 0; + virtual void BindIndexBuffer( IIndexBuffer *pIndexBuffer, int nOffsetInBytes ) = 0; + virtual void Draw( MaterialPrimitiveType_t primitiveType, int nFirstIndex, int nIndexCount ) = 0; +// ------------ End ---------------------------- + virtual VertexFormat_t GetCurrentVertexFormat( void ) const = 0; + virtual void RenderPassWithVertexAndIndexBuffers( void ) = 0; + + // Computes the vertex format + virtual VertexFormat_t ComputeVertexFormat( unsigned int flags, + int numTexCoords, int* pTexCoordDimensions, int numBoneWeights, + int userDataSize ) const = 0; + + // Returns the number of buffers... + virtual int BufferCount() const = 0; + + // Use fat vertices (for tools) + virtual void UseFatVertices( bool bUseFat ) = 0; + + // Returns the number of vertices + indices we can render using the dynamic mesh + // Passing true in the second parameter will return the max # of vertices + indices + // we can use before a flush is provoked and may return different values + // if called multiple times in succession. + // Passing false into the second parameter will return + // the maximum possible vertices + indices that can be rendered in a single batch + virtual void GetMaxToRender( IMesh *pMesh, bool bMaxUntilFlush, int *pMaxVerts, int *pMaxIndices ) = 0; + + // Returns the max number of vertices we can render for a given material + virtual int GetMaxVerticesToRender( IMaterial *pMaterial ) = 0; + virtual int GetMaxIndicesToRender( ) = 0; + virtual IMesh *GetFlexMesh() = 0; + + virtual void ComputeVertexDescription( unsigned char* pBuffer, VertexFormat_t vertexFormat, MeshDesc_t& desc ) const = 0; + + virtual IVertexBuffer *GetDynamicVertexBuffer( IMaterial *pMaterial, bool buffered = true ) = 0; + virtual IIndexBuffer *GetDynamicIndexBuffer( IMaterial *pMaterial, bool buffered = true ) = 0; + + virtual void MarkUnusedVertexFields( unsigned int nFlags, int nTexCoordCount, bool *pUnusedTexCoords ) = 0; +}; + +#endif // IMESHDX8_H diff --git a/materialsystem/shaderapidx9/inputlayoutdx10.cpp b/materialsystem/shaderapidx9/inputlayoutdx10.cpp new file mode 100644 index 0000000..9482907 --- /dev/null +++ b/materialsystem/shaderapidx9/inputlayoutdx10.cpp @@ -0,0 +1,214 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//===========================================================================// + +#include +#undef GetCommandLine + +#include "inputlayoutdx10.h" +#include "materialsystem/imesh.h" +#include "shaderdevicedx10.h" + +// NOTE: This has to be the last file included! +#include "tier0/memdbgon.h" + + +//----------------------------------------------------------------------------- +// Standard input layouts +//----------------------------------------------------------------------------- +static const DXGI_FORMAT s_pSizeLookup[] = +{ + DXGI_FORMAT_UNKNOWN, // Should be unused... + DXGI_FORMAT_R32_FLOAT, // D3DDECLTYPE_FLOAT1 + DXGI_FORMAT_R32G32_FLOAT, // D3DDECLTYPE_FLOAT2, + DXGI_FORMAT_R32G32B32_FLOAT, // D3DDECLTYPE_FLOAT3, + DXGI_FORMAT_R32G32B32A32_FLOAT, // D3DDECLTYPE_FLOAT4 +}; + +struct FieldInfo_t +{ + const char *m_pSemanticString; + unsigned int m_nSemanticIndex; + uint64 m_nFormatMask; + int m_nFieldSize; +}; + +static FieldInfo_t s_pFieldInfo[] = +{ + { "POSITION", 0, VERTEX_POSITION, sizeof( float ) * 3 }, + { "BLENDWEIGHT", 0, VERTEX_BONE_WEIGHT_MASK, 0 }, + { "BLENDINDICES", 0, VERTEX_BONE_INDEX, 4 }, + { "NORMAL", 0, VERTEX_NORMAL, sizeof( float ) * 3 }, + { "COLOR", 0, VERTEX_COLOR, 4 }, + { "SPECULAR", 0, VERTEX_SPECULAR, 4 }, + { "TEXCOORD", 0, VERTEX_TEXCOORD_MASK(0), 0 }, + { "TEXCOORD", 1, VERTEX_TEXCOORD_MASK(1), 0 }, + { "TEXCOORD", 2, VERTEX_TEXCOORD_MASK(2), 0 }, + { "TEXCOORD", 3, VERTEX_TEXCOORD_MASK(3), 0 }, + { "TEXCOORD", 4, VERTEX_TEXCOORD_MASK(4), 0 }, + { "TEXCOORD", 5, VERTEX_TEXCOORD_MASK(5), 0 }, + { "TEXCOORD", 6, VERTEX_TEXCOORD_MASK(6), 0 }, + { "TEXCOORD", 7, VERTEX_TEXCOORD_MASK(7), 0 }, + { "TANGENT", 0, VERTEX_TANGENT_S, sizeof( float ) * 3 }, + { "BINORMAL", 0, VERTEX_TANGENT_T, sizeof( float ) * 3 }, + { "USERDATA", 0, USER_DATA_SIZE_MASK, 0 }, + { NULL, 0, 0 }, +}; + +static D3D10_INPUT_ELEMENT_DESC s_pVertexDesc[] = +{ + { "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D10_INPUT_PER_VERTEX_DATA, 0 }, + { "BLENDWEIGHT", 0, DXGI_FORMAT_UNKNOWN, 0, 0, D3D10_INPUT_PER_VERTEX_DATA, 0 }, + { "BLENDINDICES", 0, DXGI_FORMAT_R8G8B8A8_UNORM, 0, 0, D3D10_INPUT_PER_VERTEX_DATA, 0 }, + { "NORMAL", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D10_INPUT_PER_VERTEX_DATA, 0 }, + { "COLOR", 0, DXGI_FORMAT_R8G8B8A8_UNORM, 0, 0, D3D10_INPUT_PER_VERTEX_DATA, 0 }, + { "SPECULAR", 0, DXGI_FORMAT_R8G8B8A8_UNORM, 0, 0, D3D10_INPUT_PER_VERTEX_DATA, 0 }, + { "TEXCOORD", 0, DXGI_FORMAT_UNKNOWN, 0, 0, D3D10_INPUT_PER_VERTEX_DATA, 0 }, + { "TEXCOORD", 1, DXGI_FORMAT_UNKNOWN, 0, 0, D3D10_INPUT_PER_VERTEX_DATA, 0 }, + { "TEXCOORD", 2, DXGI_FORMAT_UNKNOWN, 0, 0, D3D10_INPUT_PER_VERTEX_DATA, 0 }, + { "TEXCOORD", 3, DXGI_FORMAT_UNKNOWN, 0, 0, D3D10_INPUT_PER_VERTEX_DATA, 0 }, + { "TEXCOORD", 4, DXGI_FORMAT_UNKNOWN, 0, 0, D3D10_INPUT_PER_VERTEX_DATA, 0 }, + { "TEXCOORD", 5, DXGI_FORMAT_UNKNOWN, 0, 0, D3D10_INPUT_PER_VERTEX_DATA, 0 }, + { "TEXCOORD", 6, DXGI_FORMAT_UNKNOWN, 0, 0, D3D10_INPUT_PER_VERTEX_DATA, 0 }, + { "TEXCOORD", 7, DXGI_FORMAT_UNKNOWN, 0, 0, D3D10_INPUT_PER_VERTEX_DATA, 0 }, + { "TANGENT", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D10_INPUT_PER_VERTEX_DATA, 0 }, + { "BINORMAL", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D10_INPUT_PER_VERTEX_DATA, 0 }, + { "USERDATA", 0, DXGI_FORMAT_UNKNOWN, 0, 0, D3D10_INPUT_PER_VERTEX_DATA, 0 }, +}; + +static D3D10_INPUT_ELEMENT_DESC s_pFallbackVertexDesc[] = +{ + { "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 15, 0, D3D10_INPUT_PER_INSTANCE_DATA, UINT_MAX }, + { "BLENDWEIGHT", 0, DXGI_FORMAT_R32G32_FLOAT, 15, 12, D3D10_INPUT_PER_INSTANCE_DATA, UINT_MAX }, + { "BLENDINDICES", 0, DXGI_FORMAT_R8G8B8A8_UNORM, 15, 20, D3D10_INPUT_PER_INSTANCE_DATA, UINT_MAX }, + { "NORMAL", 0, DXGI_FORMAT_R32G32B32_FLOAT, 15, 24, D3D10_INPUT_PER_INSTANCE_DATA, UINT_MAX }, + { "COLOR", 0, DXGI_FORMAT_R8G8B8A8_UNORM, 15, 36, D3D10_INPUT_PER_INSTANCE_DATA, UINT_MAX }, + { "SPECULAR", 0, DXGI_FORMAT_R8G8B8A8_UNORM, 15, 40, D3D10_INPUT_PER_INSTANCE_DATA, UINT_MAX }, + { "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 15, 44, D3D10_INPUT_PER_INSTANCE_DATA, UINT_MAX }, + { "TEXCOORD", 1, DXGI_FORMAT_R32G32_FLOAT, 15, 52, D3D10_INPUT_PER_INSTANCE_DATA, UINT_MAX }, + { "TEXCOORD", 2, DXGI_FORMAT_R32G32_FLOAT, 15, 60, D3D10_INPUT_PER_INSTANCE_DATA, UINT_MAX }, + { "TEXCOORD", 3, DXGI_FORMAT_R32G32_FLOAT, 15, 68, D3D10_INPUT_PER_INSTANCE_DATA, UINT_MAX }, + { "TEXCOORD", 4, DXGI_FORMAT_R32G32_FLOAT, 15, 76, D3D10_INPUT_PER_INSTANCE_DATA, UINT_MAX }, + { "TEXCOORD", 5, DXGI_FORMAT_R32G32_FLOAT, 15, 84, D3D10_INPUT_PER_INSTANCE_DATA, UINT_MAX }, + { "TEXCOORD", 6, DXGI_FORMAT_R32G32_FLOAT, 15, 92, D3D10_INPUT_PER_INSTANCE_DATA, UINT_MAX }, + { "TEXCOORD", 7, DXGI_FORMAT_R32G32_FLOAT, 15, 100, D3D10_INPUT_PER_INSTANCE_DATA, UINT_MAX }, + { "TANGENT", 0, DXGI_FORMAT_R32G32B32_FLOAT, 15, 108, D3D10_INPUT_PER_INSTANCE_DATA, UINT_MAX }, + { "BINORMAL", 0, DXGI_FORMAT_R32G32B32_FLOAT, 15, 120, D3D10_INPUT_PER_INSTANCE_DATA, UINT_MAX }, + { "USERDATA", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 15, 132, D3D10_INPUT_PER_INSTANCE_DATA, UINT_MAX }, +}; + + +//----------------------------------------------------------------------------- +// Computes the required input desc based on the vertex format +//----------------------------------------------------------------------------- +static void PrintInputDesc( int nCount, const D3D10_INPUT_ELEMENT_DESC *pDecl ) +{ + for ( int i = 0; i < nCount; i++ ) + { + Msg( "%s (%d): Stream: %d, Offset: %d, Instanced? %c\n", + pDecl[i].SemanticName, + pDecl[i].SemanticIndex, + ( int )pDecl[i].InputSlot, + ( int )pDecl[i].AlignedByteOffset, + pDecl[i].InputSlotClass == D3D10_INPUT_PER_VERTEX_DATA ? 'n' : 'y' + ); + } +} + + +//----------------------------------------------------------------------------- +// Checks to see if a shader requires a particular field +//----------------------------------------------------------------------------- +static bool CheckShaderSignatureExpectations( ID3D10ShaderReflection* pReflection, const char* pSemantic, unsigned int nSemanticIndex ) +{ + D3D10_SHADER_DESC shaderDesc; + D3D10_SIGNATURE_PARAMETER_DESC paramDesc; + + Assert( pSemantic ); + Assert( pReflection ); + + pReflection->GetDesc( &shaderDesc ); + + for ( unsigned int k=0; k < shaderDesc.InputParameters; k++ ) + { + pReflection->GetInputParameterDesc( k, ¶mDesc ); + if ( ( nSemanticIndex == paramDesc.SemanticIndex ) && !Q_stricmp( pSemantic, paramDesc.SemanticName ) ) + return true; + } + + return false; +} + + +//----------------------------------------------------------------------------- +// Computes the required input desc based on the vertex format +//----------------------------------------------------------------------------- +static unsigned int ComputeInputDesc( VertexFormat_t fmt, D3D10_INPUT_ELEMENT_DESC *pDecl, ID3D10ShaderReflection* pReflection ) +{ + unsigned int nCount = 0; + int nOffset = 0; + + // Fix up the global table so we don't need special-case code + int nBoneCount = NumBoneWeights( fmt ); + s_pFieldInfo[1].m_nFieldSize = sizeof( float ) * nBoneCount; + s_pVertexDesc[1].Format = s_pSizeLookup[ nBoneCount ]; + + int nUserDataSize = UserDataSize( fmt ); + s_pFieldInfo[16].m_nFieldSize = sizeof( float ) * nUserDataSize; + s_pVertexDesc[16].Format = s_pSizeLookup[ nUserDataSize ]; + + // NOTE: Fix s_pFieldInfo, s_pVertexDesc, s_pFallbackVertexDesc if you add more fields + // As well as the fallback stream (stream #15) + COMPILE_TIME_ASSERT( VERTEX_MAX_TEXTURE_COORDINATES == 8 ); + for ( int i = 0; i < VERTEX_MAX_TEXTURE_COORDINATES; ++i ) + { + int nTexCoordCount = TexCoordSize( i, fmt ); + s_pFieldInfo[6+i].m_nFieldSize = sizeof( float ) * nTexCoordCount; + s_pVertexDesc[6+i].Format = s_pSizeLookup[ nTexCoordCount ]; + } + + // FIXME: Change this loop so CheckShaderSignatureExpectations is called once! + for ( int i = 0; s_pFieldInfo[i].m_pSemanticString; ++i ) + { + if ( fmt & s_pFieldInfo[i].m_nFormatMask ) + { + memcpy( &pDecl[nCount], &s_pVertexDesc[i], sizeof(D3D10_INPUT_ELEMENT_DESC) ); + pDecl[nCount].AlignedByteOffset = nOffset; + nOffset += s_pFieldInfo[i].m_nFieldSize; + ++nCount; + } + else if ( CheckShaderSignatureExpectations( pReflection, s_pFieldInfo[i].m_pSemanticString, s_pFieldInfo[i].m_nSemanticIndex ) ) + { + memcpy( &pDecl[nCount], &s_pFallbackVertexDesc[i], sizeof(D3D10_INPUT_ELEMENT_DESC) ); + ++nCount; + } + } + + // For debugging only... +// PrintInputDesc( nCount, pDecl ); + + return nCount; +} + + +//----------------------------------------------------------------------------- +// Gets the input layout associated with a vertex format +//----------------------------------------------------------------------------- +ID3D10InputLayout *CreateInputLayout( VertexFormat_t fmt, ID3D10ShaderReflection* pReflection, const void *pByteCode, size_t nByteCodeLen ) +{ + D3D10_INPUT_ELEMENT_DESC pDecl[32]; + unsigned int nDeclCount = ComputeInputDesc( fmt, pDecl, pReflection ); + + ID3D10InputLayout *pInputLayout; + HRESULT hr = D3D10Device()->CreateInputLayout( pDecl, nDeclCount, pByteCode, nByteCodeLen, &pInputLayout ); + if ( FAILED( hr ) ) + { + Warning( "CreateInputLayout::Unable to create input layout for format %llX!\n", fmt ); + return NULL; + } + return pInputLayout; +} diff --git a/materialsystem/shaderapidx9/inputlayoutdx10.h b/materialsystem/shaderapidx9/inputlayoutdx10.h new file mode 100644 index 0000000..b1ac116 --- /dev/null +++ b/materialsystem/shaderapidx9/inputlayoutdx10.h @@ -0,0 +1,35 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//===========================================================================// + +#ifndef INPUTLAYOUTDX10_H +#define INPUTLAYOUTDX10_H + +#ifdef _WIN32 +#pragma once +#endif + +#include "materialsystem/imaterial.h" + + +//----------------------------------------------------------------------------- +// Forward declaration +//----------------------------------------------------------------------------- +struct ID3D10InputLayout; +struct ID3D10ShaderReflection; + + +//----------------------------------------------------------------------------- +// Gets the input layout associated with a vertex format +// FIXME: Note that we'll need to change this from a VertexFormat_t +//----------------------------------------------------------------------------- +ID3D10InputLayout *CreateInputLayout( VertexFormat_t fmt, + ID3D10ShaderReflection* pReflection, const void *pByteCode, size_t nByteCodeLen ); + + +#endif // INPUTLAYOUTDX10_H + diff --git a/materialsystem/shaderapidx9/ivertexbufferdx8.h b/materialsystem/shaderapidx9/ivertexbufferdx8.h new file mode 100644 index 0000000..dea00d7 --- /dev/null +++ b/materialsystem/shaderapidx9/ivertexbufferdx8.h @@ -0,0 +1,34 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// + +#ifndef IVERTEXBUFFERDX8_H +#define IVERTEXBUFFERDX8_H +#pragma once + +#include "IVertexBuffer.h" + +abstract_class IVertexBufferDX8 : public IVertexBuffer +{ +public: + // TEMPORARY! + virtual int Begin( int flags, int numVerts ) = 0; + + // Sets up the renderstate + virtual void SetRenderState( int stream ) = 0; + + // Gets FVF info + virtual void ComputeFVFInfo( int flags, int& fvf, int& size ) const = 0; + + // Cleans up the vertex buffers + virtual void CleanUp() = 0; + + // Flushes the vertex buffers + virtual void Flush() = 0; +}; + +#endif // IVERTEXBUFFERDX8_H diff --git a/materialsystem/shaderapidx9/meshbase.cpp b/materialsystem/shaderapidx9/meshbase.cpp new file mode 100644 index 0000000..336f150 --- /dev/null +++ b/materialsystem/shaderapidx9/meshbase.cpp @@ -0,0 +1,419 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//===========================================================================// + +#include "meshbase.h" +#include "shaderapi_global.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + + +//----------------------------------------------------------------------------- +// Helpers with VertexDesc_t... +//----------------------------------------------------------------------------- +// FIXME: add compression-agnostic read-accessors (which decompress and return by value, checking desc.m_CompressionType) +inline Vector &Position( VertexDesc_t const &desc, int vert ) +{ + return *(Vector*)((unsigned char*)desc.m_pPosition + vert * desc.m_VertexSize_Position ); +} + +inline float Wrinkle( VertexDesc_t const &desc, int vert ) +{ + return *(float*)((unsigned char*)desc.m_pWrinkle + vert * desc.m_VertexSize_Wrinkle ); +} + +inline float *BoneWeight( VertexDesc_t const &desc, int vert ) +{ + Assert( desc.m_CompressionType == VERTEX_COMPRESSION_NONE ); + return (float*)((unsigned char*)desc.m_pBoneWeight + vert * desc.m_VertexSize_BoneWeight ); +} + +inline unsigned char *BoneIndex( VertexDesc_t const &desc, int vert ) +{ + return desc.m_pBoneMatrixIndex + vert * desc.m_VertexSize_BoneMatrixIndex; +} + +inline Vector &Normal( VertexDesc_t const &desc, int vert ) +{ + Assert( desc.m_CompressionType == VERTEX_COMPRESSION_NONE ); + return *(Vector*)((unsigned char*)desc.m_pNormal + vert * desc.m_VertexSize_Normal ); +} + +inline unsigned char *Color( VertexDesc_t const &desc, int vert ) +{ + return desc.m_pColor + vert * desc.m_VertexSize_Color; +} + +inline Vector2D &TexCoord( VertexDesc_t const &desc, int vert, int stage ) +{ + return *(Vector2D*)((unsigned char*)desc.m_pTexCoord[stage] + vert * desc.m_VertexSize_TexCoord[stage] ); +} + +inline Vector &TangentS( VertexDesc_t const &desc, int vert ) +{ + return *(Vector*)((unsigned char*)desc.m_pTangentS + vert * desc.m_VertexSize_TangentS ); +} + +inline Vector &TangentT( VertexDesc_t const &desc, int vert ) +{ + return *(Vector*)((unsigned char*)desc.m_pTangentT + vert * desc.m_VertexSize_TangentT ); +} + + +//----------------------------------------------------------------------------- +// +// Vertex Buffer implementations begin here +// +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +// constructor, destructor +//----------------------------------------------------------------------------- +CVertexBufferBase::CVertexBufferBase( const char *pBudgetGroupName ) +{ + m_pBudgetGroupName = pBudgetGroupName; +} + +CVertexBufferBase::~CVertexBufferBase() +{ +} + +//----------------------------------------------------------------------------- +// Displays the vertex format +//----------------------------------------------------------------------------- +void CVertexBufferBase::PrintVertexFormat( VertexFormat_t vertexFormat ) +{ + VertexCompressionType_t compression = CompressionType( vertexFormat ); + if( vertexFormat & VERTEX_POSITION ) + { + Msg( "VERTEX_POSITION|" ); + } + if( vertexFormat & VERTEX_NORMAL ) + { + // FIXME: genericise this stuff using VertexElement_t data tables (so funcs like 'just work' if we make compression changes) + if ( compression == VERTEX_COMPRESSION_ON ) + Msg( "VERTEX_NORMAL|" ); + else + Msg( "VERTEX_NORMAL[COMPRESSED]|" ); + } + if( vertexFormat & VERTEX_COLOR ) + { + Msg( "VERTEX_COLOR|" ); + } + if( vertexFormat & VERTEX_SPECULAR ) + { + Msg( "VERTEX_SPECULAR|" ); + } + if( vertexFormat & VERTEX_TANGENT_S ) + { + Msg( "VERTEX_TANGENT_S|" ); + } + if( vertexFormat & VERTEX_TANGENT_T ) + { + Msg( "VERTEX_TANGENT_T|" ); + } + if( vertexFormat & VERTEX_BONE_INDEX ) + { + Msg( "VERTEX_BONE_INDEX|" ); + } + if( vertexFormat & VERTEX_FORMAT_VERTEX_SHADER ) + { + Msg( "VERTEX_FORMAT_VERTEX_SHADER|" ); + } + if( NumBoneWeights( vertexFormat ) > 0 ) + { + Msg( "VERTEX_BONEWEIGHT(%d)%s|", + NumBoneWeights( vertexFormat ), ( compression ? "[COMPRESSED]" : "" ) ); + } + if( UserDataSize( vertexFormat ) > 0 ) + { + Msg( "VERTEX_USERDATA_SIZE(%d)|", UserDataSize( vertexFormat ) ); + } + int i; + for( i = 0; i < VERTEX_MAX_TEXTURE_COORDINATES; i++ ) + { + int nDim = TexCoordSize( i, vertexFormat ); + if ( nDim == 0 ) + continue; + + Msg( "VERTEX_TEXCOORD_SIZE(%d,%d)", i, nDim ); + } + Msg( "\n" ); +} + + +//----------------------------------------------------------------------------- +// Used to construct vertex data +//----------------------------------------------------------------------------- +void CVertexBufferBase::ComputeVertexDescription( unsigned char *pBuffer, + VertexFormat_t vertexFormat, VertexDesc_t &desc ) +{ + ComputeVertexDesc( pBuffer, vertexFormat, desc ); +} + + +//----------------------------------------------------------------------------- +// Returns the vertex format size +//----------------------------------------------------------------------------- +int CVertexBufferBase::VertexFormatSize( VertexFormat_t vertexFormat ) +{ + // FIXME: We could make this much faster + MeshDesc_t temp; + ComputeVertexDescription( 0, vertexFormat, temp ); + return temp.m_ActualVertexSize; +} + + +//----------------------------------------------------------------------------- +// Spews the mesh data +//----------------------------------------------------------------------------- +void CVertexBufferBase::Spew( int nVertexCount, const VertexDesc_t &desc ) +{ + LOCK_SHADERAPI(); + + char pTempBuf[1024]; + Q_snprintf( pTempBuf, sizeof(pTempBuf), "\nVerts %d (First %d, Offset %d) :\n", nVertexCount, desc.m_nFirstVertex, desc.m_nOffset ); + Warning( "%s", pTempBuf ); + + Assert( ( desc.m_NumBoneWeights == 2 ) || ( desc.m_NumBoneWeights == 0 ) ); + + int nLen = 0; + int nBoneWeightCount = desc.m_NumBoneWeights; + for ( int i = 0; i < nVertexCount; ++i ) + { + nLen += Q_snprintf( &pTempBuf[nLen], sizeof(pTempBuf) - nLen, "[%4d] ", i + desc.m_nFirstVertex ); + if ( desc.m_VertexSize_Position ) + { + Vector &pos = Position( desc, i ); + nLen += Q_snprintf( &pTempBuf[nLen], sizeof(pTempBuf) - nLen, "P %8.2f %8.2f %8.2f ", + pos[0], pos[1], pos[2]); + } + + if ( desc.m_VertexSize_Wrinkle ) + { + float flWrinkle = Wrinkle( desc, i ); + nLen += Q_snprintf( &pTempBuf[nLen], sizeof(pTempBuf) - nLen, "Wr %8.2f ",flWrinkle ); + } + + if ( nBoneWeightCount ) + { + nLen += Q_snprintf( &pTempBuf[nLen], sizeof(pTempBuf) - nLen, "BW "); + float* pWeight = BoneWeight( desc, i ); + for ( int j = 0; j < nBoneWeightCount; ++j ) + { + nLen += Q_snprintf( &pTempBuf[nLen], sizeof(pTempBuf) - nLen, "%1.2f ", pWeight[j] ); + } + } + if ( desc.m_VertexSize_BoneMatrixIndex ) + { + unsigned char *pIndex = BoneIndex( desc, i ); + nLen += Q_snprintf( &pTempBuf[nLen], sizeof(pTempBuf) - nLen, "BI %d %d %d %d ", ( int )pIndex[0], ( int )pIndex[1], ( int )pIndex[2], ( int )pIndex[3] ); + Assert( pIndex[0] >= 0 && pIndex[0] < 16 ); + Assert( pIndex[1] >= 0 && pIndex[1] < 16 ); + Assert( pIndex[2] >= 0 && pIndex[2] < 16 ); + Assert( pIndex[3] >= 0 && pIndex[3] < 16 ); + } + + if ( desc.m_VertexSize_Normal ) + { + Vector & normal = Normal( desc, i ); + nLen += Q_snprintf( &pTempBuf[nLen], sizeof(pTempBuf) - nLen, "N %1.2f %1.2f %1.2f ", + normal[0], normal[1], normal[2]); + } + + if ( desc.m_VertexSize_Color ) + { + unsigned char* pColor = Color( desc, i ); + nLen += Q_snprintf( &pTempBuf[nLen], sizeof(pTempBuf) - nLen, "C b %3d g %3d r %3d a %3d ", + pColor[0], pColor[1], pColor[2], pColor[3]); + } + + for ( int j = 0; j < VERTEX_MAX_TEXTURE_COORDINATES; ++j ) + { + if ( desc.m_VertexSize_TexCoord[j] ) + { + Vector2D& texcoord = TexCoord( desc, i, j ); + nLen += Q_snprintf( &pTempBuf[nLen], sizeof(pTempBuf) - nLen, "T%d %.2f %.2f ", j,texcoord[0], texcoord[1]); + } + } + + if ( desc.m_VertexSize_TangentS ) + { + Vector& tangentS = TangentS( desc, i ); + nLen += Q_snprintf( &pTempBuf[nLen], sizeof(pTempBuf) - nLen, "S %1.2f %1.2f %1.2f ", + tangentS[0], tangentS[1], tangentS[2]); + } + + if ( desc.m_VertexSize_TangentT ) + { + Vector& tangentT = TangentT( desc, i ); + nLen += Q_snprintf( &pTempBuf[nLen], sizeof(pTempBuf) - nLen, "T %1.2f %1.2f %1.2f ", + tangentT[0], tangentT[1], tangentT[2]); + } + + Q_snprintf( &pTempBuf[nLen], sizeof(pTempBuf) - nLen, "\n" ); + Warning( "%s", pTempBuf ); + nLen = 0; + } +} + + +//----------------------------------------------------------------------------- +// Validates vertex buffer data +//----------------------------------------------------------------------------- +void CVertexBufferBase::ValidateData( int nVertexCount, const VertexDesc_t &spewDesc ) +{ + LOCK_SHADERAPI(); +#ifdef VALIDATE_DEBUG + int i; + + // This is needed so buffering can just use this + VertexFormat_t fmt = m_pMaterial->GetVertexUsage(); + + // Set up the vertex descriptor + VertexDesc_t desc = spewDesc; + + int numBoneWeights = NumBoneWeights( fmt ); + for ( i = 0; i < nVertexCount; ++i ) + { + if( fmt & VERTEX_POSITION ) + { + D3DXVECTOR3& pos = Position( desc, i ); + Assert( IsFinite( pos[0] ) && IsFinite( pos[1] ) && IsFinite( pos[2] ) ); + } + if( fmt & VERTEX_WRINKLE ) + { + float flWrinkle = Wrinkle( desc, i ); + Assert( IsFinite( flWrinkle ) ); + } + if (numBoneWeights > 0) + { + float* pWeight = BoneWeight( desc, i ); + for (int j = 0; j < numBoneWeights; ++j) + { + Assert( pWeight[j] >= 0.0f && pWeight[j] <= 1.0f ); + } + } + if( fmt & VERTEX_BONE_INDEX ) + { + unsigned char *pIndex = BoneIndex( desc, i ); + Assert( pIndex[0] >= 0 && pIndex[0] < 16 ); + Assert( pIndex[1] >= 0 && pIndex[1] < 16 ); + Assert( pIndex[2] >= 0 && pIndex[2] < 16 ); + Assert( pIndex[3] >= 0 && pIndex[3] < 16 ); + } + if( fmt & VERTEX_NORMAL ) + { + D3DXVECTOR3& normal = Normal( desc, i ); + Assert( normal[0] >= -1.05f && normal[0] <= 1.05f ); + Assert( normal[1] >= -1.05f && normal[1] <= 1.05f ); + Assert( normal[2] >= -1.05f && normal[2] <= 1.05f ); + } + + if (fmt & VERTEX_COLOR) + { + int* pColor = (int*)Color( desc, i ); + Assert( *pColor != FLOAT32_NAN_BITS ); + } + + for (int j = 0; j < VERTEX_MAX_TEXTURE_COORDINATES; ++j) + { + if( TexCoordSize( j, fmt ) > 0) + { + D3DXVECTOR2& texcoord = TexCoord( desc, i, j ); + Assert( IsFinite( texcoord[0] ) && IsFinite( texcoord[1] ) ); + } + } + + if (fmt & VERTEX_TANGENT_S) + { + D3DXVECTOR3& tangentS = TangentS( desc, i ); + Assert( IsFinite( tangentS[0] ) && IsFinite( tangentS[1] ) && IsFinite( tangentS[2] ) ); + } + + if (fmt & VERTEX_TANGENT_T) + { + D3DXVECTOR3& tangentT = TangentT( desc, i ); + Assert( IsFinite( tangentT[0] ) && IsFinite( tangentT[1] ) && IsFinite( tangentT[2] ) ); + } + } +#endif // _DEBUG +} + + +//----------------------------------------------------------------------------- +// +// Index Buffer implementations begin here +// +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +// constructor, destructor +//----------------------------------------------------------------------------- +CIndexBufferBase::CIndexBufferBase( const char *pBudgetGroupName ) : m_pBudgetGroupName( pBudgetGroupName ) +{ +} + + +//----------------------------------------------------------------------------- +// Spews the mesh data +//----------------------------------------------------------------------------- +void CIndexBufferBase::Spew( int nIndexCount, const IndexDesc_t &indexDesc ) +{ + LOCK_SHADERAPI(); + + char pTempBuf[512]; + int nLen = 0; + pTempBuf[0] = '\0'; + char *pTemp = pTempBuf; + Q_snprintf( pTempBuf, sizeof(pTempBuf), "\nIndices: %d (First %d, Offset %d)\n", nIndexCount, indexDesc.m_nFirstIndex, indexDesc.m_nOffset ); + Warning( "%s", pTempBuf ); + for ( int i = 0; i < nIndexCount; ++i ) + { + nLen += Q_snprintf( pTemp, sizeof(pTempBuf) - nLen - 1, "%d ", ( int )indexDesc.m_pIndices[i] ); + pTemp = pTempBuf + nLen; + if ( (i & 0x0F) == 0x0F ) + { + Q_snprintf( pTemp, sizeof(pTempBuf) - nLen - 1, "\n" ); + Warning( "%s", pTempBuf ); + pTempBuf[0] = '\0'; + nLen = 0; + pTemp = pTempBuf; + } + } + Q_snprintf( pTemp, sizeof(pTempBuf) - nLen - 1, "\n" ); + Warning( "%s", pTempBuf ); +} + + +//----------------------------------------------------------------------------- +// Call this in debug mode to make sure our data is good. +//----------------------------------------------------------------------------- +void CIndexBufferBase::ValidateData( int nIndexCount, const IndexDesc_t& desc ) +{ + /* FIXME */ + // NOTE: Is there anything reasonable to do here at all? + // Or is this a bogus method altogether? +} + + + +//----------------------------------------------------------------------------- +// +// Base mesh +// +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +// constructor, destructor +//----------------------------------------------------------------------------- +CMeshBase::CMeshBase() +{ +} + +CMeshBase::~CMeshBase() +{ +} diff --git a/materialsystem/shaderapidx9/meshbase.h b/materialsystem/shaderapidx9/meshbase.h new file mode 100644 index 0000000..b566099 --- /dev/null +++ b/materialsystem/shaderapidx9/meshbase.h @@ -0,0 +1,309 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//===========================================================================// + +#ifndef MESHBASE_H +#define MESHBASE_H + +#ifdef _WIN32 +#pragma once +#endif + + +#include "materialsystem/imesh.h" +#include "materialsystem/imaterial.h" + + +//----------------------------------------------------------------------------- +// Base vertex buffer +//----------------------------------------------------------------------------- +abstract_class CVertexBufferBase : public IVertexBuffer +{ + // Methods of IVertexBuffer +public: + virtual void Spew( int nVertexCount, const VertexDesc_t &desc ); + virtual void ValidateData( int nVertexCount, const VertexDesc_t& desc ); + +public: + // constructor, destructor + CVertexBufferBase( const char *pBudgetGroupName ); + virtual ~CVertexBufferBase(); + + // Displays the vertex format + static void PrintVertexFormat( VertexFormat_t vertexFormat ); + + // Used to construct vertex data + static void ComputeVertexDescription( unsigned char *pBuffer, VertexFormat_t vertexFormat, VertexDesc_t &desc ); + + // Returns the vertex format size + static int VertexFormatSize( VertexFormat_t vertexFormat ); + +protected: + const char *m_pBudgetGroupName; +}; + + +//----------------------------------------------------------------------------- +// Base index buffer +//----------------------------------------------------------------------------- +abstract_class CIndexBufferBase : public IIndexBuffer +{ + // Methods of IIndexBuffer +public: + virtual void Spew( int nIndexCount, const IndexDesc_t &desc ); + virtual void ValidateData( int nIndexCount, const IndexDesc_t& desc ); + + // Other public methods +public: + // constructor, destructor + CIndexBufferBase( const char *pBudgetGroupName ); + virtual ~CIndexBufferBase() {} + +protected: + const char *m_pBudgetGroupName; +}; + + +//----------------------------------------------------------------------------- +// Base mesh +//----------------------------------------------------------------------------- +class CMeshBase : public IMesh +{ + // Methods of IMesh +public: + + // Other public methods that need to be overridden +public: + // Begins a pass + virtual void BeginPass( ) = 0; + + // Draws a single pass of the mesh + virtual void RenderPass() = 0; + + // Does it have a color mesh? + virtual bool HasColorMesh() const = 0; + + // Am I using morph data? + virtual bool IsUsingMorphData() const = 0; + + virtual bool HasFlexMesh() const = 0; + + virtual IMesh *GetMesh() { return this; } + +public: + // constructor, destructor + CMeshBase(); + virtual ~CMeshBase(); + +}; + +//----------------------------------------------------------------------------- +// Utility method for VertexDesc_t (don't want to expose it in public, in imesh.h) +//----------------------------------------------------------------------------- +inline void ComputeVertexDesc( unsigned char * pBuffer, VertexFormat_t vertexFormat, VertexDesc_t & desc ) +{ + int i; + int *pVertexSizesToSet[64]; + int nVertexSizesToSet = 0; + static ALIGN32 ModelVertexDX8_t temp[4]; + float *dummyData = (float*)&temp; // should be larger than any CMeshBuilder command can set. + + // Determine which vertex compression type this format specifies (affects element sizes/decls): + VertexCompressionType_t compression = CompressionType( vertexFormat ); + desc.m_CompressionType = compression; + + // We use fvf instead of flags here because we may pad out the fvf + // vertex structure to optimize performance + int offset = 0; + // NOTE: At the moment, we assume that if you specify wrinkle, you also specify position + Assert( ( ( vertexFormat & VERTEX_WRINKLE ) == 0 ) || ( ( vertexFormat & VERTEX_POSITION ) != 0 ) ); + if ( vertexFormat & VERTEX_POSITION ) + { + // UNDONE: compress position+wrinkle to SHORT4N, and roll the scale into the transform matrices + desc.m_pPosition = reinterpret_cast(pBuffer); + offset += GetVertexElementSize( VERTEX_ELEMENT_POSITION, compression ); + pVertexSizesToSet[nVertexSizesToSet++] = &desc.m_VertexSize_Position; + + if ( vertexFormat & VERTEX_WRINKLE ) + { + desc.m_pWrinkle = reinterpret_cast( pBuffer + offset ); + offset += GetVertexElementSize( VERTEX_ELEMENT_WRINKLE, compression ); + pVertexSizesToSet[nVertexSizesToSet++] = &desc.m_VertexSize_Wrinkle; + } + else + { + desc.m_pWrinkle = dummyData; + desc.m_VertexSize_Wrinkle = 0; + } + } + else + { + desc.m_pPosition = dummyData; + desc.m_VertexSize_Position = 0; + desc.m_pWrinkle = dummyData; + desc.m_VertexSize_Wrinkle = 0; + } + + // Bone weights/matrix indices + desc.m_NumBoneWeights = NumBoneWeights( vertexFormat ); + + Assert( ( desc.m_NumBoneWeights == 2 ) || ( desc.m_NumBoneWeights == 0 ) ); + + // We assume that if you have any indices/weights, you have exactly two of them + Assert( ( ( desc.m_NumBoneWeights == 2 ) && ( ( vertexFormat & VERTEX_BONE_INDEX ) != 0 ) ) || + ( ( desc.m_NumBoneWeights == 0 ) && ( ( vertexFormat & VERTEX_BONE_INDEX ) == 0 ) ) ); + + if ( ( vertexFormat & VERTEX_BONE_INDEX ) != 0 ) + { + if ( desc.m_NumBoneWeights > 0 ) + { + Assert( desc.m_NumBoneWeights == 2 ); + + // Always exactly two weights + desc.m_pBoneWeight = reinterpret_cast(pBuffer + offset); + offset += GetVertexElementSize( VERTEX_ELEMENT_BONEWEIGHTS2, compression ); + pVertexSizesToSet[nVertexSizesToSet++] = &desc.m_VertexSize_BoneWeight; + } + else + { + desc.m_pBoneWeight = dummyData; + desc.m_VertexSize_BoneWeight = 0; + } + + desc.m_pBoneMatrixIndex = pBuffer + offset; + offset += GetVertexElementSize( VERTEX_ELEMENT_BONEINDEX, compression ); + pVertexSizesToSet[nVertexSizesToSet++] = &desc.m_VertexSize_BoneMatrixIndex; + } + else + { + desc.m_pBoneWeight = dummyData; + desc.m_VertexSize_BoneWeight = 0; + + desc.m_pBoneMatrixIndex = (unsigned char*)dummyData; + desc.m_VertexSize_BoneMatrixIndex = 0; + } + + if ( vertexFormat & VERTEX_NORMAL ) + { + desc.m_pNormal = reinterpret_cast(pBuffer + offset); + // See PackNormal_[SHORT2|UBYTE4|HEND3N] in mathlib.h for the compression algorithm + offset += GetVertexElementSize( VERTEX_ELEMENT_NORMAL, compression ); + pVertexSizesToSet[nVertexSizesToSet++] = &desc.m_VertexSize_Normal; + } + else + { + desc.m_pNormal = dummyData; + desc.m_VertexSize_Normal = 0; + } + + if ( vertexFormat & VERTEX_COLOR ) + { + desc.m_pColor = pBuffer + offset; + offset += GetVertexElementSize( VERTEX_ELEMENT_COLOR, compression ); + pVertexSizesToSet[nVertexSizesToSet++] = &desc.m_VertexSize_Color; + } + else + { + desc.m_pColor = (unsigned char*)dummyData; + desc.m_VertexSize_Color = 0; + } + + if ( vertexFormat & VERTEX_SPECULAR ) + { + desc.m_pSpecular = pBuffer + offset; + offset += GetVertexElementSize( VERTEX_ELEMENT_SPECULAR, compression ); + pVertexSizesToSet[nVertexSizesToSet++] = &desc.m_VertexSize_Specular; + } + else + { + desc.m_pSpecular = (unsigned char*)dummyData; + desc.m_VertexSize_Specular = 0; + } + + // Set up texture coordinates + for ( i = 0; i < VERTEX_MAX_TEXTURE_COORDINATES; ++i ) + { + // FIXME: compress texcoords to SHORT2N/SHORT4N, with a scale rolled into the texture transform + VertexElement_t texCoordElements[4] = { VERTEX_ELEMENT_TEXCOORD1D_0, VERTEX_ELEMENT_TEXCOORD2D_0, VERTEX_ELEMENT_TEXCOORD3D_0, VERTEX_ELEMENT_TEXCOORD4D_0 }; + int nSize = TexCoordSize( i, vertexFormat ); + if ( nSize != 0 ) + { + desc.m_pTexCoord[i] = reinterpret_cast(pBuffer + offset); + VertexElement_t texCoordElement = (VertexElement_t)( texCoordElements[ nSize - 1 ] + i ); + offset += GetVertexElementSize( texCoordElement, compression ); + pVertexSizesToSet[nVertexSizesToSet++] = &desc.m_VertexSize_TexCoord[i]; + } + else + { + desc.m_pTexCoord[i] = dummyData; + desc.m_VertexSize_TexCoord[i] = 0; + } + } + + // Binormal + tangent... + // Note we have to put these at the end so the vertex is FVF + stuff at end + if ( vertexFormat & VERTEX_TANGENT_S ) + { + // UNDONE: use normal compression here (use mem_dumpvballocs to see if this uses much memory) + desc.m_pTangentS = reinterpret_cast(pBuffer + offset); + offset += GetVertexElementSize( VERTEX_ELEMENT_TANGENT_S, compression ); + pVertexSizesToSet[nVertexSizesToSet++] = &desc.m_VertexSize_TangentS; + } + else + { + desc.m_pTangentS = dummyData; + desc.m_VertexSize_TangentS = 0; + } + + if ( vertexFormat & VERTEX_TANGENT_T ) + { + // UNDONE: use normal compression here (use mem_dumpvballocs to see if this uses much memory) + desc.m_pTangentT = reinterpret_cast(pBuffer + offset); + offset += GetVertexElementSize( VERTEX_ELEMENT_TANGENT_T, compression ); + pVertexSizesToSet[nVertexSizesToSet++] = &desc.m_VertexSize_TangentT; + } + else + { + desc.m_pTangentT = dummyData; + desc.m_VertexSize_TangentT = 0; + } + + // User data.. + int userDataSize = UserDataSize( vertexFormat ); + if ( userDataSize > 0 ) + { + desc.m_pUserData = reinterpret_cast(pBuffer + offset); + VertexElement_t userDataElement = (VertexElement_t)( VERTEX_ELEMENT_USERDATA1 + ( userDataSize - 1 ) ); + // See PackNormal_[SHORT2|UBYTE4|HEND3N] in mathlib.h for the compression algorithm + offset += GetVertexElementSize( userDataElement, compression ); + + pVertexSizesToSet[nVertexSizesToSet++] = &desc.m_VertexSize_UserData; + } + else + { + desc.m_pUserData = dummyData; + desc.m_VertexSize_UserData = 0; + } + + // We always use vertex sizes which are half-cache aligned (16 bytes) + // x360 compressed vertexes are not compatible with forced alignments + bool bCacheAlign = ( vertexFormat & VERTEX_FORMAT_USE_EXACT_FORMAT ) == 0; + if ( bCacheAlign && ( offset > 16 ) && IsPC() ) + { + offset = (offset + 0xF) & (~0xF); + } + desc.m_ActualVertexSize = offset; + + // Now set the m_VertexSize for all the members that were actually valid. + Assert( nVertexSizesToSet < sizeof(pVertexSizesToSet)/sizeof(pVertexSizesToSet[0]) ); + for ( int iElement=0; iElement < nVertexSizesToSet; iElement++ ) + { + *pVertexSizesToSet[iElement] = offset; + } +} + +#endif // MESHBASE_H diff --git a/materialsystem/shaderapidx9/meshdx10.cpp b/materialsystem/shaderapidx9/meshdx10.cpp new file mode 100644 index 0000000..b1a892c --- /dev/null +++ b/materialsystem/shaderapidx9/meshdx10.cpp @@ -0,0 +1,775 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//===========================================================================// + +#include +#undef GetCommandLine + +#include "meshdx10.h" +#include "utlvector.h" +#include "materialsystem/imaterialsystem.h" +#include "IHardwareConfigInternal.h" +#include "shaderapi_global.h" +#include "shaderapi/ishaderutil.h" +#include "shaderapi/ishaderapi.h" +#include "shaderdevicedx10.h" +#include "materialsystem/imesh.h" +#include "tier0/vprof.h" +#include "tier0/dbg.h" +#include "materialsystem/idebugtextureinfo.h" +#include "materialsystem/ivballoctracker.h" +#include "tier2/tier2.h" + + +//----------------------------------------------------------------------------- +// +// Dx10 implementation of a vertex buffer +// +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +// globals +//----------------------------------------------------------------------------- +#ifdef _DEBUG +int CVertexBufferDx10::s_nBufferCount = 0; +#endif + + +//----------------------------------------------------------------------------- +// constructor, destructor +//----------------------------------------------------------------------------- +CVertexBufferDx10::CVertexBufferDx10( ShaderBufferType_t type, VertexFormat_t fmt, int nVertexCount, const char *pBudgetGroupName ) : + BaseClass( pBudgetGroupName ) +{ + Assert( nVertexCount != 0 ); + + m_pVertexBuffer = NULL; + m_VertexFormat = fmt; + m_nVertexCount = ( fmt == VERTEX_FORMAT_UNKNOWN ) ? 0 : nVertexCount; + m_nBufferSize = ( fmt == VERTEX_FORMAT_UNKNOWN ) ? nVertexCount : nVertexCount * VertexSize(); + m_nFirstUnwrittenOffset = 0; + m_bIsLocked = false; + m_bIsDynamic = ( type == SHADER_BUFFER_TYPE_DYNAMIC ) || ( type == SHADER_BUFFER_TYPE_DYNAMIC_TEMP ); + m_bFlush = false; +} + +CVertexBufferDx10::~CVertexBufferDx10() +{ + Free(); +} + + +//----------------------------------------------------------------------------- +// Creates, destroys the vertex buffer +//----------------------------------------------------------------------------- +bool CVertexBufferDx10::Allocate( ) +{ + Assert( !m_pVertexBuffer ); + + m_nFirstUnwrittenOffset = 0; + + D3D10_BUFFER_DESC bd; + bd.Usage = D3D10_USAGE_DYNAMIC; + bd.ByteWidth = m_nBufferSize; + bd.BindFlags = D3D10_BIND_VERTEX_BUFFER; + bd.CPUAccessFlags = D3D10_CPU_ACCESS_WRITE; + bd.MiscFlags = 0; + + HRESULT hr = D3D10Device()->CreateBuffer( &bd, NULL, &m_pVertexBuffer ); + bool bOk = !FAILED( hr ) && ( m_pVertexBuffer != 0 ); + + if ( bOk ) + { + // Track VB allocations + g_VBAllocTracker->CountVB( m_pVertexBuffer, m_bIsDynamic, m_nBufferSize, VertexSize(), GetVertexFormat() ); + + if ( !m_bIsDynamic ) + { + VPROF_INCREMENT_GROUP_COUNTER( "TexGroup_global_" TEXTURE_GROUP_STATIC_INDEX_BUFFER, + COUNTER_GROUP_TEXTURE_GLOBAL, m_nBufferSize ); + } + else + { + VPROF_INCREMENT_GROUP_COUNTER( "TexGroup_global_" TEXTURE_GROUP_DYNAMIC_INDEX_BUFFER, + COUNTER_GROUP_TEXTURE_GLOBAL, m_nBufferSize ); + // Dynamic meshes should never be compressed (slows down writing to them) + Assert( CompressionType( GetVertexFormat() ) == VERTEX_COMPRESSION_NONE ); + } +#ifdef _DEBUG + ++s_nBufferCount; +#endif + } + + return bOk; +} + +void CVertexBufferDx10::Free() +{ + if ( m_pVertexBuffer ) + { +#ifdef _DEBUG + --s_nBufferCount; +#endif + + // Track VB allocations + g_VBAllocTracker->UnCountVB( m_pVertexBuffer ); + + m_pVertexBuffer->Release(); + m_pVertexBuffer = NULL; + + if ( !m_bIsDynamic ) + { + VPROF_INCREMENT_GROUP_COUNTER( "TexGroup_global_" TEXTURE_GROUP_STATIC_INDEX_BUFFER, + COUNTER_GROUP_TEXTURE_GLOBAL, - m_nBufferSize ); + } + else + { + VPROF_INCREMENT_GROUP_COUNTER( "TexGroup_global_" TEXTURE_GROUP_DYNAMIC_INDEX_BUFFER, + COUNTER_GROUP_TEXTURE_GLOBAL, - m_nBufferSize ); + } + } +} + + +//----------------------------------------------------------------------------- +// Vertex Buffer info +//----------------------------------------------------------------------------- +int CVertexBufferDx10::VertexCount() const +{ + Assert( !m_bIsDynamic ); + return m_nVertexCount; +} + + +//----------------------------------------------------------------------------- +// Returns the buffer format (only valid for static index buffers) +//----------------------------------------------------------------------------- +VertexFormat_t CVertexBufferDx10::GetVertexFormat() const +{ + Assert( !m_bIsDynamic ); + return m_VertexFormat; +} + + +//----------------------------------------------------------------------------- +// Returns true if the buffer is dynamic +//----------------------------------------------------------------------------- +bool CVertexBufferDx10::IsDynamic() const +{ + return m_bIsDynamic; +} + + +//----------------------------------------------------------------------------- +// Only used by dynamic buffers, indicates the next lock should perform a discard. +//----------------------------------------------------------------------------- +void CVertexBufferDx10::Flush() +{ + // This strange-looking line makes a flush only occur if the buffer is dynamic. + m_bFlush = m_bIsDynamic; +} + + +//----------------------------------------------------------------------------- +// Casts a dynamic buffer to be a particular vertex type +//----------------------------------------------------------------------------- +void CVertexBufferDx10::BeginCastBuffer( VertexFormat_t format ) +{ + Assert( format != MATERIAL_INDEX_FORMAT_UNKNOWN ); + Assert( m_bIsDynamic && ( m_VertexFormat == 0 || m_VertexFormat == format ) ); + if ( !m_bIsDynamic ) + return; + + m_VertexFormat = format; + m_nVertexCount = m_nBufferSize / VertexSize(); +} + +void CVertexBufferDx10::EndCastBuffer( ) +{ + Assert( m_bIsDynamic && m_VertexFormat != 0 ); + if ( !m_bIsDynamic ) + return; + m_VertexFormat = 0; + m_nVertexCount = 0; +} + + +//----------------------------------------------------------------------------- +// Returns the number of indices that can be written into the buffer +//----------------------------------------------------------------------------- +int CVertexBufferDx10::GetRoomRemaining() const +{ + return ( m_nBufferSize - m_nFirstUnwrittenOffset ) / VertexSize(); +} + + +//----------------------------------------------------------------------------- +// Lock, unlock +//----------------------------------------------------------------------------- +bool CVertexBufferDx10::Lock( int nMaxVertexCount, bool bAppend, VertexDesc_t &desc ) +{ + Assert( !m_bIsLocked && ( nMaxVertexCount != 0 ) && ( nMaxVertexCount <= m_nVertexCount ) ); + Assert( m_VertexFormat != 0 ); + + // FIXME: Why do we need to sync matrices now? + ShaderUtil()->SyncMatrices(); + g_ShaderMutex.Lock(); + + void *pLockedData = NULL; + HRESULT hr; + + // This can happen if the buffer was locked but a type wasn't bound + if ( m_VertexFormat == 0 ) + goto vertexBufferLockFailed; + + // Just give the app crap buffers to fill up while we're suppressed... + if ( g_pShaderDevice->IsDeactivated() || ( nMaxVertexCount == 0 ) ) + goto vertexBufferLockFailed; + + // Did we ask for something too large? + if ( nMaxVertexCount > m_nVertexCount ) + { + Warning( "Too many vertices for vertex buffer. . tell a programmer (%d>%d)\n", nMaxVertexCount, m_nVertexCount ); + goto vertexBufferLockFailed; + } + + // We might not have a buffer owing to alt-tab type stuff + if ( !m_pVertexBuffer ) + { + if ( !Allocate() ) + goto vertexBufferLockFailed; + } + + // Check to see if we have enough memory + int nMemoryRequired = nMaxVertexCount * VertexSize(); + bool bHasEnoughMemory = ( m_nFirstUnwrittenOffset + nMemoryRequired <= m_nBufferSize ); + + D3D10_MAP map; + if ( bAppend ) + { + // Can't have the first lock after a flush be an appending lock + Assert( !m_bFlush ); + + // If we're appending and we don't have enough room, then puke! + if ( !bHasEnoughMemory || m_bFlush ) + goto vertexBufferLockFailed; + map = ( m_nFirstUnwrittenOffset == 0 ) ? D3D10_MAP_WRITE_DISCARD : D3D10_MAP_WRITE_NO_OVERWRITE; + } + else + { + // If we're not appending, no overwrite unless we don't have enough room + // If we're a static buffer, always discard if we're not appending + if ( !m_bFlush && bHasEnoughMemory && m_bIsDynamic ) + { + map = ( m_nFirstUnwrittenOffset == 0 ) ? D3D10_MAP_WRITE_DISCARD : D3D10_MAP_WRITE_NO_OVERWRITE; + } + else + { + map = D3D10_MAP_WRITE_DISCARD; + m_nFirstUnwrittenOffset = 0; + m_bFlush = false; + } + } + + hr = m_pVertexBuffer->Map( map, 0, &pLockedData ); + if ( FAILED( hr ) ) + { + Warning( "Failed to lock vertex buffer in CVertexBufferDx10::Lock\n" ); + goto vertexBufferLockFailed; + } + + ComputeVertexDescription( (unsigned char*)pLockedData + m_nFirstUnwrittenOffset, m_VertexFormat, desc ); + desc.m_nFirstVertex = 0; + desc.m_nOffset = m_nFirstUnwrittenOffset; + m_bIsLocked = true; + return true; + +vertexBufferLockFailed: + g_ShaderMutex.Unlock(); + + // Set up a bogus index descriptor + ComputeVertexDescription( 0, 0, desc ); + desc.m_nFirstVertex = 0; + desc.m_nOffset = 0; + return false; +} + +void CVertexBufferDx10::Unlock( int nWrittenVertexCount, VertexDesc_t &desc ) +{ + Assert( nWrittenVertexCount <= m_nVertexCount ); + + // NOTE: This can happen if the lock occurs during alt-tab + // or if another application is initializing + if ( !m_bIsLocked ) + return; + + if ( m_pVertexBuffer ) + { + m_pVertexBuffer->Unmap(); + } + + m_nFirstUnwrittenOffset += nWrittenVertexCount * VertexSize(); + m_bIsLocked = false; + g_ShaderMutex.Unlock(); +} + + +//----------------------------------------------------------------------------- +// +// Dx10 implementation of an index buffer +// +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +// globals +//----------------------------------------------------------------------------- + +// shove indices into this if you don't actually want indices +static unsigned int s_nScratchIndexBuffer = 0; + +#ifdef _DEBUG +int CIndexBufferDx10::s_nBufferCount = 0; +#endif + + +//----------------------------------------------------------------------------- +// constructor, destructor +//----------------------------------------------------------------------------- +CIndexBufferDx10::CIndexBufferDx10( ShaderBufferType_t type, MaterialIndexFormat_t fmt, int nIndexCount, const char *pBudgetGroupName ) : + BaseClass( pBudgetGroupName ) +{ + Assert( nIndexCount != 0 ); + Assert( IsDynamicBufferType( type ) || ( fmt != MATERIAL_INDEX_FORMAT_UNKNOWN ) ); + + m_pIndexBuffer = NULL; + m_IndexFormat = fmt; + m_nIndexCount = ( fmt == MATERIAL_INDEX_FORMAT_UNKNOWN ) ? 0 : nIndexCount; + m_nBufferSize = ( fmt == MATERIAL_INDEX_FORMAT_UNKNOWN ) ? nIndexCount : nIndexCount * IndexSize(); + m_nFirstUnwrittenOffset = 0; + m_bIsLocked = false; + m_bIsDynamic = IsDynamicBufferType( type ); + m_bFlush = false; + + // NOTE: This has to happen at the end since m_IndexFormat must be valid for IndexSize() to work + if ( m_bIsDynamic ) + { + m_IndexFormat = MATERIAL_INDEX_FORMAT_UNKNOWN; + m_nIndexCount = 0; + } +} + +CIndexBufferDx10::~CIndexBufferDx10() +{ + Free(); +} + + +//----------------------------------------------------------------------------- +// Creates, destroys the index buffer +//----------------------------------------------------------------------------- +bool CIndexBufferDx10::Allocate( ) +{ + Assert( !m_pIndexBuffer ); + + m_nFirstUnwrittenOffset = 0; + + D3D10_BUFFER_DESC bd; + bd.Usage = D3D10_USAGE_DYNAMIC; + bd.ByteWidth = m_nBufferSize; + bd.BindFlags = D3D10_BIND_INDEX_BUFFER; + bd.CPUAccessFlags = D3D10_CPU_ACCESS_WRITE; + bd.MiscFlags = 0; + + HRESULT hr = D3D10Device()->CreateBuffer( &bd, NULL, &m_pIndexBuffer ); + bool bOk = !FAILED( hr ) && ( m_pIndexBuffer != NULL ); + + if ( bOk ) + { + if ( !m_bIsDynamic ) + { + VPROF_INCREMENT_GROUP_COUNTER( "TexGroup_global_" TEXTURE_GROUP_STATIC_INDEX_BUFFER, + COUNTER_GROUP_TEXTURE_GLOBAL, m_nBufferSize ); + } + else + { + VPROF_INCREMENT_GROUP_COUNTER( "TexGroup_global_" TEXTURE_GROUP_DYNAMIC_INDEX_BUFFER, + COUNTER_GROUP_TEXTURE_GLOBAL, m_nBufferSize ); + } +#ifdef _DEBUG + ++s_nBufferCount; +#endif + } + + return bOk; +} + +void CIndexBufferDx10::Free() +{ + if ( m_pIndexBuffer ) + { +#ifdef _DEBUG + --s_nBufferCount; +#endif + + m_pIndexBuffer->Release(); + m_pIndexBuffer = NULL; + + if ( !m_bIsDynamic ) + { + VPROF_INCREMENT_GROUP_COUNTER( "TexGroup_global_" TEXTURE_GROUP_STATIC_INDEX_BUFFER, + COUNTER_GROUP_TEXTURE_GLOBAL, - m_nBufferSize ); + } + else + { + VPROF_INCREMENT_GROUP_COUNTER( "TexGroup_global_" TEXTURE_GROUP_DYNAMIC_INDEX_BUFFER, + COUNTER_GROUP_TEXTURE_GLOBAL, - m_nBufferSize ); + } + } +} + + +//----------------------------------------------------------------------------- +// Returns the buffer size (only valid for static index buffers) +//----------------------------------------------------------------------------- +int CIndexBufferDx10::IndexCount() const +{ + Assert( !m_bIsDynamic ); + return m_nIndexCount; +} + + +//----------------------------------------------------------------------------- +// Returns the buffer format (only valid for static index buffers) +//----------------------------------------------------------------------------- +MaterialIndexFormat_t CIndexBufferDx10::IndexFormat() const +{ + Assert( !m_bIsDynamic ); + return m_IndexFormat; +} + + +//----------------------------------------------------------------------------- +// Returns true if the buffer is dynamic +//----------------------------------------------------------------------------- +bool CIndexBufferDx10::IsDynamic() const +{ + return m_bIsDynamic; +} + + +//----------------------------------------------------------------------------- +// Only used by dynamic buffers, indicates the next lock should perform a discard. +//----------------------------------------------------------------------------- +void CIndexBufferDx10::Flush() +{ + // This strange-looking line makes a flush only occur if the buffer is dynamic. + m_bFlush = m_bIsDynamic; +} + + +//----------------------------------------------------------------------------- +// Casts a dynamic buffer to be a particular index type +//----------------------------------------------------------------------------- +void CIndexBufferDx10::BeginCastBuffer( MaterialIndexFormat_t format ) +{ + Assert( format != MATERIAL_INDEX_FORMAT_UNKNOWN ); + Assert( m_bIsDynamic && ( m_IndexFormat == MATERIAL_INDEX_FORMAT_UNKNOWN || m_IndexFormat == format ) ); + if ( !m_bIsDynamic ) + return; + + m_IndexFormat = format; + m_nIndexCount = m_nBufferSize / IndexSize(); +} + +void CIndexBufferDx10::EndCastBuffer( ) +{ + Assert( m_bIsDynamic && m_IndexFormat != MATERIAL_INDEX_FORMAT_UNKNOWN ); + if ( !m_bIsDynamic ) + return; + m_IndexFormat = MATERIAL_INDEX_FORMAT_UNKNOWN; + m_nIndexCount = 0; +} + + +//----------------------------------------------------------------------------- +// Returns the number of indices that can be written into the buffer +//----------------------------------------------------------------------------- +int CIndexBufferDx10::GetRoomRemaining() const +{ + return ( m_nBufferSize - m_nFirstUnwrittenOffset ) / IndexSize(); +} + + +//----------------------------------------------------------------------------- +// Locks, unlocks the mesh +//----------------------------------------------------------------------------- +bool CIndexBufferDx10::Lock( int nMaxIndexCount, bool bAppend, IndexDesc_t &desc ) +{ + Assert( !m_bIsLocked && ( nMaxIndexCount != 0 ) && ( nMaxIndexCount <= m_nIndexCount ) ); + Assert( m_IndexFormat != MATERIAL_INDEX_FORMAT_UNKNOWN ); + + // FIXME: Why do we need to sync matrices now? + ShaderUtil()->SyncMatrices(); + g_ShaderMutex.Lock(); + + void *pLockedData = NULL; + HRESULT hr; + + // This can happen if the buffer was locked but a type wasn't bound + if ( m_IndexFormat == MATERIAL_INDEX_FORMAT_UNKNOWN ) + goto indexBufferLockFailed; + + // Just give the app crap buffers to fill up while we're suppressed... + if ( g_pShaderDevice->IsDeactivated() || ( nMaxIndexCount == 0 ) ) + goto indexBufferLockFailed; + + // Did we ask for something too large? + if ( nMaxIndexCount > m_nIndexCount ) + { + Warning( "Too many indices for index buffer. . tell a programmer (%d>%d)\n", nMaxIndexCount, m_nIndexCount ); + goto indexBufferLockFailed; + } + + // We might not have a buffer owing to alt-tab type stuff + if ( !m_pIndexBuffer ) + { + if ( !Allocate() ) + goto indexBufferLockFailed; + } + + // Check to see if we have enough memory + int nMemoryRequired = nMaxIndexCount * IndexSize(); + bool bHasEnoughMemory = ( m_nFirstUnwrittenOffset + nMemoryRequired <= m_nBufferSize ); + + D3D10_MAP map; + if ( bAppend ) + { + // Can't have the first lock after a flush be an appending lock + Assert( !m_bFlush ); + + // If we're appending and we don't have enough room, then puke! + if ( !bHasEnoughMemory || m_bFlush ) + goto indexBufferLockFailed; + map = ( m_nFirstUnwrittenOffset == 0 ) ? D3D10_MAP_WRITE_DISCARD : D3D10_MAP_WRITE_NO_OVERWRITE; + } + else + { + // If we're not appending, no overwrite unless we don't have enough room + if ( !m_bFlush && bHasEnoughMemory && m_bIsDynamic ) + { + map = ( m_nFirstUnwrittenOffset == 0 ) ? D3D10_MAP_WRITE_DISCARD : D3D10_MAP_WRITE_NO_OVERWRITE; + } + else + { + map = D3D10_MAP_WRITE_DISCARD; + m_nFirstUnwrittenOffset = 0; + m_bFlush = false; + } + } + + hr = m_pIndexBuffer->Map( map, 0, &pLockedData ); + if ( FAILED( hr ) ) + { + Warning( "Failed to lock index buffer in CIndexBufferDx10::Lock\n" ); + goto indexBufferLockFailed; + } + + desc.m_pIndices = (unsigned short*)( (unsigned char*)pLockedData + m_nFirstUnwrittenOffset ); + desc.m_nIndexSize = IndexSize() >> 1; + desc.m_nFirstIndex = 0; + desc.m_nOffset = m_nFirstUnwrittenOffset; + m_bIsLocked = true; + return true; + +indexBufferLockFailed: + g_ShaderMutex.Unlock(); + + // Set up a bogus index descriptor + desc.m_pIndices = (unsigned short*)( &s_nScratchIndexBuffer ); + desc.m_nFirstIndex = 0; + desc.m_nIndexSize = 0; + desc.m_nOffset = 0; + return false; +} + +void CIndexBufferDx10::Unlock( int nWrittenIndexCount, IndexDesc_t &desc ) +{ + Assert( nWrittenIndexCount <= m_nIndexCount ); + + // NOTE: This can happen if the lock occurs during alt-tab + // or if another application is initializing + if ( !m_bIsLocked ) + return; + + if ( m_pIndexBuffer ) + { + m_pIndexBuffer->Unmap(); + } + + m_nFirstUnwrittenOffset += nWrittenIndexCount * IndexSize(); + m_bIsLocked = false; + g_ShaderMutex.Unlock(); +} + + +//----------------------------------------------------------------------------- +// Locks, unlocks an existing mesh +//----------------------------------------------------------------------------- +void CIndexBufferDx10::ModifyBegin( bool bReadOnly, int nFirstIndex, int nIndexCount, IndexDesc_t& desc ) +{ + Assert( 0 ); +} + +void CIndexBufferDx10::ModifyEnd( IndexDesc_t& desc ) +{ + +} + + +//----------------------------------------------------------------------------- +// +// The empty mesh... +// +//----------------------------------------------------------------------------- +CMeshDx10::CMeshDx10() +{ + m_pVertexMemory = new unsigned char[VERTEX_BUFFER_SIZE]; +} + +CMeshDx10::~CMeshDx10() +{ + delete[] m_pVertexMemory; +} + +void CMeshDx10::LockMesh( int numVerts, int numIndices, MeshDesc_t& desc ) +{ + // Who cares about the data? + desc.m_pPosition = (float*)m_pVertexMemory; + desc.m_pNormal = (float*)m_pVertexMemory; + desc.m_pColor = m_pVertexMemory; + int i; + for ( i = 0; i < VERTEX_MAX_TEXTURE_COORDINATES; ++i) + desc.m_pTexCoord[i] = (float*)m_pVertexMemory; + desc.m_pIndices = (unsigned short*)m_pVertexMemory; + + desc.m_pBoneWeight = (float*)m_pVertexMemory; + desc.m_pBoneMatrixIndex = (unsigned char*)m_pVertexMemory; + desc.m_pTangentS = (float*)m_pVertexMemory; + desc.m_pTangentT = (float*)m_pVertexMemory; + desc.m_pUserData = (float*)m_pVertexMemory; + desc.m_NumBoneWeights = 2; + + desc.m_VertexSize_Position = 0; + desc.m_VertexSize_BoneWeight = 0; + desc.m_VertexSize_BoneMatrixIndex = 0; + desc.m_VertexSize_Normal = 0; + desc.m_VertexSize_Color = 0; + for( i=0; i < VERTEX_MAX_TEXTURE_COORDINATES; i++ ) + desc.m_VertexSize_TexCoord[i] = 0; + desc.m_VertexSize_TangentS = 0; + desc.m_VertexSize_TangentT = 0; + desc.m_VertexSize_UserData = 0; + desc.m_ActualVertexSize = 0; // Size of the vertices.. Some of the m_VertexSize_ elements above + + desc.m_nFirstVertex = 0; + desc.m_nIndexSize = 0; +} + +void CMeshDx10::UnlockMesh( int numVerts, int numIndices, MeshDesc_t& desc ) +{ +} + +void CMeshDx10::ModifyBeginEx( bool bReadOnly, int firstVertex, int numVerts, int firstIndex, int numIndices, MeshDesc_t& desc ) +{ + // Who cares about the data? + desc.m_pPosition = (float*)m_pVertexMemory; + desc.m_pNormal = (float*)m_pVertexMemory; + desc.m_pColor = m_pVertexMemory; + int i; + for ( i = 0; i < VERTEX_MAX_TEXTURE_COORDINATES; ++i) + desc.m_pTexCoord[i] = (float*)m_pVertexMemory; + desc.m_pIndices = (unsigned short*)m_pVertexMemory; + + desc.m_pBoneWeight = (float*)m_pVertexMemory; + desc.m_pBoneMatrixIndex = (unsigned char*)m_pVertexMemory; + desc.m_pTangentS = (float*)m_pVertexMemory; + desc.m_pTangentT = (float*)m_pVertexMemory; + desc.m_pUserData = (float*)m_pVertexMemory; + desc.m_NumBoneWeights = 2; + + desc.m_VertexSize_Position = 0; + desc.m_VertexSize_BoneWeight = 0; + desc.m_VertexSize_BoneMatrixIndex = 0; + desc.m_VertexSize_Normal = 0; + desc.m_VertexSize_Color = 0; + for( i=0; i < VERTEX_MAX_TEXTURE_COORDINATES; i++ ) + desc.m_VertexSize_TexCoord[i] = 0; + desc.m_VertexSize_TangentS = 0; + desc.m_VertexSize_TangentT = 0; + desc.m_VertexSize_UserData = 0; + desc.m_ActualVertexSize = 0; // Size of the vertices.. Some of the m_VertexSize_ elements above + + desc.m_nFirstVertex = 0; + desc.m_nIndexSize = 0; +} + +void CMeshDx10::ModifyBegin( int firstVertex, int numVerts, int firstIndex, int numIndices, MeshDesc_t& desc ) +{ + ModifyBeginEx( false, firstVertex, numVerts, firstIndex, numIndices, desc ); +} + +void CMeshDx10::ModifyEnd( MeshDesc_t& desc ) +{ +} + +// returns the # of vertices (static meshes only) +int CMeshDx10::VertexCount() const +{ + return 0; +} + +// Sets the primitive type +void CMeshDx10::SetPrimitiveType( MaterialPrimitiveType_t type ) +{ +} + +// Draws the entire mesh +void CMeshDx10::Draw( int firstIndex, int numIndices ) +{ +} + +void CMeshDx10::Draw(CPrimList *pPrims, int nPrims) +{ +} + +// Copy verts and/or indices to a mesh builder. This only works for temp meshes! +void CMeshDx10::CopyToMeshBuilder( + int iStartVert, // Which vertices to copy. + int nVerts, + int iStartIndex, // Which indices to copy. + int nIndices, + int indexOffset, // This is added to each index. + CMeshBuilder &builder ) +{ +} + +// Spews the mesh data +void CMeshDx10::Spew( int numVerts, int numIndices, const MeshDesc_t & desc ) +{ +} + +void CMeshDx10::ValidateData( int numVerts, int numIndices, const MeshDesc_t & desc ) +{ +} + +// gets the associated material +IMaterial* CMeshDx10::GetMaterial() +{ + // umm. this don't work none + Assert(0); + return 0; +} diff --git a/materialsystem/shaderapidx9/meshdx10.h b/materialsystem/shaderapidx9/meshdx10.h new file mode 100644 index 0000000..2279612 --- /dev/null +++ b/materialsystem/shaderapidx9/meshdx10.h @@ -0,0 +1,282 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//===========================================================================// + + +#ifndef MESHDX10_H +#define MESHDX10_H + +#ifdef _WIN32 +#pragma once +#endif + +#include "meshbase.h" +#include "shaderapi/ishaderdevice.h" + + +//----------------------------------------------------------------------------- +// Forward declaration +//----------------------------------------------------------------------------- +struct ID3D10Buffer; + + +//----------------------------------------------------------------------------- +// Dx10 implementation of a vertex buffer +//----------------------------------------------------------------------------- +class CVertexBufferDx10 : public CVertexBufferBase +{ + typedef CVertexBufferBase BaseClass; + + // Methods of IVertexBuffer +public: + virtual int VertexCount() const; + virtual VertexFormat_t GetVertexFormat() const; + virtual bool Lock( int nMaxVertexCount, bool bAppend, VertexDesc_t &desc ); + virtual void Unlock( int nWrittenVertexCount, VertexDesc_t &desc ); + virtual bool IsDynamic() const; + virtual void BeginCastBuffer( VertexFormat_t format ); + virtual void EndCastBuffer( ); + virtual int GetRoomRemaining() const; + + // Other public methods +public: + // constructor, destructor + CVertexBufferDx10( ShaderBufferType_t type, VertexFormat_t fmt, int nVertexCount, const char *pBudgetGroupName ); + virtual ~CVertexBufferDx10(); + + ID3D10Buffer* GetDx10Buffer() const; + int VertexSize() const; + + // Only used by dynamic buffers, indicates the next lock should perform a discard. + void Flush(); + +protected: + // Creates, destroys the index buffer + bool Allocate( ); + void Free(); + + ID3D10Buffer *m_pVertexBuffer; + VertexFormat_t m_VertexFormat; + int m_nVertexCount; + int m_nBufferSize; + int m_nFirstUnwrittenOffset; + bool m_bIsLocked : 1; + bool m_bIsDynamic : 1; + bool m_bFlush : 1; // Used only for dynamic buffers, indicates to discard the next time + +#ifdef _DEBUG + static int s_nBufferCount; +#endif +}; + + +//----------------------------------------------------------------------------- +// inline methods for CVertexBufferDx10 +//----------------------------------------------------------------------------- +inline ID3D10Buffer* CVertexBufferDx10::GetDx10Buffer() const +{ + return m_pVertexBuffer; +} + +inline int CVertexBufferDx10::VertexSize() const +{ + return VertexFormatSize( m_VertexFormat ); +} + + +//----------------------------------------------------------------------------- +// Dx10 implementation of an index buffer +//----------------------------------------------------------------------------- +class CIndexBufferDx10 : public CIndexBufferBase +{ + typedef CIndexBufferBase BaseClass; + + // Methods of IIndexBuffer +public: + virtual int IndexCount() const; + virtual MaterialIndexFormat_t IndexFormat() const; + virtual int GetRoomRemaining() const; + virtual bool Lock( int nMaxIndexCount, bool bAppend, IndexDesc_t &desc ); + virtual void Unlock( int nWrittenIndexCount, IndexDesc_t &desc ); + virtual void ModifyBegin( bool bReadOnly, int nFirstIndex, int nIndexCount, IndexDesc_t& desc ); + virtual void ModifyEnd( IndexDesc_t& desc ); + virtual bool IsDynamic() const; + virtual void BeginCastBuffer( MaterialIndexFormat_t format ); + virtual void EndCastBuffer( ); + + // Other public methods +public: + // constructor, destructor + CIndexBufferDx10( ShaderBufferType_t type, MaterialIndexFormat_t fmt, int nIndexCount, const char *pBudgetGroupName ); + virtual ~CIndexBufferDx10(); + + ID3D10Buffer* GetDx10Buffer() const; + MaterialIndexFormat_t GetIndexFormat() const; + + // Only used by dynamic buffers, indicates the next lock should perform a discard. + void Flush(); + +protected: + // Creates, destroys the index buffer + bool Allocate( ); + void Free(); + + // Returns the size of the index in bytes + int IndexSize() const; + + ID3D10Buffer *m_pIndexBuffer; + MaterialIndexFormat_t m_IndexFormat; + int m_nIndexCount; + int m_nBufferSize; + int m_nFirstUnwrittenOffset; // Used only for dynamic buffers, indicates where it's safe to write (nooverwrite) + bool m_bIsLocked : 1; + bool m_bIsDynamic : 1; + bool m_bFlush : 1; // Used only for dynamic buffers, indicates to discard the next time + +#ifdef _DEBUG + static int s_nBufferCount; +#endif +}; + + +//----------------------------------------------------------------------------- +// Returns the size of the index in bytes +//----------------------------------------------------------------------------- +inline int CIndexBufferDx10::IndexSize() const +{ + switch( m_IndexFormat ) + { + default: + case MATERIAL_INDEX_FORMAT_UNKNOWN: + return 0; + case MATERIAL_INDEX_FORMAT_16BIT: + return 2; + case MATERIAL_INDEX_FORMAT_32BIT: + return 4; + } +} + +inline ID3D10Buffer* CIndexBufferDx10::GetDx10Buffer() const +{ + return m_pIndexBuffer; +} + +inline MaterialIndexFormat_t CIndexBufferDx10::GetIndexFormat() const +{ + return m_IndexFormat; +} + + +//----------------------------------------------------------------------------- +// Dx10 implementation of a mesh +//----------------------------------------------------------------------------- +class CMeshDx10 : public CMeshBase +{ +public: + CMeshDx10(); + virtual ~CMeshDx10(); + + // FIXME: Make this work! Unsupported methods of IIndexBuffer + virtual bool Lock( int nMaxIndexCount, bool bAppend, IndexDesc_t& desc ) { Assert(0); return false; } + virtual void Unlock( int nWrittenIndexCount, IndexDesc_t& desc ) { Assert(0); } + virtual int GetRoomRemaining() const { Assert(0); return 0; } + virtual void ModifyBegin( bool bReadOnly, int nFirstIndex, int nIndexCount, IndexDesc_t& desc ) { Assert(0); } + virtual void ModifyEnd( IndexDesc_t& desc ) { Assert(0); } + virtual void Spew( int nIndexCount, const IndexDesc_t & desc ) { Assert(0); } + virtual void ValidateData( int nIndexCount, const IndexDesc_t &desc ) { Assert(0); } + virtual bool Lock( int nVertexCount, bool bAppend, VertexDesc_t &desc ) { Assert(0); return false; } + virtual void Unlock( int nVertexCount, VertexDesc_t &desc ) { Assert(0); } + virtual void Spew( int nVertexCount, const VertexDesc_t &desc ) { Assert(0); } + virtual void ValidateData( int nVertexCount, const VertexDesc_t & desc ) { Assert(0); } + virtual bool IsDynamic() const { Assert(0); return false; } + virtual void BeginCastBuffer( MaterialIndexFormat_t format ) { Assert(0); } + virtual void BeginCastBuffer( VertexFormat_t format ) { Assert(0); } + virtual void EndCastBuffer( ) { Assert(0); } + + void LockMesh( int numVerts, int numIndices, MeshDesc_t& desc ); + void UnlockMesh( int numVerts, int numIndices, MeshDesc_t& desc ); + + void ModifyBeginEx( bool bReadOnly, int firstVertex, int numVerts, int firstIndex, int numIndices, MeshDesc_t& desc ); + void ModifyBegin( int firstVertex, int numVerts, int firstIndex, int numIndices, MeshDesc_t& desc ); + void ModifyEnd( MeshDesc_t& desc ); + + // returns the # of vertices (static meshes only) + int VertexCount() const; + + virtual void BeginPass( ) {} + virtual void RenderPass() {} + virtual bool HasColorMesh() const { return false; } + virtual bool IsUsingMorphData() const { return false; } + virtual bool HasFlexMesh() const { return false; } + + // Sets the primitive type + void SetPrimitiveType( MaterialPrimitiveType_t type ); + + // Draws the entire mesh + void Draw(int firstIndex, int numIndices); + + void Draw(CPrimList *pPrims, int nPrims); + + // Copy verts and/or indices to a mesh builder. This only works for temp meshes! + virtual void CopyToMeshBuilder( + int iStartVert, // Which vertices to copy. + int nVerts, + int iStartIndex, // Which indices to copy. + int nIndices, + int indexOffset, // This is added to each index. + CMeshBuilder &builder ); + + // Spews the mesh data + void Spew( int numVerts, int numIndices, const MeshDesc_t & desc ); + + void ValidateData( int numVerts, int numIndices, const MeshDesc_t & desc ); + + // gets the associated material + IMaterial* GetMaterial(); + + void SetColorMesh( IMesh *pColorMesh, int nVertexOffset ) + { + } + + + virtual int IndexCount() const + { + return 0; + } + + virtual MaterialIndexFormat_t IndexFormat() const + { + Assert( 0 ); + return MATERIAL_INDEX_FORMAT_UNKNOWN; + } + + virtual void SetFlexMesh( IMesh *pMesh, int nVertexOffset ) {} + + virtual void DisableFlexMesh() {} + + virtual void MarkAsDrawn() {} + + virtual unsigned ComputeMemoryUsed() { return 0; } + + virtual VertexFormat_t GetVertexFormat() const { return VERTEX_POSITION; } + + virtual IMesh *GetMesh() + { + return this; + } + +private: + enum + { + VERTEX_BUFFER_SIZE = 1024 * 1024 + }; + + unsigned char* m_pVertexMemory; +}; + +#endif // MESHDX10_H + diff --git a/materialsystem/shaderapidx9/meshdx8.cpp b/materialsystem/shaderapidx9/meshdx8.cpp new file mode 100644 index 0000000..30a4d60 --- /dev/null +++ b/materialsystem/shaderapidx9/meshdx8.cpp @@ -0,0 +1,5982 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//===========================================================================// + +#define DISABLE_PROTECTED_THINGS +#include "locald3dtypes.h" +#include "imeshdx8.h" +#include "shaderapidx8_global.h" +#include "materialsystem/IShader.h" +#include "tier0/vprof.h" +#include "studio.h" +#include "tier1/fmtstr.h" + +#include "tier0/platform.h" +#include "tier0/systeminformation.h" + +// fixme - stick this in a header file. +#if defined( _DEBUG ) && !defined( _X360 ) +// define this if you want to range check all indices when drawing +#define CHECK_INDICES +#endif +#ifdef CHECK_INDICES +#define CHECK_INDICES_MAX_NUM_STREAMS 2 +#endif + +#include "dynamicib.h" +#include "dynamicvb.h" +#include "utlvector.h" +#include "shaderapi/ishaderapi.h" +#include "imaterialinternal.h" +#include "imaterialsysteminternal.h" +#include "shaderapidx8.h" +#include "shaderapi/ishaderutil.h" +#include "materialsystem/imaterialsystemhardwareconfig.h" +#include "materialsystem/materialsystem_config.h" +#include "materialsystem/ivballoctracker.h" +#include "tier1/strtools.h" +#include "convar.h" +#include "shaderdevicedx8.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +//----------------------------------------------------------------------------- +// Uncomment this to test buffered state +//----------------------------------------------------------------------------- +//#define DEBUG_BUFFERED_MESHES 1 + +#define MAX_DX8_STREAMS 16 + +#define VERTEX_FORMAT_INVALID 0xFFFFFFFFFFFFFFFFull + +// this is hooked into the engines convar +extern ConVar mat_debugalttab; + +//#define DRAW_SELECTION 1 +static bool g_bDrawSelection = true; // only used in DRAW_SELECTION +static unsigned short g_nScratchIndexBuffer[6]; // large enough for a fast quad; used when device is not active +#ifdef _DEBUG +int CVertexBuffer::s_BufferCount = 0; +int CIndexBuffer::s_BufferCount = 0; +#endif + +//----------------------------------------------------------------------------- +// Important enumerations +//----------------------------------------------------------------------------- +enum +{ + // DEMEZ + // VERTEX_BUFFER_SIZE = 32768, + VERTEX_BUFFER_SIZE = 65535, + MAX_QUAD_INDICES = 16384, +}; + + +//----------------------------------------------------------------------------- +// +// Code related to vertex buffers start here +// +//----------------------------------------------------------------------------- +class CVertexBufferDx8 : public CVertexBufferBase +{ + typedef CVertexBufferBase BaseClass; + + // Methods of IVertexBuffer +public: + virtual int VertexCount() const; + virtual VertexFormat_t GetVertexFormat() const; + virtual bool IsDynamic() const; + virtual void BeginCastBuffer( VertexFormat_t format ); + virtual void EndCastBuffer( ); + virtual int GetRoomRemaining() const; + virtual bool Lock( int nVertexCount, bool bAppend, VertexDesc_t &desc ); + virtual void Unlock( int nVertexCount, VertexDesc_t &desc ); + +public: + // constructor + CVertexBufferDx8( ShaderBufferType_t type, VertexFormat_t fmt, int nVertexCount, const char *pBudgetGroupName ); + virtual ~CVertexBufferDx8(); + + // Allocates, deallocates the index buffer + bool Allocate( ); + void Free(); + + // Returns the vertex size + int VertexSize() const; + + // Only used by dynamic buffers, indicates the next lock should perform a discard. + void Flush(); + + // Returns the D3D buffer + IDirect3DVertexBuffer9* GetDx9Buffer(); + + // Used to measure how much static buffer memory is touched each frame + void HandlePerFrameTextureStats( int nFrame ); + +protected: + IDirect3DVertexBuffer9 *m_pVertexBuffer; + VertexFormat_t m_VertexFormat; + int m_nVertexCount; + int m_nBufferSize; + int m_nFirstUnwrittenOffset; // Used only for dynamic buffers, indicates where it's safe to write (nooverwrite) + + // Is it locked? + bool m_bIsLocked : 1; + bool m_bIsDynamic : 1; + bool m_bFlush : 1; // Used only for dynamic buffers, indicates to discard the next time + +#ifdef VPROF_ENABLED + int m_nVProfFrame; + int *m_pFrameCounter; + int *m_pGlobalCounter; +#endif + +#ifdef _DEBUG + static int s_nBufferCount; +#endif +}; + + +//----------------------------------------------------------------------------- +// +// Code related to index buffers start here +// +//----------------------------------------------------------------------------- +class CIndexBufferDx8 : public CIndexBufferBase +{ + typedef CIndexBufferBase BaseClass; + + // Methods of IIndexBuffer +public: + virtual int IndexCount( ) const; + virtual MaterialIndexFormat_t IndexFormat() const; + virtual int GetRoomRemaining() const; + virtual bool Lock( int nIndexCount, bool bAppend, IndexDesc_t &desc ); + virtual void Unlock( int nIndexCount, IndexDesc_t &desc ); + virtual void BeginCastBuffer( MaterialIndexFormat_t format ); + virtual void EndCastBuffer( ); + virtual bool IsDynamic() const; + virtual void ModifyBegin( bool bReadOnly, int nFirstIndex, int nIndexCount, IndexDesc_t& desc ) { Assert(0); } + virtual void ModifyEnd( IndexDesc_t& desc ) { Assert(0); } + +public: + // constructor + CIndexBufferDx8( ShaderBufferType_t bufferType, MaterialIndexFormat_t fmt, int nIndexCount, const char *pBudgetGroupName ); + virtual ~CIndexBufferDx8(); + + // Allocates, deallocates the index buffer + bool Allocate( ); + void Free(); + + // Returns the index size + int IndexSize() const; + + // Only used by dynamic buffers, indicates the next lock should perform a discard. + void Flush(); + + // Returns the D3D buffer + IDirect3DIndexBuffer9* GetDx9Buffer(); + + // Used to measure how much static buffer memory is touched each frame + void HandlePerFrameTextureStats( int nFrame ); + +#ifdef CHECK_INDICES + unsigned short GetShadowIndex( int i ) const; +#endif + +private: + IDirect3DIndexBuffer9 *m_pIndexBuffer; + MaterialIndexFormat_t m_IndexFormat; + int m_nIndexCount; + int m_nBufferSize; + int m_nFirstUnwrittenOffset; // Used only for dynamic buffers, indicates where it's safe to write (nooverwrite) + + // Is it locked? + bool m_bIsLocked : 1; + bool m_bIsDynamic : 1; + bool m_bFlush : 1; // Used only for dynamic buffers, indicates to discard the next time + +#ifdef CHECK_INDICES + unsigned char *m_pShadowIndices; + void *m_pLockIndexBuffer; + int m_nLockIndexBufferSize; + int m_nLockIndexOffset; +#endif + +#ifdef VPROF_ENABLED + int m_nVProfFrame; +#endif + +#ifdef _DEBUG + static int s_nBufferCount; +#endif +}; + + +//----------------------------------------------------------------------------- +// +// Backward compat mesh code; will go away soon +// +//----------------------------------------------------------------------------- +abstract_class CBaseMeshDX8 : public CMeshBase +{ +public: + // constructor, destructor + CBaseMeshDX8(); + virtual ~CBaseMeshDX8(); + + // FIXME: Make this work! Unsupported methods of IIndexBuffer + IVertexBuffer + virtual bool Lock( int nMaxIndexCount, bool bAppend, IndexDesc_t& desc ) { Assert(0); return false; } + virtual void Unlock( int nWrittenIndexCount, IndexDesc_t& desc ) { Assert(0); } + virtual void ModifyBegin( bool bReadOnly, int nFirstIndex, int nIndexCount, IndexDesc_t& desc ) { Assert(0); } + virtual void ModifyEnd( IndexDesc_t& desc ) { Assert(0); } + virtual void Spew( int nIndexCount, const IndexDesc_t & desc ) { Assert(0); } + virtual void ValidateData( int nIndexCount, const IndexDesc_t &desc ) { Assert(0); } + virtual bool Lock( int nVertexCount, bool bAppend, VertexDesc_t &desc ) { Assert(0); return false; } + virtual void Unlock( int nVertexCount, VertexDesc_t &desc ) { Assert(0); } + virtual void Spew( int nVertexCount, const VertexDesc_t &desc ) { Assert(0); } + virtual void ValidateData( int nVertexCount, const VertexDesc_t & desc ) { Assert(0); } + + // Locks mesh for modifying + void ModifyBeginEx( bool bReadOnly, int nFirstVertex, int nVertexCount, int nFirstIndex, int nIndexCount, MeshDesc_t& desc ); + void ModifyBegin( int nFirstVertex, int nVertexCount, int nFirstIndex, int nIndexCount, MeshDesc_t& desc ); + void ModifyEnd( MeshDesc_t& desc ); + + // Sets/gets the vertex format + virtual void SetVertexFormat( VertexFormat_t format ); + virtual VertexFormat_t GetVertexFormat() const; + + // Sets/gets the morph format + virtual void SetMorphFormat( MorphFormat_t format ); + virtual MorphFormat_t GetMorphFormat() const; + // Am I using morph data? + virtual bool IsUsingMorphData() const; + bool IsUsingVertexID() const + { + return ShaderAPI()->GetBoundMaterial()->IsUsingVertexID(); + } + + // Sets the material + virtual void SetMaterial( IMaterial* pMaterial ); + + // returns the # of vertices (static meshes only) + int VertexCount() const { return 0; } + + void SetColorMesh( IMesh *pColorMesh, int nVertexOffsetInBytes ) + { + Assert( 0 ); + } + + void SetFlexMesh( IMesh *pMesh, int nVertexOffsetInBytes ) + { + Assert( pMesh == NULL && nVertexOffsetInBytes == 0 ); + } + + void DisableFlexMesh( ) + { + Assert( 0 ); + } + + void MarkAsDrawn() {} + + bool HasColorMesh( ) const { return false; } + bool HasFlexMesh( ) const { return false; } + + // Draws the mesh + void DrawMesh( ); + + // Begins a pass + void BeginPass( ); + + // Spews the mesh data + virtual void Spew( int nVertexCount, int nIndexCount, const MeshDesc_t & desc ); + + // Call this in debug mode to make sure our data is good. + virtual void ValidateData( int nVertexCount, int nIndexCount, const MeshDesc_t & desc ); + + virtual void HandleLateCreation( ) = 0; + + void Draw( CPrimList *pLists, int nLists ); + + // Copy verts and/or indices to a mesh builder. This only works for temp meshes! + virtual void CopyToMeshBuilder( + int iStartVert, // Which vertices to copy. + int nVerts, + int iStartIndex, // Which indices to copy. + int nIndices, + int indexOffset, // This is added to each index. + CMeshBuilder &builder ); + + // returns the primitive type + virtual MaterialPrimitiveType_t GetPrimitiveType() const = 0; + + // Returns the number of indices in a mesh.. + virtual int IndexCount( ) const = 0; + + // FIXME: Make this work! + virtual MaterialIndexFormat_t IndexFormat() const { return MATERIAL_INDEX_FORMAT_16BIT; } + + // NOTE: For dynamic index buffers only! + // Casts the memory of the dynamic index buffer to the appropriate type + virtual void BeginCastBuffer( MaterialIndexFormat_t format ) { Assert(0); } + virtual void BeginCastBuffer( VertexFormat_t format ) { Assert(0); } + virtual void EndCastBuffer( ) { Assert(0); } + virtual int GetRoomRemaining() const { Assert(0); return 0; } + + // returns a static vertex buffer... + virtual CVertexBuffer *GetVertexBuffer() { return 0; } + virtual CIndexBuffer *GetIndexBuffer() { return 0; } + + // Do I need to reset the vertex format? + virtual bool NeedsVertexFormatReset( VertexFormat_t fmt ) const; + + // Do I have enough room? + virtual bool HasEnoughRoom( int nVertexCount, int nIndexCount ) const; + + // Operation to do pre-lock + virtual void PreLock() {} + + virtual unsigned ComputeMemoryUsed(); + + bool m_bMeshLocked; + +protected: + bool DebugTrace() const; + + // The vertex format we're using... + VertexFormat_t m_VertexFormat; + + // The morph format we're using + MorphFormat_t m_MorphFormat; + +#ifdef DBGFLAG_ASSERT + IMaterialInternal* m_pMaterial; + bool m_IsDrawing; +#endif +}; + +//----------------------------------------------------------------------------- +// Implementation of the mesh +//----------------------------------------------------------------------------- +class CMeshDX8 : public CBaseMeshDX8 +{ +public: + // constructor + CMeshDX8( const char *pTextureGroupName ); + virtual ~CMeshDX8(); + + // Locks/unlocks the mesh + void LockMesh( int nVertexCount, int nIndexCount, MeshDesc_t& desc ); + void UnlockMesh( int nVertexCount, int nIndexCount, MeshDesc_t& desc ); + + // Locks mesh for modifying + void ModifyBeginEx( bool bReadOnly, int nFirstVertex, int nVertexCount, int nFirstIndex, int nIndexCount, MeshDesc_t& desc ); + void ModifyBegin( int nFirstVertex, int nVertexCount, int nFirstIndex, int nIndexCount, MeshDesc_t& desc ); + void ModifyEnd( MeshDesc_t& desc ); + + // returns the # of vertices (static meshes only) + int VertexCount() const; + + // returns the # of indices + virtual int IndexCount( ) const; + + // Sets up the vertex and index buffers + void UseIndexBuffer( CIndexBuffer* pBuffer ); + void UseVertexBuffer( CVertexBuffer* pBuffer ); + + // returns a static vertex buffer... + CVertexBuffer *GetVertexBuffer() { return m_pVertexBuffer; } + CIndexBuffer *GetIndexBuffer() { return m_pIndexBuffer; } + + void SetColorMesh( IMesh *pColorMesh, int nVertexOffsetInBytes ); + void SetFlexMesh( IMesh *pMesh, int nVertexOffsetInBytes ); + void DisableFlexMesh(); + + virtual void HandleLateCreation( ); + + bool HasColorMesh( ) const; + bool HasFlexMesh( ) const; + + // Draws the mesh + void Draw( int nFirstIndex, int nIndexCount ); + void Draw( CPrimList *pLists, int nLists ); + void DrawInternal( CPrimList *pLists, int nLists ); + + // Draws a single pass + void RenderPass(); + + // Sets the primitive type + void SetPrimitiveType( MaterialPrimitiveType_t type ); + MaterialPrimitiveType_t GetPrimitiveType() const; + + bool IsDynamic() const { return false; } + +protected: + // Sets the render state. + bool SetRenderState( int nVertexOffsetInBytes, int nFirstVertexIdx, VertexFormat_t vertexFormat = VERTEX_FORMAT_INVALID ); + + // Is the vertex format valid? + bool IsValidVertexFormat( VertexFormat_t vertexFormat = VERTEX_FORMAT_INVALID ); + + // Locks/ unlocks the vertex buffer + bool Lock( int nVertexCount, bool bAppend, VertexDesc_t &desc ); + void Unlock( int nVertexCount, VertexDesc_t &desc ); + + // Locks/unlocks the index buffer + // Pass in nFirstIndex=-1 to lock wherever the index buffer is. Pass in a value + // >= 0 to specify where to lock. + int Lock( bool bReadOnly, int nFirstIndex, int nIndexCount, IndexDesc_t &pIndices ); + void Unlock( int nIndexCount, IndexDesc_t &desc ); + + // computes how many primitives we've got + int NumPrimitives( int nVertexCount, int nIndexCount ) const; + + // Debugging output... + void SpewMaterialVerts( ); + + // Stream source setting methods + void SetVertexIDStreamState( ); + void SetColorStreamState( ); + void SetVertexStreamState( int nVertOffsetInBytes ); + void SetIndexStreamState( int firstVertexIdx ); + + void CheckIndices( CPrimList *pPrim, int numPrimitives ); + + // The vertex and index buffers + CVertexBuffer* m_pVertexBuffer; + CIndexBuffer* m_pIndexBuffer; + + // The current color mesh (to be bound to stream 1) + // The vertex offset allows use of a global, shared color mesh VB + CMeshDX8 * m_pColorMesh; + int m_nColorMeshVertOffsetInBytes; + + CVertexBuffer *m_pFlexVertexBuffer; + + bool m_bHasFlexVerts; + int m_nFlexVertOffsetInBytes; + int m_flexVertCount; + + // Primitive type + MaterialPrimitiveType_t m_Type; + + // Primitive mode + D3DPRIMITIVETYPE m_Mode; + + // Number of primitives + unsigned int m_NumIndices; + unsigned short m_NumVertices; + + // Is it locked? + bool m_IsVBLocked; + bool m_IsIBLocked; + + // Used in rendering sub-parts of the mesh + static CPrimList *s_pPrims; + static int s_nPrims; + static unsigned int s_FirstVertex; // Gets reset during CMeshDX8::DrawInternal + static unsigned int s_NumVertices; + int m_FirstIndex; + +#ifdef RECORDING + int m_LockVertexBufferSize; + void *m_LockVertexBuffer; +#endif + +#if defined( RECORDING ) || defined( CHECK_INDICES ) + void *m_LockIndexBuffer; + int m_LockIndexBufferSize; +#endif + const char *m_pTextureGroupName; + + friend class CMeshMgr; // MESHFIXME +}; + + +//----------------------------------------------------------------------------- +// A little extra stuff for the dynamic version +//----------------------------------------------------------------------------- +class CDynamicMeshDX8 : public CMeshDX8 +{ +public: + // constructor, destructor + CDynamicMeshDX8(); + virtual ~CDynamicMeshDX8(); + + // Initializes the dynamic mesh + void Init( int nBufferId ); + + // Sets the vertex format + virtual void SetVertexFormat( VertexFormat_t format ); + + // Resets the state in case of a task switch + void Reset(); + + // Do I have enough room in the buffer? + bool HasEnoughRoom( int nVertexCount, int nIndexCount ) const; + + // returns the # of indices + int IndexCount( ) const; + + // Locks the mesh + void LockMesh( int nVertexCount, int nIndexCount, MeshDesc_t& desc ); + + // Unlocks the mesh + void UnlockMesh( int nVertexCount, int nIndexCount, MeshDesc_t& desc ); + + // Override vertex + index buffer + void OverrideVertexBuffer( CVertexBuffer *pStaticVertexBuffer ); + void OverrideIndexBuffer( CIndexBuffer *pStaticIndexBuffer ); + + // Do I need to reset the vertex format? + bool NeedsVertexFormatReset(VertexFormat_t fmt) const; + + // Draws it + void Draw( int nFirstIndex, int nIndexCount ); + void MarkAsDrawn() { m_HasDrawn = true; } + // Simply draws what's been buffered up immediately, without state change + void DrawSinglePassImmediately(); + + // Operation to do pre-lock + void PreLock(); + + bool IsDynamic() const { return true; } + +private: + // Resets buffering state + void ResetVertexAndIndexCounts(); + + // Buffer Id + int m_nBufferId; + + // total queued vertices + int m_TotalVertices; + int m_TotalIndices; + + // the first vertex and index since the last draw + int m_nFirstVertex; + int m_FirstIndex; + + // Have we drawn since the last lock? + bool m_HasDrawn; + + // Any overrides? + bool m_VertexOverride; + bool m_IndexOverride; +}; + + +//----------------------------------------------------------------------------- +// A mesh that stores temporary vertex data in the correct format (for modification) +//----------------------------------------------------------------------------- +class CTempMeshDX8 : public CBaseMeshDX8 +{ +public: + // constructor, destructor + CTempMeshDX8( bool isDynamic ); + virtual ~CTempMeshDX8(); + + // Sets the material + virtual void SetVertexFormat( VertexFormat_t format ); + + // Locks/unlocks the mesh + void LockMesh( int nVertexCount, int nIndexCount, MeshDesc_t &desc ); + void UnlockMesh( int nVertexCount, int nIndexCount, MeshDesc_t &desc ); + + // Locks mesh for modifying + virtual void ModifyBeginEx( bool bReadOnly, int nFirstVertex, int nVertexCount, int nFirstIndex, int nIndexCount, MeshDesc_t& desc ); + virtual void ModifyBegin( int nFirstVertex, int nVertexCount, int nFirstIndex, int nIndexCount, MeshDesc_t& desc ); + virtual void ModifyEnd( MeshDesc_t& desc ); + + // Number of indices + vertices + int VertexCount() const; + virtual int IndexCount() const; + virtual bool IsDynamic() const; + + // Sets the primitive type + void SetPrimitiveType( MaterialPrimitiveType_t type ); + MaterialPrimitiveType_t GetPrimitiveType() const; + + // Begins a pass + void BeginPass( ); + + // Draws a single pass + void RenderPass(); + + virtual void HandleLateCreation() + { + Assert( !"TBD - CTempMeshDX8::HandleLateCreation()" ); + } + + // Draws the entire beast + void Draw( int nFirstIndex, int nIndexCount ); + + virtual void CopyToMeshBuilder( + int iStartVert, // Which vertices to copy. + int nVerts, + int iStartIndex, // Which indices to copy. + int nIndices, + int indexOffset, // This is added to each index. + CMeshBuilder &builder ); +private: + // Selection mode + void TestSelection( ); + void ClipTriangle( D3DXVECTOR3 **ppVert, float zNear, D3DXMATRIX &proj ); + + CDynamicMeshDX8 *GetDynamicMesh(); + + CUtlVector< unsigned char, CUtlMemoryAligned< unsigned char, 32 > > m_VertexData; + CUtlVector< unsigned short > m_IndexData; + + unsigned short m_VertexSize; + MaterialPrimitiveType_t m_Type; + int m_LockedVerts; + int m_LockedIndices; + bool m_IsDynamic; + + // Used in rendering sub-parts of the mesh + static unsigned int s_NumIndices; + static unsigned int s_FirstIndex; + +#ifdef DBGFLAG_ASSERT + bool m_Locked; + bool m_InPass; +#endif +}; + +#if 0 +//----------------------------------------------------------------------------- +// A mesh that stores temporary vertex data in the correct format (for modification) +//----------------------------------------------------------------------------- +class CTempIndexBufferDX8 : public CIndexBufferBase +{ +public: + // constructor, destructor + CTempIndexBufferDX8( bool isDynamic ); + virtual ~CTempIndexBufferDX8(); + + // Locks/unlocks the mesh + void LockIndexBuffer( int nIndexCount ); + void UnlockMesh( int nIndexCount ); + + // Locks mesh for modifying + virtual void ModifyBeginEx( bool bReadOnly, int nFirstIndex, int nIndexCount ); + virtual void ModifyEnd(); + + // Number of indices + virtual int IndexCount() const; + virtual bool IsDynamic() const; + + virtual void CopyToIndexBuilder( + int iStartIndex, // Which indices to copy. + int nIndices, + int indexOffset, // This is added to each index. + CIndexBuilder &builder ); +private: + // Selection mode + void TestSelection( ); + + CDynamicMeshDX8 *GetDynamicMesh(); + + CUtlVector< unsigned short > m_IndexData; + + MaterialPrimitiveType_t m_Type; + int m_LockedIndices; + bool m_IsDynamic; + + // Used in rendering sub-parts of the mesh + static unsigned int s_NumIndices; + static unsigned int s_FirstIndex; + +#ifdef DBGFLAG_ASSERT + bool m_Locked; + bool m_InPass; +#endif +}; +#endif +//----------------------------------------------------------------------------- +// This is a version that buffers up vertex data so we can blast through it later +//----------------------------------------------------------------------------- +class CBufferedMeshDX8 : public CBaseMeshDX8 +{ +public: + // constructor, destructor + CBufferedMeshDX8(); + virtual ~CBufferedMeshDX8(); + + // checks to see if it was rendered.. + void ResetRendered(); + bool WasNotRendered() const; + + // Sets the mesh we're really going to draw into + void SetMesh( CBaseMeshDX8* pMesh ); + const CBaseMeshDX8* GetMesh() const { return m_pMesh; } + + // Spews the mesh data + virtual void Spew( int nVertexCount, int nIndexCount, const MeshDesc_t &spewDesc ); + + // Sets the vertex format + virtual void SetVertexFormat( VertexFormat_t format ); + virtual VertexFormat_t GetVertexFormat() const; + + // Sets the morph format + virtual void SetMorphFormat( MorphFormat_t format ); + + // Sets the material + void SetMaterial( IMaterial *pMaterial ); + + // returns the number of indices (should never be called!) + virtual int IndexCount() const { Assert(0); return 0; } + virtual MaterialIndexFormat_t IndexFormat() const { Assert(0); return MATERIAL_INDEX_FORMAT_16BIT; } + virtual bool IsDynamic() const { Assert(0); return true; } + virtual void BeginCastBuffer( MaterialIndexFormat_t format ) { Assert(0); } + virtual void EndCastBuffer( ) { Assert(0); } + virtual int GetRoomRemaining() const { Assert(0); return 0; } + + // Locks the mesh + void LockMesh( int nVertexCount, int nIndexCount, MeshDesc_t& desc ); + void UnlockMesh( int nVertexCount, int nIndexCount, MeshDesc_t& desc ); + + // Sets the primitive type + void SetPrimitiveType( MaterialPrimitiveType_t type ); + MaterialPrimitiveType_t GetPrimitiveType( ) const; + + void ValidateData( int nVertexCount, int nIndexCount, const MeshDesc_t & spewDesc ); + + virtual void HandleLateCreation( ) + { + if ( m_pMesh ) + { + m_pMesh->HandleLateCreation(); + } + } + + // Draws it + void Draw( int nFirstIndex, int nIndexCount ); + + // Renders a pass + void RenderPass(); + + // Flushes queued data + void Flush( ); + + void SetFlexMesh( IMesh *pMesh, int nVertexOffsetInBytes ); + +private: + // The actual mesh we need to render.... + CBaseMeshDX8* m_pMesh; + + // The index of the last vertex (for tristrip fixup) + unsigned short m_LastIndex; + + // Extra padding indices for tristrips + unsigned short m_ExtraIndices; + + // Am I currently flushing? + bool m_IsFlushing; + + // has the dynamic mesh been rendered? + bool m_WasRendered; + + // Do I need to flush? + bool m_FlushNeeded; + +#ifdef DEBUG_BUFFERED_MESHES + // for debugging only + bool m_BufferedStateSet; + BufferedState_t m_BufferedState; +#endif +}; + + +//----------------------------------------------------------------------------- +// Implementation of the mesh manager +//----------------------------------------------------------------------------- +class CMeshMgr : public IMeshMgr +{ +public: + // constructor, destructor + CMeshMgr(); + virtual ~CMeshMgr(); + + // Initialize, shutdown + void Init(); + void Shutdown(); + + // Task switch... + void ReleaseBuffers(); + void RestoreBuffers(); + + // Releases all dynamic vertex buffers + void DestroyVertexBuffers(); + + // Flushes the dynamic mesh + void Flush(); + + // Flushes the vertex buffers + void DiscardVertexBuffers(); + + // Creates, destroys static meshes + IMesh *CreateStaticMesh( VertexFormat_t vertexFormat, const char *pTextureBudgetGroup, IMaterial *pMaterial = NULL ); + void DestroyStaticMesh( IMesh *pMesh ); + + // Gets at the dynamic mesh (spoofs it though) + IMesh *GetDynamicMesh( IMaterial *pMaterial, VertexFormat_t vertexFormat, int nHWSkinBoneCount, bool buffered, + IMesh *pVertexOverride, IMesh *pIndexOverride ); + +// ----------------------------------------------------------- +// ------------ New Vertex/Index Buffer interface ---------------------------- + // Do we need support for bForceTempMesh and bSoftwareVertexShader? + // I don't think we use bSoftwareVertexShader anymore. .need to look into bForceTempMesh. + IVertexBuffer *CreateVertexBuffer( ShaderBufferType_t type, VertexFormat_t fmt, int nVertexCount, const char *pBudgetGroup ); + IIndexBuffer *CreateIndexBuffer( ShaderBufferType_t bufferType, MaterialIndexFormat_t fmt, int nIndexCount, const char *pBudgetGroup ); + void DestroyVertexBuffer( IVertexBuffer * ); + void DestroyIndexBuffer( IIndexBuffer * ); + // Do we need to specify the stream here in the case of locking multiple dynamic VBs on different streams? + IVertexBuffer *GetDynamicVertexBuffer( int streamID, VertexFormat_t vertexFormat, bool bBuffered = true ); + IIndexBuffer *GetDynamicIndexBuffer( MaterialIndexFormat_t fmt, bool bBuffered = true ); + void BindVertexBuffer( int streamID, IVertexBuffer *pVertexBuffer, int nOffsetInBytes, int nFirstVertex, int nVertexCount, VertexFormat_t fmt, int nRepetitions = 1 ); + void BindIndexBuffer( IIndexBuffer *pIndexBuffer, int nOffsetInBytes ); + void Draw( MaterialPrimitiveType_t primitiveType, int nFirstIndex, int nIndexCount ); +// ------------ End ---------------------------- + void RenderPassWithVertexAndIndexBuffers( void ); + + VertexFormat_t GetCurrentVertexFormat( void ) const { return m_CurrentVertexFormat; } + + // Gets at the *actual* dynamic mesh + IMesh* GetActualDynamicMesh( VertexFormat_t vertexFormat ); + IMesh *GetFlexMesh(); + + // Computes vertex format from a list of ingredients + VertexFormat_t ComputeVertexFormat( unsigned int flags, + int numTexCoords, int *pTexCoordDimensions, int numBoneWeights, + int userDataSize ) const; + + // Use fat vertices (for tools) + virtual void UseFatVertices( bool bUseFat ); + + // Returns the number of vertices we can render using the dynamic mesh + virtual void GetMaxToRender( IMesh *pMesh, bool bMaxUntilFlush, int *pMaxVerts, int *pMaxIndices ); + virtual int GetMaxVerticesToRender( IMaterial *pMaterial ); + virtual int GetMaxIndicesToRender( ); + + // Returns a vertex buffer appropriate for the flags + CVertexBuffer *FindOrCreateVertexBuffer( int nDynamicBufferId, VertexFormat_t fmt ); + CIndexBuffer *GetDynamicIndexBuffer(); + + // Is the mesh dynamic? + bool IsDynamicMesh( IMesh *pMesh ) const; + bool IsBufferedDynamicMesh( IMesh *pMesh ) const; + + // Is the vertex buffer dynamic? + bool IsDynamicVertexBuffer( IVertexBuffer *pVertexBuffer ) const; + + // Is the index buffer dynamic? + bool IsDynamicIndexBuffer( IIndexBuffer *pIndexBuffer ) const; + + // Returns the vertex size + int VertexFormatSize( VertexFormat_t vertexFormat ) const + { + return CVertexBufferBase::VertexFormatSize( vertexFormat ); + } + + // Computes the vertex buffer pointers + void ComputeVertexDescription( unsigned char *pBuffer, + VertexFormat_t vertexFormat, MeshDesc_t &desc ) const; + + // Returns the number of buffers... + int BufferCount() const + { +#ifdef _DEBUG + return CVertexBuffer::BufferCount() + CIndexBuffer::BufferCount(); +#else + return 0; +#endif + } + + CVertexBuffer *GetVertexIDBuffer(); + + IVertexBuffer *GetDynamicVertexBuffer( IMaterial *pMaterial, bool buffered = true ); + IIndexBuffer *GetDynamicIndexBuffer( IMaterial *pMaterial, bool buffered = true ); + virtual void MarkUnusedVertexFields( unsigned int nFlags, int nTexCoordCount, bool *pUnusedTexCoords ); + + int UnusedVertexFields() const { return m_nUnusedVertexFields; } + int UnusedTextureCoords() const { return m_nUnusedTextureCoords; } + + IDirect3DVertexBuffer9 *GetZeroVertexBuffer() const { return m_pZeroVertexBuffer; } + +private: + void SetVertexIDStreamState( ); + void SetColorStreamState( ); + void SetVertexStreamState( int nVertOffsetInBytes, int nVertexStride ); + void SetIndexStreamState( int firstVertexIdx ); + bool SetRenderState( int nVertexOffsetInBytes, int nFirstVertexIdx, VertexFormat_t vertexFormat, int nVertexStride ); + + struct VertexBufferLookup_t + { + CVertexBuffer* m_pBuffer; + int m_VertexSize; + }; + + void CopyStaticMeshIndexBufferToTempMeshIndexBuffer( CTempMeshDX8 *pDstIndexMesh, CMeshDX8 *pSrcIndexMesh ); + + // Cleans up the class + void CleanUp(); + + // Creates, destroys the dynamic index + void CreateDynamicIndexBuffer(); + void DestroyDynamicIndexBuffer(); + + // Creates, destroys the vertexID buffer + void CreateVertexIDBuffer(); + void DestroyVertexIDBuffer(); + void CreateZeroVertexBuffer(); + void DestroyZeroVertexBuffer(); + + // Fills a vertexID buffer + void FillVertexIDBuffer( CVertexBuffer *pVertexIDBuffer, int nCount ); + + // The dynamic index buffer + CIndexBuffer *m_pDynamicIndexBuffer; + + // A static vertexID buffer + CVertexBuffer *m_pVertexIDBuffer; + + // The dynamic vertex buffers + CUtlVector< VertexBufferLookup_t > m_DynamicVertexBuffers; + + // The buffered mesh + CBufferedMeshDX8 m_BufferedMesh; + + // The current dynamic mesh + CDynamicMeshDX8 m_DynamicMesh; + CDynamicMeshDX8 m_DynamicFlexMesh; + + // The current dynamic vertex buffer + CVertexBufferDx8 m_DynamicVertexBuffer; + + // The current dynamic index buffer + CIndexBufferDx8 m_DynamicIndexBuffer; + + // The dynamic mesh temp version (for shaders that modify vertex data) + CTempMeshDX8 m_DynamicTempMesh; + + // Am I buffering or not? + bool m_BufferedMode; + + // Using fat vertices? + bool m_bUseFatVertices; + + CVertexBufferDx8 *m_pCurrentVertexBuffer; + VertexFormat_t m_CurrentVertexFormat; + int m_pVertexBufferOffset[MAX_DX8_STREAMS]; + int m_pCurrentVertexStride[MAX_DX8_STREAMS]; + int m_pFirstVertex[MAX_DX8_STREAMS]; + int m_pVertexCount[MAX_DX8_STREAMS]; + CIndexBufferBase *m_pCurrentIndexBuffer; + int m_nIndexBufferOffset; + MaterialPrimitiveType_t m_PrimitiveType; + int m_nFirstIndex; + int m_nNumIndices; + + unsigned int m_nUnusedVertexFields; + unsigned int m_nUnusedTextureCoords; + + // 4096 byte static VB containing all-zeros + IDirect3DVertexBuffer9 *m_pZeroVertexBuffer; +}; + +//----------------------------------------------------------------------------- +// Singleton... +//----------------------------------------------------------------------------- +static CMeshMgr g_MeshMgr; +IMeshMgr* MeshMgr() +{ + return &g_MeshMgr; +} + +//----------------------------------------------------------------------------- +// Tracks stream state and queued data +//----------------------------------------------------------------------------- +static CIndexBuffer *g_pLastIndex = NULL; +static IDirect3DIndexBuffer9 *g_pLastIndexBuffer = NULL; +static CVertexBuffer *g_pLastVertex = NULL; +static IDirect3DVertexBuffer9 *g_pLastVertexBuffer = NULL; +static int g_nLastVertOffsetInBytes = 0; +static int g_nLastVertStride = 0; +static int g_LastVertexIdx = -1; +static CMeshDX8 *g_pLastColorMesh = NULL; +static int g_nLastColorMeshVertOffsetInBytes = 0; +static bool g_bUsingVertexID = false; +static bool g_bFlexMeshStreamSet = false; +static VertexFormat_t g_LastVertexFormat = 0; + +inline void D3DSetStreamSource( unsigned int streamNumber, IDirect3DVertexBuffer9 *pStreamData, + unsigned int nVertexOffsetInBytes, unsigned int stride ) +{ + Dx9Device()->SetStreamSource( streamNumber, pStreamData, nVertexOffsetInBytes, stride ); +} + + +//----------------------------------------------------------------------------- +// Tracks stream state and queued data +//----------------------------------------------------------------------------- +void Unbind( IDirect3DIndexBuffer9 *pIndexBuffer ) +{ +#ifdef _X360 + IDirect3DIndexBuffer9 *pBoundBuffer; + Dx9Device()->GetIndices( &pBoundBuffer ); + if ( pBoundBuffer == pIndexBuffer ) + { + // xboxissue - cannot lock indexes set in a d3d device, clear possibly set indices + Dx9Device()->SetIndices( NULL ); + g_pLastIndex = NULL; + g_pLastIndexBuffer = NULL; + } + + if ( pBoundBuffer ) + { + pBoundBuffer->Release(); + } +#endif +} + +void Unbind( IDirect3DVertexBuffer9 *pVertexBuffer ) +{ +#ifdef _X360 + UINT nOffset, nStride; + IDirect3DVertexBuffer9 *pBoundBuffer; + for ( int i = 0; i < MAX_DX8_STREAMS; ++i ) + { + Dx9Device()->GetStreamSource( i, &pBoundBuffer, &nOffset, &nStride ); + if ( pBoundBuffer == pVertexBuffer ) + { + // xboxissue - cannot lock indexes set in a d3d device, clear possibly set indices + Dx9Device()->SetStreamSource( i, 0, 0, 0 ); + switch ( i ) + { + case 0: + g_pLastVertex = NULL; + g_pLastVertexBuffer = NULL; + break; + + case 1: + g_pLastColorMesh = NULL; + g_nLastColorMeshVertOffsetInBytes = 0; + break; + } + } + + if ( pBoundBuffer ) + { + pBoundBuffer->Release(); + } + } +#endif +} + + +//----------------------------------------------------------------------------- +// Helpers to count texture coordinates +//----------------------------------------------------------------------------- +static int NumTextureCoordinates( VertexFormat_t vertexFormat ) +{ + int nTexCoordCount = 0; + for ( int i = 0; i < VERTEX_MAX_TEXTURE_COORDINATES; ++i ) + { + if ( TexCoordSize( i, vertexFormat ) == 0 ) + continue; + ++nTexCoordCount; + } + return nTexCoordCount; +} + + +//----------------------------------------------------------------------------- +// Makes sure that the render state is always set next time +//----------------------------------------------------------------------------- +static void ResetMeshRenderState() +{ + SafeRelease( &g_pLastIndex ); + g_pLastIndexBuffer = 0; + g_pLastVertex = 0; + g_nLastVertOffsetInBytes = 0; + g_pLastColorMesh = 0; + g_nLastColorMeshVertOffsetInBytes = 0; + g_LastVertexIdx = -1; + g_bUsingVertexID = false; + g_bFlexMeshStreamSet = false; + g_LastVertexFormat = 0; +} + +//----------------------------------------------------------------------------- +// Makes sure that the render state is always set next time +//----------------------------------------------------------------------------- +static void ResetIndexBufferRenderState() +{ + SafeRelease( &g_pLastIndex ); + g_pLastIndexBuffer = 0; + g_LastVertexIdx = -1; +} + + +//----------------------------------------------------------------------------- +// +// Index Buffer implementations begin here +// +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +// Globals +//----------------------------------------------------------------------------- +#ifdef _DEBUG +int CIndexBufferDx8::s_nBufferCount = 0; +#endif + + +//----------------------------------------------------------------------------- +// Constructor, destructor +//----------------------------------------------------------------------------- +CIndexBufferDx8::CIndexBufferDx8( ShaderBufferType_t bufferType, MaterialIndexFormat_t fmt, int nIndexCount, const char *pBudgetGroupName ) : + BaseClass( pBudgetGroupName ) +{ +// Debugger(); + + Assert( nIndexCount != 0 ); + + // NOTE: MATERIAL_INDEX_FORMAT_UNKNOWN can't be dealt with under dx9 + // because format is bound at buffer creation time. What we'll do + // is just arbitrarily choose to use a 16-bit index buffer of the same size + if ( fmt == MATERIAL_INDEX_FORMAT_UNKNOWN ) + { + fmt = MATERIAL_INDEX_FORMAT_16BIT; + nIndexCount /= 2; + } + + m_pIndexBuffer = NULL; + m_IndexFormat = fmt; + m_nBufferSize = nIndexCount * IndexSize(); + m_nIndexCount = nIndexCount; + m_nFirstUnwrittenOffset = 0; + m_bIsLocked = false; + m_bIsDynamic = IsDynamicBufferType( bufferType ); + m_bFlush = false; + +#ifdef CHECK_INDICES + m_pShadowIndices = NULL; +#endif + +#ifdef VPROF_ENABLED + m_nVProfFrame = -1; +#endif +} + +CIndexBufferDx8::~CIndexBufferDx8() +{ + Free(); +} + + +//----------------------------------------------------------------------------- +// Returns the index size +//----------------------------------------------------------------------------- +inline int CIndexBufferDx8::IndexSize() const +{ + Assert( m_IndexFormat != MATERIAL_INDEX_FORMAT_UNKNOWN ); + return ( m_IndexFormat == MATERIAL_INDEX_FORMAT_16BIT ) ? 2 : 4; +} + + +//----------------------------------------------------------------------------- +// Creates, destroys the index buffer +//----------------------------------------------------------------------------- +bool CIndexBufferDx8::Allocate() +{ +#ifdef OSX + Debugger(); +#endif + Assert( !m_pIndexBuffer ); + m_nFirstUnwrittenOffset = 0; + + // FIXME: This doesn't really work for dynamic buffers; dynamic buffers + // can't have mixed-type indices in them. Bleah. + D3DFORMAT format = ( m_IndexFormat == MATERIAL_INDEX_FORMAT_32BIT ) ? + D3DFMT_INDEX32 : D3DFMT_INDEX16; + + DWORD usage = D3DUSAGE_WRITEONLY; + if ( m_bIsDynamic ) + { + usage |= D3DUSAGE_DYNAMIC; + } + + HRESULT hr = Dx9Device()->CreateIndexBuffer( + m_nBufferSize, usage, format, D3DPOOL_DEFAULT, &m_pIndexBuffer, NULL ); + +#if !defined( _X360 ) + if ( ( hr == D3DERR_OUTOFVIDEOMEMORY ) || ( hr == E_OUTOFMEMORY ) ) + { + // Don't have the memory for this. Try flushing all managed resources + // out of vid mem and try again. + // FIXME: need to record this + Dx9Device()->EvictManagedResources(); + hr = Dx9Device()->CreateIndexBuffer( + m_nBufferSize, usage, format, D3DPOOL_DEFAULT, &m_pIndexBuffer, NULL ); + } +#endif // !X360 + + if ( FAILED(hr) || ( m_pIndexBuffer == NULL ) ) + { + Warning( "CIndexBufferDx8::Allocate: CreateIndexBuffer failed!\n" ); + return false; + } + + if ( !m_bIsDynamic ) + { + VPROF_INCREMENT_GROUP_COUNTER( "TexGroup_global_" TEXTURE_GROUP_STATIC_INDEX_BUFFER, + COUNTER_GROUP_TEXTURE_GLOBAL, m_nBufferSize ); + } + else + { + VPROF_INCREMENT_GROUP_COUNTER( "TexGroup_global_" TEXTURE_GROUP_DYNAMIC_INDEX_BUFFER, + COUNTER_GROUP_TEXTURE_GLOBAL, m_nBufferSize ); + } + +#ifdef CHECK_INDICES + Assert ( !m_pShadowIndices ); + m_pShadowIndices = new unsigned char[ m_nBufferSize ]; + memset( m_pShadowIndices, 0xFF, m_nBufferSize ); +#endif // CHECK_INDICES + +#ifdef _DEBUG + ++s_nBufferCount; +#endif + + return true; +} + +void CIndexBufferDx8::Free() +{ +// FIXME: Unlock(0); + if ( m_pIndexBuffer ) + { +#ifdef _DEBUG + --s_nBufferCount; +#endif + + m_pIndexBuffer->Release(); + m_pIndexBuffer = NULL; + + if ( !m_bIsDynamic ) + { + VPROF_INCREMENT_GROUP_COUNTER( "TexGroup_global_" TEXTURE_GROUP_STATIC_INDEX_BUFFER, + COUNTER_GROUP_TEXTURE_GLOBAL, - m_nBufferSize ); + } + else + { + VPROF_INCREMENT_GROUP_COUNTER( "TexGroup_global_" TEXTURE_GROUP_DYNAMIC_INDEX_BUFFER, + COUNTER_GROUP_TEXTURE_GLOBAL, - m_nBufferSize ); + } + } + +#ifdef CHECK_INDICES + if ( m_pShadowIndices ) + { + delete[] m_pShadowIndices; + m_pShadowIndices = NULL; + } +#endif // CHECK_INDICES +} + + +//----------------------------------------------------------------------------- +// Index buffer information +//----------------------------------------------------------------------------- +int CIndexBufferDx8::IndexCount( ) const +{ + Assert( !m_bIsDynamic ); + return m_nIndexCount; +} + +MaterialIndexFormat_t CIndexBufferDx8::IndexFormat() const +{ + Assert( !m_bIsDynamic ); + return m_IndexFormat; +} + + +//----------------------------------------------------------------------------- +// Returns true if the buffer is dynamic +//----------------------------------------------------------------------------- +bool CIndexBufferDx8::IsDynamic() const +{ + return m_bIsDynamic; +} + + +//----------------------------------------------------------------------------- +// Only used by dynamic buffers, indicates the next lock should perform a discard. +//----------------------------------------------------------------------------- +void CIndexBufferDx8::Flush() +{ + // This strange-looking line makes a flush only occur if the buffer is dynamic. + m_bFlush = m_bIsDynamic; +} + + +//----------------------------------------------------------------------------- +// Returns the D3D buffer +//----------------------------------------------------------------------------- +IDirect3DIndexBuffer9* CIndexBufferDx8::GetDx9Buffer() +{ + return m_pIndexBuffer; +} + + +//----------------------------------------------------------------------------- +// Returns a shadowed index, for validation +//----------------------------------------------------------------------------- +#ifdef CHECK_INDICES +unsigned short CIndexBufferDx8::GetShadowIndex( int i ) const +{ + Assert( i >= 0 && i < m_nIndexCount ); + Assert( m_IndexFormat == MATERIAL_INDEX_FORMAT_16BIT ); + return *(unsigned short*)( &m_pShadowIndices[ i * IndexSize() ] ); +} +#endif // CHECK_INDICES + + +//----------------------------------------------------------------------------- +// Used to measure how much static buffer memory is touched each frame +//----------------------------------------------------------------------------- +void CIndexBufferDx8::HandlePerFrameTextureStats( int nFrame ) +{ +#ifdef VPROF_ENABLED + if ( m_nVProfFrame != nFrame && !m_bIsDynamic ) + { + m_nVProfFrame = nFrame; + VPROF_INCREMENT_GROUP_COUNTER( "TexGroup_frame_" TEXTURE_GROUP_STATIC_INDEX_BUFFER, + COUNTER_GROUP_TEXTURE_PER_FRAME, m_nBufferSize ); + } +#endif +} + + +//----------------------------------------------------------------------------- +// Casts a dynamic buffer to be a particular index type +//----------------------------------------------------------------------------- +void CIndexBufferDx8::BeginCastBuffer( MaterialIndexFormat_t format ) +{ + // NOTE: This should have no effect under Dx9, since we can't recast index buffers. + Assert( format != MATERIAL_INDEX_FORMAT_UNKNOWN ); + Assert( m_bIsDynamic && ( m_IndexFormat == format ) ); +} + +void CIndexBufferDx8::EndCastBuffer( ) +{ + // NOTE: This should have no effect under Dx9, since we can't recast index buffers. +} + +int CIndexBufferDx8::GetRoomRemaining() const +{ + return ( m_nBufferSize - m_nFirstUnwrittenOffset ) / IndexSize(); +} + + +//----------------------------------------------------------------------------- +// Locks/unlocks the index buffer +//----------------------------------------------------------------------------- +bool CIndexBufferDx8::Lock( int nMaxIndexCount, bool bAppend, IndexDesc_t &desc ) +{ + Assert( !m_bIsLocked && ( nMaxIndexCount != 0 ) && ( nMaxIndexCount <= m_nIndexCount ) ); + Assert( m_IndexFormat != MATERIAL_INDEX_FORMAT_UNKNOWN ); + + // FIXME: Why do we need to sync matrices now? + ShaderUtil()->SyncMatrices(); + g_ShaderMutex.Lock(); + + VPROF( "CIndexBufferX8::Lock" ); + + void *pLockedData = NULL; + HRESULT hr; + int nMemoryRequired; + bool bHasEnoughMemory; + UINT nLockFlags; + + // This can happen if the buffer was locked but a type wasn't bound + if ( m_IndexFormat == MATERIAL_INDEX_FORMAT_UNKNOWN ) + goto indexBufferLockFailed; + + // Just give the app crap buffers to fill up while we're suppressed... + if ( g_pShaderDeviceDx8->IsDeactivated() || ( nMaxIndexCount == 0 ) ) + goto indexBufferLockFailed; + + // Did we ask for something too large? + if ( nMaxIndexCount > m_nIndexCount ) + { + Warning( "Too many indices for index buffer. . tell a programmer (%d>%d)\n", nMaxIndexCount, m_nIndexCount ); + goto indexBufferLockFailed; + } + + // We might not have a buffer owing to alt-tab type stuff + if ( !m_pIndexBuffer ) + { + if ( !Allocate() ) + goto indexBufferLockFailed; + } + + // Check to see if we have enough memory + nMemoryRequired = nMaxIndexCount * IndexSize(); + bHasEnoughMemory = ( m_nFirstUnwrittenOffset + nMemoryRequired <= m_nBufferSize ); + + nLockFlags = D3DLOCK_NOSYSLOCK; + if ( bAppend ) + { + // Can't have the first lock after a flush be an appending lock + Assert( !m_bFlush ); + + // If we're appending and we don't have enough room, then puke! + if ( !bHasEnoughMemory || m_bFlush ) + goto indexBufferLockFailed; + nLockFlags |= ( m_nFirstUnwrittenOffset == 0 ) ? D3DLOCK_DISCARD : D3DLOCK_NOOVERWRITE; + } + else + { + // If we're not appending, no overwrite unless we don't have enough room + // If we're a static buffer, always discard if we're not appending + if ( !m_bFlush && bHasEnoughMemory && m_bIsDynamic ) + { + nLockFlags |= ( m_nFirstUnwrittenOffset == 0 ) ? D3DLOCK_DISCARD : D3DLOCK_NOOVERWRITE; + } + else + { + if ( m_bIsDynamic ) + { + nLockFlags |= D3DLOCK_DISCARD; + } + m_nFirstUnwrittenOffset = 0; + m_bFlush = false; + } + } + +#if !defined( _X360 ) + hr = m_pIndexBuffer->Lock( m_nFirstUnwrittenOffset, nMemoryRequired, &pLockedData, nLockFlags ); +#else + hr = m_pIndexBuffer->Lock( 0, 0, &pLockedData, nLockFlags ); + pLockedData = ( ( unsigned char * )pLockedData + m_nFirstUnwrittenOffset ); +#endif + + if ( FAILED( hr ) ) + { + Warning( "Failed to lock index buffer in CIndexBufferDx8::LockIndexBuffer\n" ); + goto indexBufferLockFailed; + } + + desc.m_pIndices = (unsigned short*)( pLockedData ); + desc.m_nIndexSize = IndexSize() >> 1; + if ( g_pHardwareConfig->SupportsStreamOffset() ) + { + desc.m_nFirstIndex = 0; + desc.m_nOffset = m_nFirstUnwrittenOffset; + } + else + { + desc.m_nFirstIndex = m_nFirstUnwrittenOffset / IndexSize(); + Assert( (int)( desc.m_nFirstIndex * IndexSize() ) == m_nFirstUnwrittenOffset ); + desc.m_nOffset = 0; + } + m_bIsLocked = true; + +#ifdef CHECK_INDICES + m_nLockIndexBufferSize = nMemoryRequired; + m_pLockIndexBuffer = desc.m_pIndices; + m_nLockIndexOffset = m_nFirstUnwrittenOffset; +#endif // CHECK_INDICES + + return true; + +indexBufferLockFailed: + g_ShaderMutex.Unlock(); + + // Set up a bogus index descriptor + desc.m_pIndices = g_nScratchIndexBuffer; + desc.m_nIndexSize = 0; + desc.m_nFirstIndex = 0; + desc.m_nOffset = 0; + return false; +} + +void CIndexBufferDx8::Unlock( int nWrittenIndexCount, IndexDesc_t &desc ) +{ + Assert( nWrittenIndexCount <= m_nIndexCount ); + + // NOTE: This can happen if another application finishes + // initializing during the construction of a mesh + if ( !m_bIsLocked ) + return; + +#ifdef CHECK_INDICES + memcpy( (unsigned char*)m_pShadowIndices + m_nLockIndexOffset, m_pLockIndexBuffer, nWrittenIndexCount * IndexSize() ); +#endif // CHECK_INDICES + + if ( m_pIndexBuffer ) + { + m_pIndexBuffer->Unlock(); + } + + m_nFirstUnwrittenOffset += nWrittenIndexCount * IndexSize(); + m_bIsLocked = false; + g_ShaderMutex.Unlock(); +} + + +//----------------------------------------------------------------------------- +// +// Vertex Buffer implementations begin here +// +//----------------------------------------------------------------------------- + + +//----------------------------------------------------------------------------- +// globals +//----------------------------------------------------------------------------- +#ifdef _DEBUG +int CVertexBufferDx8::s_nBufferCount = 0; +#endif + + +//----------------------------------------------------------------------------- +// constructor +//----------------------------------------------------------------------------- +CVertexBufferDx8::CVertexBufferDx8( ShaderBufferType_t type, VertexFormat_t fmt, int nVertexCount, const char *pBudgetGroupName ) : + BaseClass( pBudgetGroupName ) +{ +// Debugger(); + Assert( nVertexCount != 0 ); + + m_pVertexBuffer = NULL; + m_VertexFormat = fmt; + m_nVertexCount = ( fmt == VERTEX_FORMAT_UNKNOWN ) ? 0 : nVertexCount; + m_nBufferSize = ( fmt == VERTEX_FORMAT_UNKNOWN ) ? nVertexCount : nVertexCount * VertexSize(); + m_nFirstUnwrittenOffset = 0; + m_bIsLocked = false; + m_bIsDynamic = ( type == SHADER_BUFFER_TYPE_DYNAMIC ) || ( type == SHADER_BUFFER_TYPE_DYNAMIC_TEMP ); + m_bFlush = false; + +#ifdef VPROF_ENABLED + if ( !m_bIsDynamic ) + { + char name[256]; + V_strcpy_safe( name, "TexGroup_global_" ); + V_strcat_safe( name, pBudgetGroupName, sizeof(name) ); + m_pGlobalCounter = g_VProfCurrentProfile.FindOrCreateCounter( name, COUNTER_GROUP_TEXTURE_GLOBAL ); + + V_strcpy_safe( name, "TexGroup_frame_" ); + V_strcat_safe( name, pBudgetGroupName, sizeof(name) ); + m_pFrameCounter = g_VProfCurrentProfile.FindOrCreateCounter( name, COUNTER_GROUP_TEXTURE_PER_FRAME ); + } + else + { + m_pGlobalCounter = g_VProfCurrentProfile.FindOrCreateCounter( "TexGroup_global_" TEXTURE_GROUP_DYNAMIC_VERTEX_BUFFER, COUNTER_GROUP_TEXTURE_GLOBAL ); + m_pFrameCounter = NULL; + } + m_nVProfFrame = -1; +#endif +} + +CVertexBufferDx8::~CVertexBufferDx8() +{ + Free(); +} + + +//----------------------------------------------------------------------------- +// Returns the vertex size +//----------------------------------------------------------------------------- +inline int CVertexBufferDx8::VertexSize() const +{ + Assert( m_VertexFormat != VERTEX_FORMAT_UNKNOWN ); + return VertexFormatSize( m_VertexFormat ); +} + +//----------------------------------------------------------------------------- +// Creates, destroys the vertex buffer +//----------------------------------------------------------------------------- +bool CVertexBufferDx8::Allocate() +{ + Assert( !m_pVertexBuffer ); + m_nFirstUnwrittenOffset = 0; + + D3DPOOL pool = D3DPOOL_MANAGED; + +#if defined(IS_WINDOWS_PC) && defined(SHADERAPIDX9) + extern bool g_ShaderDeviceUsingD3D9Ex; + if ( g_ShaderDeviceUsingD3D9Ex ) + { + pool = D3DPOOL_DEFAULT; + } +#endif + + DWORD usage = D3DUSAGE_WRITEONLY; + if ( m_bIsDynamic ) + { + usage |= D3DUSAGE_DYNAMIC; + pool = D3DPOOL_DEFAULT; + // Dynamic meshes should never be compressed (slows down writing to them) + Assert( CompressionType( GetVertexFormat() ) == VERTEX_COMPRESSION_NONE ); + } + + HRESULT hr = Dx9Device()->CreateVertexBuffer( + m_nBufferSize, usage, 0, pool, &m_pVertexBuffer, NULL ); + +#if !defined( _X360 ) + if ( ( hr == D3DERR_OUTOFVIDEOMEMORY ) || ( hr == E_OUTOFMEMORY ) ) + { + // Don't have the memory for this. Try flushing all managed resources + // out of vid mem and try again. + // FIXME: need to record this + Dx9Device()->EvictManagedResources(); + hr = Dx9Device()->CreateVertexBuffer( + m_nBufferSize, usage, 0, pool, &m_pVertexBuffer, NULL ); + } +#endif // !X360 + + if ( FAILED(hr) || ( m_pVertexBuffer == NULL ) ) + { + Warning( "CVertexBufferDx8::Allocate: CreateVertexBuffer failed!\n" ); + return false; + } + + // Track VB allocations + g_VBAllocTracker->CountVB( m_pVertexBuffer, m_bIsDynamic, m_nBufferSize, VertexSize(), GetVertexFormat() ); + +#ifdef VPROF_ENABLED + if ( IsX360() || !m_bIsDynamic ) + { + Assert( m_pGlobalCounter ); + *m_pGlobalCounter += m_nBufferSize; + } +#endif + +#ifdef _DEBUG + ++s_nBufferCount; +#endif + + return true; +} + +void CVertexBufferDx8::Free() +{ + // FIXME: Unlock(0); + if ( m_pVertexBuffer ) + { +#ifdef _DEBUG + --s_nBufferCount; +#endif + + // Track VB allocations + g_VBAllocTracker->UnCountVB( m_pVertexBuffer ); + +#ifdef VPROF_ENABLED + if ( IsX360() || !m_bIsDynamic ) + { + Assert( m_pGlobalCounter ); + *m_pGlobalCounter -= m_nBufferSize; + } +#endif + + m_pVertexBuffer->Release(); + m_pVertexBuffer = NULL; + } +} + + +//----------------------------------------------------------------------------- +// Vertex buffer information +//----------------------------------------------------------------------------- +int CVertexBufferDx8::VertexCount() const +{ + Assert( !m_bIsDynamic ); + return m_nVertexCount; +} + +VertexFormat_t CVertexBufferDx8::GetVertexFormat() const +{ + Assert( !m_bIsDynamic ); + return m_VertexFormat; +} + + +//----------------------------------------------------------------------------- +// Returns true if the buffer is dynamic +//----------------------------------------------------------------------------- +bool CVertexBufferDx8::IsDynamic() const +{ + return m_bIsDynamic; +} + + +//----------------------------------------------------------------------------- +// Only used by dynamic buffers, indicates the next lock should perform a discard. +//----------------------------------------------------------------------------- +void CVertexBufferDx8::Flush() +{ + // This strange-looking line makes a flush only occur if the buffer is dynamic. + m_bFlush = m_bIsDynamic; +} + + +//----------------------------------------------------------------------------- +// Returns the D3D buffer +//----------------------------------------------------------------------------- +IDirect3DVertexBuffer9* CVertexBufferDx8::GetDx9Buffer() +{ + return m_pVertexBuffer; +} + + +//----------------------------------------------------------------------------- +// Casts a dynamic buffer to be a particular vertex type +//----------------------------------------------------------------------------- +void CVertexBufferDx8::BeginCastBuffer( VertexFormat_t format ) +{ + Assert( format != MATERIAL_INDEX_FORMAT_UNKNOWN ); + Assert( m_bIsDynamic && ( m_VertexFormat == 0 || m_VertexFormat == format ) ); + if ( !m_bIsDynamic ) + return; + + m_VertexFormat = format; + int nVertexSize = VertexSize(); + m_nVertexCount = m_nBufferSize / nVertexSize; + + // snap current position up to the next position based on expected size + // so append can safely guarantee nooverwrite regardless of a format growth or shrinkage + if ( !g_pHardwareConfig->SupportsStreamOffset() ) + { + m_nFirstUnwrittenOffset = ( m_nFirstUnwrittenOffset + nVertexSize - 1 ) / nVertexSize; + m_nFirstUnwrittenOffset *= nVertexSize; + if ( m_nFirstUnwrittenOffset > m_nBufferSize ) + { + m_nFirstUnwrittenOffset = m_nBufferSize; + } + } +} + +void CVertexBufferDx8::EndCastBuffer( ) +{ + Assert( m_bIsDynamic && m_VertexFormat != 0 ); + if ( !m_bIsDynamic ) + return; + m_VertexFormat = 0; + m_nVertexCount = 0; +} + + +//----------------------------------------------------------------------------- +// Returns the number of vertices we can still write into the buffer +//----------------------------------------------------------------------------- +int CVertexBufferDx8::GetRoomRemaining() const +{ + return ( m_nBufferSize - m_nFirstUnwrittenOffset ) / VertexSize(); +} + + +//----------------------------------------------------------------------------- +// Locks/unlocks the vertex buffer mesh +//----------------------------------------------------------------------------- +bool CVertexBufferDx8::Lock( int nMaxVertexCount, bool bAppend, VertexDesc_t &desc ) +{ + Assert( !m_bIsLocked && ( nMaxVertexCount != 0 ) && ( nMaxVertexCount <= m_nVertexCount ) ); + Assert( m_VertexFormat != VERTEX_FORMAT_UNKNOWN ); + + // FIXME: Why do we need to sync matrices now? + ShaderUtil()->SyncMatrices(); + g_ShaderMutex.Lock(); + + VPROF( "CVertexBufferDx8::Lock" ); + + void *pLockedData = NULL; + HRESULT hr; + int nMemoryRequired; + bool bHasEnoughMemory; + UINT nLockFlags; + + // This can happen if the buffer was locked but a type wasn't bound + if ( m_VertexFormat == VERTEX_FORMAT_UNKNOWN ) + goto vertexBufferLockFailed; + + // Just give the app crap buffers to fill up while we're suppressed... + if ( g_pShaderDeviceDx8->IsDeactivated() || ( nMaxVertexCount == 0 ) ) + goto vertexBufferLockFailed; + + // Did we ask for something too large? + if ( nMaxVertexCount > m_nVertexCount ) + { + Warning( "Too many vertices for vertex buffer. . tell a programmer (%d>%d)\n", nMaxVertexCount, m_nVertexCount ); + goto vertexBufferLockFailed; + } + + // We might not have a buffer owing to alt-tab type stuff + if ( !m_pVertexBuffer ) + { + if ( !Allocate() ) + goto vertexBufferLockFailed; + } + + // Check to see if we have enough memory + nMemoryRequired = nMaxVertexCount * VertexSize(); + bHasEnoughMemory = ( m_nFirstUnwrittenOffset + nMemoryRequired <= m_nBufferSize ); + + nLockFlags = D3DLOCK_NOSYSLOCK; + if ( bAppend ) + { + // Can't have the first lock after a flush be an appending lock + Assert( !m_bFlush ); + + // If we're appending and we don't have enough room, then puke! + if ( !bHasEnoughMemory || m_bFlush ) + goto vertexBufferLockFailed; + nLockFlags |= ( m_nFirstUnwrittenOffset == 0 ) ? D3DLOCK_DISCARD : D3DLOCK_NOOVERWRITE; + } + else + { + // If we're not appending, no overwrite unless we don't have enough room + // If we're a static buffer, always discard if we're not appending + if ( !m_bFlush && bHasEnoughMemory && m_bIsDynamic ) + { + nLockFlags |= ( m_nFirstUnwrittenOffset == 0 ) ? D3DLOCK_DISCARD : D3DLOCK_NOOVERWRITE; + } + else + { + if ( m_bIsDynamic ) + { + nLockFlags |= D3DLOCK_DISCARD; + } + m_nFirstUnwrittenOffset = 0; + m_bFlush = false; + } + } + +#if !defined( _X360 ) + hr = m_pVertexBuffer->Lock( m_nFirstUnwrittenOffset, nMemoryRequired, &pLockedData, nLockFlags ); +#else + hr = m_pVertexBuffer->Lock( 0, 0, &pLockedData, nLockFlags ); + pLockedData = (unsigned char*)pLockedData + m_nFirstUnwrittenOffset; +#endif + + if ( FAILED( hr ) ) + { + // Check if paged pool is in critical state ( < 5% free ) + PAGED_POOL_INFO_t ppi; + if ( ( SYSCALL_SUCCESS == Plat_GetPagedPoolInfo( &ppi ) ) && + ( ( ppi.numPagesFree * 20 ) < ( ppi.numPagesUsed + ppi.numPagesFree ) ) ) + { + Error( "Out of OS Paged Pool Memory! For more information, please see\nhttp://support.steampowered.com/cgi-bin/steampowered.cfg/php/enduser/std_adp.php?p_faqid=150\n" ); + } + else + { + Warning( "Failed to lock vertex buffer in CVertexBufferDx8::Lock\n" ); + } + goto vertexBufferLockFailed; + } + + ComputeVertexDescription( (unsigned char*)pLockedData, m_VertexFormat, desc ); + if ( g_pHardwareConfig->SupportsStreamOffset() ) + { + desc.m_nFirstVertex = 0; + desc.m_nOffset = m_nFirstUnwrittenOffset; + } + else + { + desc.m_nFirstVertex = m_nFirstUnwrittenOffset / VertexSize(); + desc.m_nOffset = 0; + Assert( m_nFirstUnwrittenOffset == VertexSize() * desc.m_nFirstVertex ); + } + m_bIsLocked = true; + return true; + +vertexBufferLockFailed: + ComputeVertexDescription( 0, 0, desc ); + desc.m_nFirstVertex = 0; + desc.m_nOffset = 0; + return false; +} + + +void CVertexBufferDx8::Unlock( int nWrittenVertexCount, VertexDesc_t &desc ) +{ + Assert( nWrittenVertexCount <= m_nVertexCount ); + + // NOTE: This can happen if another application finishes + // initializing during the construction of a mesh + if ( !m_bIsLocked ) + return; + + if ( m_pVertexBuffer ) + { + m_pVertexBuffer->Unlock(); + } + + m_nFirstUnwrittenOffset += nWrittenVertexCount * VertexSize(); + m_bIsLocked = false; + g_ShaderMutex.Unlock(); +} + + +//----------------------------------------------------------------------------- +// Used to measure how much static buffer memory is touched each frame +//----------------------------------------------------------------------------- +void CVertexBufferDx8::HandlePerFrameTextureStats( int nFrame ) +{ +#ifdef VPROF_ENABLED + if ( m_nVProfFrame != nFrame && !m_bIsDynamic ) + { + m_nVProfFrame = nFrame; + m_pFrameCounter += m_nBufferSize; + } +#endif +} + + +//----------------------------------------------------------------------------- +// Helpers with meshdescs... +//----------------------------------------------------------------------------- +// FIXME: add compression-agnostic read-accessors (which decompress and return by value, checking desc.m_CompressionType) +inline D3DXVECTOR3 &Position( MeshDesc_t const &desc, int vert ) +{ + return *(D3DXVECTOR3*)((unsigned char*)desc.m_pPosition + vert * desc.m_VertexSize_Position ); +} + +inline float Wrinkle( MeshDesc_t const &desc, int vert ) +{ + return *(float*)((unsigned char*)desc.m_pWrinkle + vert * desc.m_VertexSize_Wrinkle ); +} + +inline D3DXVECTOR3 &BoneWeight( MeshDesc_t const &desc, int vert ) +{ + Assert( desc.m_CompressionType == VERTEX_COMPRESSION_NONE ); + return *(D3DXVECTOR3*)((unsigned char*)desc.m_pBoneWeight + vert * desc.m_VertexSize_BoneWeight ); +} + +inline unsigned char *BoneIndex( MeshDesc_t const &desc, int vert ) +{ + return desc.m_pBoneMatrixIndex + vert * desc.m_VertexSize_BoneMatrixIndex; +} + +inline D3DXVECTOR3 &Normal( MeshDesc_t const &desc, int vert ) +{ + Assert( desc.m_CompressionType == VERTEX_COMPRESSION_NONE ); + return *(D3DXVECTOR3*)((unsigned char*)desc.m_pNormal + vert * desc.m_VertexSize_Normal ); +} + +inline unsigned char *Color( MeshDesc_t const &desc, int vert ) +{ + return desc.m_pColor + vert * desc.m_VertexSize_Color; +} + +inline D3DXVECTOR2 &TexCoord( MeshDesc_t const &desc, int vert, int stage ) +{ + return *(D3DXVECTOR2*)((unsigned char*)desc.m_pTexCoord[stage] + vert * desc.m_VertexSize_TexCoord[stage] ); +} + +inline D3DXVECTOR3 &TangentS( MeshDesc_t const &desc, int vert ) +{ + return *(D3DXVECTOR3*)((unsigned char*)desc.m_pTangentS + vert * desc.m_VertexSize_TangentS ); +} + +inline D3DXVECTOR3 &TangentT( MeshDesc_t const &desc, int vert ) +{ + return *(D3DXVECTOR3*)((unsigned char*)desc.m_pTangentT + vert * desc.m_VertexSize_TangentT ); +} + + +//----------------------------------------------------------------------------- +// +// Base mesh +// +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +// constructor, destructor +//----------------------------------------------------------------------------- + +CBaseMeshDX8::CBaseMeshDX8() : m_VertexFormat(0) +{ + m_bMeshLocked = false; +#ifdef DBGFLAG_ASSERT + m_IsDrawing = false; + m_pMaterial = 0; +#endif +} + +CBaseMeshDX8::~CBaseMeshDX8() +{ +} + + +//----------------------------------------------------------------------------- +// For debugging... +//----------------------------------------------------------------------------- +bool CBaseMeshDX8::DebugTrace() const +{ +#ifdef _DEBUG + if (m_pMaterial) + return m_pMaterial->PerformDebugTrace(); +#endif + + return false; +} + +void CBaseMeshDX8::SetMaterial( IMaterial *pMaterial ) +{ +#ifdef DBGFLAG_ASSERT + m_pMaterial = static_cast(pMaterial); +#endif +} + + +//----------------------------------------------------------------------------- +// Sets, gets the vertex format +//----------------------------------------------------------------------------- +void CBaseMeshDX8::SetVertexFormat( VertexFormat_t format ) +{ + m_VertexFormat = format; +} + +VertexFormat_t CBaseMeshDX8::GetVertexFormat() const +{ + return m_VertexFormat; +} + + +//----------------------------------------------------------------------------- +// Sets/gets the morph format +//----------------------------------------------------------------------------- +void CBaseMeshDX8::SetMorphFormat( MorphFormat_t format ) +{ + m_MorphFormat = format; +} + +MorphFormat_t CBaseMeshDX8::GetMorphFormat() const +{ + return m_MorphFormat; +} + + +//----------------------------------------------------------------------------- +// Am I using morph data? +//----------------------------------------------------------------------------- +bool CBaseMeshDX8::IsUsingMorphData() const +{ + LOCK_SHADERAPI(); + // We're not using a morph unless the bound morph is a superset of what the rendermesh needs + MorphFormat_t morphFormat = GetMorphFormat(); + if ( !morphFormat ) + return false; + + return ( ( morphFormat & ShaderUtil()->GetBoundMorphFormat() ) == morphFormat ); +} + +//----------------------------------------------------------------------------- +// Do I need to reset the vertex format? +//----------------------------------------------------------------------------- +bool CBaseMeshDX8::NeedsVertexFormatReset( VertexFormat_t fmt ) const +{ + return m_VertexFormat != fmt; +} + + +//----------------------------------------------------------------------------- +// Do I have enough room? +//----------------------------------------------------------------------------- +bool CBaseMeshDX8::HasEnoughRoom( int nVertexCount, int nIndexCount ) const +{ + // by default, we do + return true; +} + +//----------------------------------------------------------------------------- +// Estimate the memory used +//----------------------------------------------------------------------------- +unsigned CBaseMeshDX8::ComputeMemoryUsed() +{ + unsigned size = 0; + + if ( GetVertexBuffer() ) + { + size += GetVertexBuffer()->VertexCount() * GetVertexBuffer()->VertexSize(); + } + + if ( GetIndexBuffer() ) + { + size += GetIndexBuffer()->IndexCount() * GetIndexBuffer()->IndexSize(); + } + + return size; +} + + +//----------------------------------------------------------------------------- +// Locks mesh for modifying +//----------------------------------------------------------------------------- +void CBaseMeshDX8::ModifyBeginEx( bool bReadOnly, int nFirstVertex, int nVertexCount, int nFirstIndex, int nIndexCount, MeshDesc_t& desc ) +{ + LOCK_SHADERAPI(); + // for the time being, disallow for most cases + Assert(0); +} + +void CBaseMeshDX8::ModifyBegin( int nFirstVertex, int nVertexCount, int nFirstIndex, int nIndexCount, MeshDesc_t& desc ) +{ + LOCK_SHADERAPI(); + // for the time being, disallow for most cases + Assert(0); +} + +void CBaseMeshDX8::ModifyEnd( MeshDesc_t& desc ) +{ + LOCK_SHADERAPI(); + // for the time being, disallow for most cases + Assert(0); +} + + +//----------------------------------------------------------------------------- +// Begins a pass +//----------------------------------------------------------------------------- +void CBaseMeshDX8::BeginPass( ) +{ + LOCK_SHADERAPI(); +} + + +//----------------------------------------------------------------------------- +// Sets the render state and gets the drawing going +//----------------------------------------------------------------------------- +inline void CBaseMeshDX8::DrawMesh( ) +{ +#ifdef DBGFLAG_ASSERT + // Make sure we're not drawing... + Assert( !m_IsDrawing ); + m_IsDrawing = true; +#endif + + // This is going to cause RenderPass to get called a bunch + ShaderAPI()->DrawMesh( this ); + +#ifdef DBGFLAG_ASSERT + m_IsDrawing = false; +#endif +} + + +//----------------------------------------------------------------------------- +// Spews the mesh data +//----------------------------------------------------------------------------- +void CBaseMeshDX8::Spew( int nVertexCount, int nIndexCount, const MeshDesc_t &spewDesc ) +{ + LOCK_SHADERAPI(); + // This has regressed. + int i; + + + // FIXME: just fall back to the base class (CVertexBufferBase) version of this function! + + +#ifdef _DEBUG + if( m_pMaterial ) + { + Plat_DebugString( ( const char * )m_pMaterial->GetName() ); + Plat_DebugString( "\n" ); + } +#endif // _DEBUG + + // This is needed so buffering can just use this + VertexFormat_t fmt = m_VertexFormat; + + // Set up the vertex descriptor + MeshDesc_t desc = spewDesc; + + char tempbuf[256]; + char* temp = tempbuf; + sprintf( tempbuf,"\nVerts: (Vertex Format %llx)\n", fmt); + Plat_DebugString(tempbuf); + + CVertexBufferBase::PrintVertexFormat( fmt ); + + int numBoneWeights = NumBoneWeights( fmt ); + for ( i = 0; i < nVertexCount; ++i ) + { + temp += sprintf( temp, "[%4d] ", i + desc.m_nFirstVertex ); + if( fmt & VERTEX_POSITION ) + { + D3DXVECTOR3& pos = Position( desc, i ); + temp += sprintf(temp, "P %8.2f %8.2f %8.2f ", + pos[0], pos[1], pos[2]); + } + + if ( fmt & VERTEX_WRINKLE ) + { + float flWrinkle = Wrinkle( desc, i ); + temp += sprintf(temp, "Wr %8.2f ",flWrinkle ); + } + + if (numBoneWeights > 0) + { + temp += sprintf(temp, "BW "); + float* pWeight = BoneWeight( desc, i ); + for (int j = 0; j < numBoneWeights; ++j) + { + temp += sprintf(temp, "%1.2f ", pWeight[j]); + } + } + if ( fmt & VERTEX_BONE_INDEX ) + { + unsigned char *pIndex = BoneIndex( desc, i ); + temp += sprintf( temp, "BI %d %d %d %d ", ( int )pIndex[0], ( int )pIndex[1], ( int )pIndex[2], ( int )pIndex[3] ); + Assert( pIndex[0] < 16 ); + Assert( pIndex[1] < 16 ); + Assert( pIndex[2] < 16 ); + Assert( pIndex[3] < 16 ); + } + + if ( fmt & VERTEX_NORMAL ) + { + D3DXVECTOR3& normal = Normal( desc, i ); + temp += sprintf(temp, "N %1.2f %1.2f %1.2f ", + normal[0], normal[1], normal[2]); + } + + if (fmt & VERTEX_COLOR) + { + unsigned char* pColor = Color( desc, i ); + temp += sprintf(temp, "C b %3d g %3d r %3d a %3d ", + pColor[0], pColor[1], pColor[2], pColor[3]); + } + + for (int j = 0; j < VERTEX_MAX_TEXTURE_COORDINATES; ++j) + { + if( TexCoordSize( j, fmt ) > 0) + { + D3DXVECTOR2& texcoord = TexCoord( desc, i, j ); + temp += sprintf(temp, "T%d %.2f %.2f ", j,texcoord[0], texcoord[1]); + } + } + + if (fmt & VERTEX_TANGENT_S) + { + D3DXVECTOR3& tangentS = TangentS( desc, i ); + temp += sprintf(temp, "S %1.2f %1.2f %1.2f ", + tangentS[0], tangentS[1], tangentS[2]); + } + + if (fmt & VERTEX_TANGENT_T) + { + D3DXVECTOR3& tangentT = TangentT( desc, i ); + temp += sprintf(temp, "T %1.2f %1.2f %1.2f ", + tangentT[0], tangentT[1], tangentT[2]); + } + + sprintf(temp,"\n"); + Plat_DebugString(tempbuf); + temp = tempbuf; + } + + sprintf( tempbuf,"\nIndices: %d\n", nIndexCount ); + Plat_DebugString(tempbuf); + for ( i = 0; i < nIndexCount; ++i ) + { + temp += sprintf( temp, "%d ", ( int )desc.m_pIndices[i] ); + if ((i & 0x0F) == 0x0F) + { + sprintf( temp, "\n" ); + Plat_DebugString(tempbuf); + tempbuf[0] = '\0'; + temp = tempbuf; + } + } + sprintf(temp,"\n"); + Plat_DebugString( tempbuf ); +} + +void CBaseMeshDX8::ValidateData( int nVertexCount, int nIndexCount, const MeshDesc_t &spewDesc ) +{ + LOCK_SHADERAPI(); +#ifdef VALIDATE_DEBUG + int i; + + + // FIXME: just fall back to the base class (CVertexBufferBase) version of this function! + + + // This is needed so buffering can just use this + VertexFormat_t fmt = m_pMaterial->GetVertexUsage(); + + // Set up the vertex descriptor + MeshDesc_t desc = spewDesc; + + int numBoneWeights = NumBoneWeights( fmt ); + for ( i = 0; i < nVertexCount; ++i ) + { + if( fmt & VERTEX_POSITION ) + { + D3DXVECTOR3& pos = Position( desc, i ); + Assert( IsFinite( pos[0] ) && IsFinite( pos[1] ) && IsFinite( pos[2] ) ); + } + if( fmt & VERTEX_WRINKLE ) + { + float flWrinkle = Wrinkle( desc, i ); + Assert( IsFinite( flWrinkle ) ); + } + if (numBoneWeights > 0) + { + float* pWeight = BoneWeight( desc, i ); + for (int j = 0; j < numBoneWeights; ++j) + { + Assert( pWeight[j] >= 0.0f && pWeight[j] <= 1.0f ); + } + } + if( fmt & VERTEX_BONE_INDEX ) + { + unsigned char *pIndex = BoneIndex( desc, i ); + Assert( pIndex[0] >= 0 && pIndex[0] < 16 ); + Assert( pIndex[1] >= 0 && pIndex[1] < 16 ); + Assert( pIndex[2] >= 0 && pIndex[2] < 16 ); + Assert( pIndex[3] >= 0 && pIndex[3] < 16 ); + } + if( fmt & VERTEX_NORMAL ) + { + D3DXVECTOR3& normal = Normal( desc, i ); + Assert( normal[0] >= -1.05f && normal[0] <= 1.05f ); + Assert( normal[1] >= -1.05f && normal[1] <= 1.05f ); + Assert( normal[2] >= -1.05f && normal[2] <= 1.05f ); + } + + if (fmt & VERTEX_COLOR) + { + int* pColor = (int*)Color( desc, i ); + Assert( *pColor != FLOAT32_NAN_BITS ); + } + + for (int j = 0; j < VERTEX_MAX_TEXTURE_COORDINATES; ++j) + { + if( TexCoordSize( j, fmt ) > 0) + { + D3DXVECTOR2& texcoord = TexCoord( desc, i, j ); + Assert( IsFinite( texcoord[0] ) && IsFinite( texcoord[1] ) ); + } + } + + if (fmt & VERTEX_TANGENT_S) + { + D3DXVECTOR3& tangentS = TangentS( desc, i ); + Assert( IsFinite( tangentS[0] ) && IsFinite( tangentS[1] ) && IsFinite( tangentS[2] ) ); + } + + if (fmt & VERTEX_TANGENT_T) + { + D3DXVECTOR3& tangentT = TangentT( desc, i ); + Assert( IsFinite( tangentT[0] ) && IsFinite( tangentT[1] ) && IsFinite( tangentT[2] ) ); + } + } +#endif // _DEBUG +} + +void CBaseMeshDX8::Draw( CPrimList *pLists, int nLists ) +{ + LOCK_SHADERAPI(); + Assert( !"CBaseMeshDX8::Draw(CPrimList, int): should never get here." ); +} + + +// Copy verts and/or indices to a mesh builder. This only works for temp meshes! +void CBaseMeshDX8::CopyToMeshBuilder( + int iStartVert, // Which vertices to copy. + int nVerts, + int iStartIndex, // Which indices to copy. + int nIndices, + int indexOffset, // This is added to each index. + CMeshBuilder &builder ) +{ + LOCK_SHADERAPI(); + Assert( false ); + Warning( "CopyToMeshBuilder called on something other than a temp mesh.\n" ); +} + + +//----------------------------------------------------------------------------- +// +// static mesh +// +//----------------------------------------------------------------------------- + +CPrimList *CMeshDX8::s_pPrims; +int CMeshDX8::s_nPrims; +unsigned int CMeshDX8::s_FirstVertex; +unsigned int CMeshDX8::s_NumVertices; + + +//----------------------------------------------------------------------------- +// Computes the mode +//----------------------------------------------------------------------------- +inline D3DPRIMITIVETYPE ComputeMode( MaterialPrimitiveType_t type ) +{ + switch(type) + { +#ifdef _X360 + case MATERIAL_INSTANCED_QUADS: + return D3DPT_QUADLIST; +#endif + + case MATERIAL_POINTS: + return D3DPT_POINTLIST; + + case MATERIAL_LINES: + return D3DPT_LINELIST; + + case MATERIAL_TRIANGLES: + return D3DPT_TRIANGLELIST; + + case MATERIAL_TRIANGLE_STRIP: + return D3DPT_TRIANGLESTRIP; + + // Here, we expect to have the type set later. only works for static meshes + case MATERIAL_HETEROGENOUS: + return (D3DPRIMITIVETYPE)-1; + + default: + Assert(0); + return (D3DPRIMITIVETYPE)-1; + } +} + +//----------------------------------------------------------------------------- +// constructor +//----------------------------------------------------------------------------- +CMeshDX8::CMeshDX8( const char *pTextureGroupName ) : m_NumVertices(0), m_NumIndices(0), m_pVertexBuffer(0), + m_pColorMesh( 0 ), m_nColorMeshVertOffsetInBytes( 0 ), + m_pIndexBuffer(0), m_Type(MATERIAL_TRIANGLES), m_IsVBLocked(false), + m_IsIBLocked(false) +{ + m_pTextureGroupName = pTextureGroupName; + m_Mode = ComputeMode(m_Type); + + m_bHasFlexVerts = false; + m_pFlexVertexBuffer = NULL; + m_nFlexVertOffsetInBytes = 0; +} + +CMeshDX8::~CMeshDX8() +{ + // Don't release the vertex buffer + if (!g_MeshMgr.IsDynamicMesh(this)) + { + if (m_pVertexBuffer) + { + delete m_pVertexBuffer; + } + if (m_pIndexBuffer) + { + SafeRelease( &m_pIndexBuffer ); + } + } +} + +void CMeshDX8::SetFlexMesh( IMesh *pMesh, int nVertexOffsetInBytes ) +{ + if ( !ShaderUtil()->OnSetFlexMesh( this, pMesh, nVertexOffsetInBytes ) ) + return; + + LOCK_SHADERAPI(); + m_nFlexVertOffsetInBytes = nVertexOffsetInBytes; // Offset into dynamic mesh (in bytes) + + if ( pMesh ) + { + m_flexVertCount = pMesh->VertexCount(); + pMesh->MarkAsDrawn(); + + CBaseMeshDX8 *pBaseMesh = static_cast(pMesh); + m_pFlexVertexBuffer = pBaseMesh->GetVertexBuffer(); + + m_bHasFlexVerts = true; + } + else + { + m_flexVertCount = 0; + m_pFlexVertexBuffer = NULL; + m_bHasFlexVerts = false; + } +} + +void CMeshDX8::DisableFlexMesh( ) +{ + CMeshDX8::SetFlexMesh( NULL, 0 ); +} + +bool CMeshDX8::HasFlexMesh( ) const +{ + LOCK_SHADERAPI(); + return m_bHasFlexVerts; +} + +void CMeshDX8::SetColorMesh( IMesh *pColorMesh, int nVertexOffsetInBytes ) +{ + if ( !ShaderUtil()->OnSetColorMesh( this, pColorMesh, nVertexOffsetInBytes ) ) + return; + + LOCK_SHADERAPI(); + m_pColorMesh = ( CMeshDX8 * )pColorMesh; // dangerous conversion! garymcthack + m_nColorMeshVertOffsetInBytes = nVertexOffsetInBytes; + Assert( m_pColorMesh || ( nVertexOffsetInBytes == 0 ) ); + +#ifdef _DEBUG + if ( pColorMesh ) + { + int nVertexCount = VertexCount(); + int numVertsColorMesh = m_pColorMesh->VertexCount(); + Assert( numVertsColorMesh >= nVertexCount ); + } +#endif +} + + +void CMeshDX8::HandleLateCreation( ) +{ + if ( m_pVertexBuffer ) + { + m_pVertexBuffer->HandleLateCreation(); + } + if ( m_pIndexBuffer ) + { + m_pIndexBuffer->HandleLateCreation(); + } + if ( m_pFlexVertexBuffer ) + { + m_pFlexVertexBuffer->HandleLateCreation(); + } + + if ( m_pColorMesh ) + { + m_pColorMesh->HandleLateCreation(); + } +} + + +bool CMeshDX8::HasColorMesh( ) const +{ + LOCK_SHADERAPI(); + return (m_pColorMesh != NULL); +} + + +//----------------------------------------------------------------------------- +// Locks/ unlocks the vertex buffer +//----------------------------------------------------------------------------- +bool CMeshDX8::Lock( int nVertexCount, bool bAppend, VertexDesc_t &desc ) +{ + Assert( !m_IsVBLocked ); + + // Just give the app crap buffers to fill up while we're suppressed... + if ( g_pShaderDeviceDx8->IsDeactivated() || (nVertexCount == 0)) + { + // Set up the vertex descriptor + CVertexBufferBase::ComputeVertexDescription( 0, 0, desc ); + desc.m_nFirstVertex = 0; + return false; + } + + // Static vertex buffer case + if (!m_pVertexBuffer) + { + int size = g_MeshMgr.VertexFormatSize( m_VertexFormat ); + m_pVertexBuffer = new CVertexBuffer( Dx9Device(), m_VertexFormat, 0, size, nVertexCount, m_pTextureGroupName, ShaderAPI()->UsingSoftwareVertexProcessing() ); + } + + // Lock it baby + int nMaxVerts, nMaxIndices; + g_MeshMgr.GetMaxToRender( this, false, &nMaxVerts, &nMaxIndices ); + if ( !g_pHardwareConfig->SupportsStreamOffset() ) + { + // Without stream offset, we can't use VBs greater than 65535 verts (due to our using 16-bit indices) + Assert( nVertexCount <= nMaxVerts ); + } + + unsigned char *pVertexMemory = m_pVertexBuffer->Lock( nVertexCount, desc.m_nFirstVertex ); + if ( !pVertexMemory ) + { + if ( nVertexCount > nMaxVerts ) + { + Assert( 0 ); + Error( "Too many verts for a dynamic vertex buffer (%d>%d) Tell a programmer to up VERTEX_BUFFER_SIZE.\n", + ( int )nVertexCount, ( int )nMaxVerts ); + } + else + { + // Check if paged pool is in critical state ( < 5% free ) + PAGED_POOL_INFO_t ppi; + if ( ( SYSCALL_SUCCESS == Plat_GetPagedPoolInfo( &ppi ) ) && + ( ( ppi.numPagesFree * 20 ) < ( ppi.numPagesUsed + ppi.numPagesFree ) ) ) + { + Error( "Out of OS Paged Pool Memory! For more information, please see\nhttp://support.steampowered.com/cgi-bin/steampowered.cfg/php/enduser/std_adp.php?p_faqid=150\n" ); + } + else + { + Assert( 0 ); + Error( "failed to lock vertex buffer in CMeshDX8::LockVertexBuffer: nVertexCount=%d, nFirstVertex=%d\n", nVertexCount, desc.m_nFirstVertex ); + } + } + CVertexBufferBase::ComputeVertexDescription( 0, 0, desc ); + return false; + } + + // Set up the vertex descriptor + CVertexBufferBase::ComputeVertexDescription( pVertexMemory, m_VertexFormat, desc ); + m_IsVBLocked = true; + +#ifdef RECORDING + m_LockVertexBufferSize = nVertexCount * desc.m_ActualVertexSize; + m_LockVertexBuffer = pVertexMemory; +#endif + + return true; +} + +void CMeshDX8::Unlock( int nVertexCount, VertexDesc_t& desc ) +{ + // NOTE: This can happen if another application finishes + // initializing during the construction of a mesh + if (!m_IsVBLocked) + return; + + // This is recorded for debugging. . not sent to dx. + RECORD_COMMAND( DX8_SET_VERTEX_BUFFER_FORMAT, 2 ); + RECORD_INT( m_pVertexBuffer->UID() ); + RECORD_INT( m_VertexFormat ); + + RECORD_COMMAND( DX8_VERTEX_DATA, 3 ); + RECORD_INT( m_pVertexBuffer->UID() ); + RECORD_INT( m_LockVertexBufferSize ); + RECORD_STRUCT( m_LockVertexBuffer, m_LockVertexBufferSize ); + + Assert(m_pVertexBuffer); + m_pVertexBuffer->Unlock(nVertexCount); + m_IsVBLocked = false; +} + +//----------------------------------------------------------------------------- +// Locks/unlocks the index buffer +//----------------------------------------------------------------------------- +int CMeshDX8::Lock( bool bReadOnly, int nFirstIndex, int nIndexCount, IndexDesc_t &desc ) +{ + Assert( !m_IsIBLocked ); + + // Just give the app crap buffers to fill up while we're suppressed... + if ( g_pShaderDeviceDx8->IsDeactivated() || (nIndexCount == 0)) + { + // Set up a bogus index descriptor + desc.m_pIndices = g_nScratchIndexBuffer; + desc.m_nIndexSize = 0; + return 0; + } + + // Static vertex buffer case + if (!m_pIndexBuffer) + { + SafeAssign( &m_pIndexBuffer, new CIndexBuffer( Dx9Device(), nIndexCount, ShaderAPI()->UsingSoftwareVertexProcessing() ) ); + } + + int startIndex; + desc.m_pIndices = m_pIndexBuffer->Lock( bReadOnly, nIndexCount, startIndex, nFirstIndex ); + if( !desc.m_pIndices ) + { + desc.m_pIndices = g_nScratchIndexBuffer; + desc.m_nIndexSize = 0; + + // Check if paged pool is in critical state ( < 5% free ) + PAGED_POOL_INFO_t ppi; + if ( ( SYSCALL_SUCCESS == Plat_GetPagedPoolInfo( &ppi ) ) && + ( ( ppi.numPagesFree * 20 ) < ( ppi.numPagesUsed + ppi.numPagesFree ) ) ) + { + Error( "Out of OS Paged Pool Memory! For more information, please see\nhttp://support.steampowered.com/cgi-bin/steampowered.cfg/php/enduser/std_adp.php?p_faqid=150\n" ); + } + else + { + Assert( 0 ); + Error( "failed to lock index buffer in CMeshDX8::LockIndexBuffer\n" ); + } + + return 0; + } + + desc.m_nIndexSize = 1; + m_IsIBLocked = true; + +#if defined( RECORDING ) || defined( CHECK_INDICES ) + m_LockIndexBufferSize = nIndexCount * 2; + m_LockIndexBuffer = desc.m_pIndices; +#endif + + return startIndex; +} + + +void CMeshDX8::Unlock( int nIndexCount, IndexDesc_t &desc ) +{ + // NOTE: This can happen if another application finishes + // initializing during the construction of a mesh + if (!m_IsIBLocked) + return; + + RECORD_COMMAND( DX8_INDEX_DATA, 3 ); + RECORD_INT( m_pIndexBuffer->UID() ); + RECORD_INT( m_LockIndexBufferSize ); + RECORD_STRUCT( m_LockIndexBuffer, m_LockIndexBufferSize ); + + Assert(m_pIndexBuffer); + +#ifdef CHECK_INDICES + m_pIndexBuffer->UpdateShadowIndices( ( unsigned short * )m_LockIndexBuffer ); +#endif // CHECK_INDICES + + // Unlock, and indicate how many vertices we actually used + m_pIndexBuffer->Unlock(nIndexCount); + m_IsIBLocked = false; +} + + +//----------------------------------------------------------------------------- +// Locks/unlocks the entire mesh +//----------------------------------------------------------------------------- +void CMeshDX8::LockMesh( int nVertexCount, int nIndexCount, MeshDesc_t& desc ) +{ + ShaderUtil()->SyncMatrices(); + + g_ShaderMutex.Lock(); + VPROF( "CMeshDX8::LockMesh" ); + Lock( nVertexCount, false, *static_cast( &desc ) ); + if ( m_Type != MATERIAL_POINTS ) + { + Lock( false, -1, nIndexCount, *static_cast( &desc ) ); + } + else + { + desc.m_pIndices = g_nScratchIndexBuffer; + desc.m_nIndexSize = 0; + } + + CBaseMeshDX8::m_bMeshLocked = true; +} + + +void CMeshDX8::UnlockMesh( int nVertexCount, int nIndexCount, MeshDesc_t& desc ) +{ + VPROF( "CMeshDX8::UnlockMesh" ); + + Assert( CBaseMeshDX8::m_bMeshLocked ); + + Unlock( nVertexCount, *static_cast( &desc ) ); + if ( m_Type != MATERIAL_POINTS ) + { + Unlock( nIndexCount, *static_cast( &desc ) ); + } + + // The actual # we wrote + m_NumVertices = nVertexCount; + m_NumIndices = nIndexCount; + + CBaseMeshDX8::m_bMeshLocked = false; + g_ShaderMutex.Unlock(); +} + + +//----------------------------------------------------------------------------- +// Locks mesh for modifying +//----------------------------------------------------------------------------- +void CMeshDX8::ModifyBeginEx( bool bReadOnly, int nFirstVertex, int nVertexCount, int nFirstIndex, int nIndexCount, MeshDesc_t& desc ) +{ + VPROF( "CMeshDX8::ModifyBegin" ); + + // Just give the app crap buffers to fill up while we're suppressed... + if ( g_pShaderDeviceDx8->IsDeactivated()) + { + // Set up a bogus descriptor + g_MeshMgr.ComputeVertexDescription( 0, 0, desc ); + desc.m_pIndices = g_nScratchIndexBuffer; + desc.m_nIndexSize = 0; + return; + } + + Assert( m_pVertexBuffer ); + + // Lock it baby + unsigned char* pVertexMemory = m_pVertexBuffer->Modify( bReadOnly, nFirstVertex, nVertexCount ); + if ( pVertexMemory ) + { + m_IsVBLocked = true; + g_MeshMgr.ComputeVertexDescription( pVertexMemory, m_VertexFormat, desc ); + +#ifdef RECORDING + m_LockVertexBufferSize = nVertexCount * desc.m_ActualVertexSize; + m_LockVertexBuffer = pVertexMemory; +#endif + } + + desc.m_nFirstVertex = nFirstVertex; + + Lock( bReadOnly, nFirstIndex, nIndexCount, *static_cast( &desc ) ); +} + +void CMeshDX8::ModifyBegin( int nFirstVertex, int nVertexCount, int nFirstIndex, int nIndexCount, MeshDesc_t& desc ) +{ + ModifyBeginEx( false, nFirstVertex, nVertexCount, nFirstIndex, nIndexCount, desc ); +} + +void CMeshDX8::ModifyEnd( MeshDesc_t& desc ) +{ + VPROF( "CMeshDX8::ModifyEnd" ); + Unlock( 0, *static_cast( &desc ) ); + Unlock( 0, *static_cast( &desc ) ); +} + + +//----------------------------------------------------------------------------- +// returns the # of vertices (static meshes only) +//----------------------------------------------------------------------------- +int CMeshDX8::VertexCount() const +{ + return m_pVertexBuffer ? m_pVertexBuffer->VertexCount() : 0; +} + + +//----------------------------------------------------------------------------- +// returns the # of indices +//----------------------------------------------------------------------------- +int CMeshDX8::IndexCount( ) const +{ + return m_pIndexBuffer ? m_pIndexBuffer->IndexCount() : 0; +} + + +//----------------------------------------------------------------------------- +// Sets up the vertex and index buffers +//----------------------------------------------------------------------------- +void CMeshDX8::UseIndexBuffer( CIndexBuffer* pBuffer ) +{ + SafeAssign( &m_pIndexBuffer, pBuffer ); +} + +void CMeshDX8::UseVertexBuffer( CVertexBuffer* pBuffer ) +{ + m_pVertexBuffer = pBuffer; +} + + +//----------------------------------------------------------------------------- +// Sets the primitive type +//----------------------------------------------------------------------------- +void CMeshDX8::SetPrimitiveType( MaterialPrimitiveType_t type ) +{ + Assert( IsX360() || ( type != MATERIAL_INSTANCED_QUADS ) ); + if ( !ShaderUtil()->OnSetPrimitiveType( this, type ) ) + { + return; + } + + LOCK_SHADERAPI(); + m_Type = type; + m_Mode = ComputeMode( type ); +} + +MaterialPrimitiveType_t CMeshDX8::GetPrimitiveType( ) const +{ + return m_Type; +} + + +//----------------------------------------------------------------------------- +// Computes the number of primitives we're gonna draw +//----------------------------------------------------------------------------- +int CMeshDX8::NumPrimitives( int nVertexCount, int nIndexCount ) const +{ + switch(m_Mode) + { + case D3DPT_POINTLIST: + return nVertexCount; + + case D3DPT_LINELIST: + return nIndexCount / 2; + + case D3DPT_TRIANGLELIST: + return nIndexCount / 3; + + case D3DPT_TRIANGLESTRIP: + return nIndexCount - 2; + + default: + // invalid, baby! + Assert(0); + } + + return 0; +} + + +//----------------------------------------------------------------------------- +// Checks if it's a valid format +//----------------------------------------------------------------------------- +#ifdef _DEBUG +static void OutputVertexFormat( VertexFormat_t format ) +{ + // FIXME: this is a duplicate of the function in meshdx8.cpp + VertexCompressionType_t compressionType = CompressionType( format ); + + if ( format & VERTEX_POSITION ) + { + Warning( "VERTEX_POSITION|" ); + } + if ( format & VERTEX_NORMAL ) + { + if ( compressionType == VERTEX_COMPRESSION_ON ) + Warning( "VERTEX_NORMAL[COMPRESSED]|" ); + else + Warning( "VERTEX_NORMAL|" ); + } + if ( format & VERTEX_COLOR ) + { + Warning( "VERTEX_COLOR|" ); + } + if ( format & VERTEX_SPECULAR ) + { + Warning( "VERTEX_SPECULAR|" ); + } + if ( format & VERTEX_TANGENT_S ) + { + Warning( "VERTEX_TANGENT_S|" ); + } + if ( format & VERTEX_TANGENT_T ) + { + Warning( "VERTEX_TANGENT_T|" ); + } + if ( format & VERTEX_BONE_INDEX ) + { + Warning( "VERTEX_BONE_INDEX|" ); + } + if ( format & VERTEX_FORMAT_VERTEX_SHADER ) + { + Warning( "VERTEX_FORMAT_VERTEX_SHADER|" ); + } + Warning( "\nBone weights: %d (%s)\n", NumBoneWeights( format ), + ( CompressionType( format ) == VERTEX_COMPRESSION_ON ? "compressed" : "uncompressed" ) ); + Warning( "user data size: %d (%s)\n", UserDataSize( format ), + ( CompressionType( format ) == VERTEX_COMPRESSION_ON ? "compressed" : "uncompressed" ) ); + Warning( "num tex coords: %d\n", NumTextureCoordinates( format ) ); + // NOTE: This doesn't print texcoord sizes. +} +#endif + +bool CMeshDX8::IsValidVertexFormat( VertexFormat_t vertexFormat ) +{ + // FIXME: Make this a debug-only check on say 6th July 2007 (after a week or so's testing) + // (i.e. avoid the 360 release build perf. hit for when we ship) + bool bCheckCompression = ( m_VertexFormat & VERTEX_FORMAT_COMPRESSED ) && + ( ( vertexFormat == VERTEX_FORMAT_INVALID ) || ( ( vertexFormat & VERTEX_FORMAT_COMPRESSED ) == 0 ) ); + + if ( bCheckCompression || IsPC() || IsDebug() ) + { + IMaterialInternal* pMaterial = ShaderAPI()->GetBoundMaterial(); + Assert( pMaterial ); + + // the material format should match the vertex usage, unless another format is passed in + if ( vertexFormat == VERTEX_FORMAT_INVALID ) + { + vertexFormat = pMaterial->GetVertexUsage() & ~( VERTEX_FORMAT_VERTEX_SHADER | VERTEX_FORMAT_USE_EXACT_FORMAT ); + + // Blat out unused fields + vertexFormat &= ~g_MeshMgr.UnusedVertexFields(); + int nUnusedTextureCoords = g_MeshMgr.UnusedTextureCoords(); + for ( int i = 0; i < VERTEX_MAX_TEXTURE_COORDINATES; ++i ) + { + if ( nUnusedTextureCoords & ( 1 << i ) ) + { + vertexFormat &= ~VERTEX_TEXCOORD_MASK( i ); + } + } + } + else + { + vertexFormat &= ~( VERTEX_FORMAT_VERTEX_SHADER | VERTEX_FORMAT_USE_EXACT_FORMAT ); + } + + bool bIsValid = (( VERTEX_FORMAT_FIELD_MASK & vertexFormat ) & ( VERTEX_FORMAT_FIELD_MASK & ~m_VertexFormat )) == 0; + + if ( m_VertexFormat & VERTEX_FORMAT_COMPRESSED ) + { + // We shouldn't get compressed verts if this material doesn't support them! + if ( ( vertexFormat & VERTEX_FORMAT_COMPRESSED ) == 0 ) + { + static int numWarnings = 0; + if ( numWarnings++ == 0 ) + { + // NOTE: ComputeVertexFormat() will make sure no materials support VERTEX_FORMAT_COMPRESSED + // if vertex compression is disabled in the config + if ( g_pHardwareConfig->SupportsCompressedVertices() == VERTEX_COMPRESSION_NONE ) + Warning( "ERROR: Compressed vertices in use but vertex compression is disabled (or not supported on this hardware)!\n" ); + else + Warning( "ERROR: Compressed vertices in use but material does not support them!\n" ); + } + Assert( 0 ); + bIsValid = false; + } + } + + bIsValid = bIsValid && UserDataSize( m_VertexFormat ) >= UserDataSize( vertexFormat ); + + for ( int i=0; i < VERTEX_MAX_TEXTURE_COORDINATES; i++ ) + { + if ( TexCoordSize( i, m_VertexFormat ) < TexCoordSize( i, vertexFormat ) ) + { + bIsValid = false; + } + } + + // NOTE: It can totally be valid to have more weights than the current number of bones. + // The -1 here is because if we have N bones, we can have only (N-1) weights, + // since the Nth is implied (the weights sum to 1). + int nWeightCount = NumBoneWeights( m_VertexFormat ); + bIsValid = bIsValid && ( nWeightCount >= ( g_pShaderAPI->GetCurrentNumBones() - 1 ) ); + +#ifdef _DEBUG + if ( !bIsValid ) + { + Warning( "Material Format:" ); + if ( g_pShaderAPI->GetCurrentNumBones() > 0 ) + { + vertexFormat |= VERTEX_BONE_INDEX; + vertexFormat &= ~VERTEX_BONE_WEIGHT_MASK; + vertexFormat |= VERTEX_BONEWEIGHT( 2 ); + } + + OutputVertexFormat( vertexFormat ); + Warning( "Mesh Format:" ); + OutputVertexFormat( m_VertexFormat ); + } +#endif + return bIsValid; + } + + return true; +} + + +//----------------------------------------------------------------------------- +// Stream source setting methods +//----------------------------------------------------------------------------- +void CMeshDX8::SetVertexIDStreamState() +{ + // FIXME: this method duplicates the code in CMeshMgr::SetVertexIDStreamState + + if ( IsX360() ) + return; + + bool bUsingVertexID = IsUsingVertexID(); + if ( bUsingVertexID != g_bUsingVertexID ) + { + if ( bUsingVertexID ) + { + // NOTE: Morphing doesn't work with dynamic buffers!!! BLEAH + // It's because the indices (which are not 0 based for dynamic buffers) + // are accessing both the vertexID buffer + the regular vertex buffer. + // This *might* be fixable with baseVertexIndex? + + // NOTE: At the moment, vertex id is only used for hw morphing. I've got it + // set up so that a shader that supports hw morphing always says it uses vertex id. + // If we ever use vertex id for something other than hw morphing, we're going + // to have to revisit how those shaders say they want to use vertex id + // or fix this some other way + Assert( !g_pShaderAPI->IsHWMorphingEnabled() || !m_pVertexBuffer->IsDynamic() ); + + CVertexBuffer *pVertexIDBuffer = g_MeshMgr.GetVertexIDBuffer( ); + RECORD_COMMAND( DX8_SET_STREAM_SOURCE, 4 ); + RECORD_INT( pVertexIDBuffer->UID() ); + RECORD_INT( 3 ); + RECORD_INT( 0 ); + RECORD_INT( pVertexIDBuffer->VertexSize() ); + + D3DSetStreamSource( 3, pVertexIDBuffer->GetInterface(), 0, pVertexIDBuffer->VertexSize() ); + pVertexIDBuffer->HandlePerFrameTextureStats( ShaderAPI()->GetCurrentFrameCounter() ); + } + else + { + RECORD_COMMAND( DX8_SET_STREAM_SOURCE, 4 ); + RECORD_INT( -1 ); // vertex buffer id + RECORD_INT( 3 ); // stream + RECORD_INT( 0 ); // vertex offset + RECORD_INT( 0 ); // vertex size + + D3DSetStreamSource( 3, 0, 0, 0 ); + } + g_bUsingVertexID = bUsingVertexID; + } +} + +void CMeshDX8::SetColorStreamState() +{ + if ( ( m_pColorMesh != g_pLastColorMesh ) || ( m_nColorMeshVertOffsetInBytes != g_nLastColorMeshVertOffsetInBytes ) ) + { + if ( m_pColorMesh ) + { + RECORD_COMMAND( DX8_SET_STREAM_SOURCE, 4 ); + RECORD_INT( m_pColorMesh->GetVertexBuffer()->UID() ); + RECORD_INT( 1 ); + RECORD_INT( m_nColorMeshVertOffsetInBytes ); + RECORD_INT( m_pColorMesh->GetVertexBuffer()->VertexSize() ); + + D3DSetStreamSource( 1, m_pColorMesh->GetVertexBuffer()->GetInterface(), + m_nColorMeshVertOffsetInBytes, m_pColorMesh->GetVertexBuffer()->VertexSize() ); + m_pColorMesh->GetVertexBuffer()->HandlePerFrameTextureStats( ShaderAPI()->GetCurrentFrameCounter() ); + } + else + { + RECORD_COMMAND( DX8_SET_STREAM_SOURCE, 4 ); + RECORD_INT( -1 ); // vertex buffer id + RECORD_INT( 1 ); // stream + RECORD_INT( 0 ); // vertex offset + RECORD_INT( 0 ); // vertex size + + D3DSetStreamSource( 1, 0, 0, 0 ); + } + g_pLastColorMesh = m_pColorMesh; + g_nLastColorMeshVertOffsetInBytes = m_nColorMeshVertOffsetInBytes; + } +} + +void CMeshDX8::SetVertexStreamState( int nVertOffsetInBytes ) +{ + // Calls in here assume shader support... + if ( HardwareConfig()->SupportsVertexAndPixelShaders() ) + { + if ( HasFlexMesh() ) + { + // m_pFlexVertexBuffer is the flex buffer down inside the CMeshMgr singleton + D3DSetStreamSource( 2, m_pFlexVertexBuffer->GetInterface(), m_nFlexVertOffsetInBytes, m_pFlexVertexBuffer->VertexSize() ); + + if ( g_pHardwareConfig->Caps().m_SupportsVertexShaders_2_0 ) + { + float c[4] = { 1.0f, g_pMaterialSystemHardwareConfig->SupportsPixelShaders_2_b() ? 1.0f : 0.0f, 0.0f, 0.0f }; + ShaderAPI()->SetVertexShaderConstant( VERTEX_SHADER_FLEXSCALE, c, 1 ); + } + + g_bFlexMeshStreamSet = true; + } + else + { + Assert( nVertOffsetInBytes == 0 ); + Assert( m_pVertexBuffer ); + + // HACK...point stream 2 at the same VB which is bound to stream 0... + // NOTE: D3D debug DLLs will RIP if stream 0 has a smaller stride than the largest + // offset in the stream 2 vertex decl elements (which are position(12)+wrinkle(4)+normal(12)) + // If this fires, go find the material/shader which is requesting a really 'thin' + // stream 0 vertex, and fatten it up slightly (e.g. add a D3DCOLOR element) + int minimumStreamZeroStride = 4 * sizeof( float ); + Assert( m_pVertexBuffer->VertexSize() >= minimumStreamZeroStride ); + if ( m_pVertexBuffer->VertexSize() < minimumStreamZeroStride ) + { + static bool bWarned = false; + if( !bWarned ) + { + Warning( "Shader specifying too-thin vertex format, should be at least %d bytes! (Suppressing furthur warnings)\n", minimumStreamZeroStride ); + bWarned = true; + } + } + + // Set a 4kb all-zero static VB into the flex/wrinkle stream with a stride of 1 bytes, so the vertex shader always reads valid floating point values (otherwise it can get NaN's/Inf's, and under OpenGL this is bad on NVidia) + // togl requires non-zero strides, but on D3D9 we can set a stride of 0 for a little more efficiency. + D3DSetStreamSource( 2, g_MeshMgr.GetZeroVertexBuffer(), 0, IsOpenGL() ? 4 : 0 ); + + if ( g_pHardwareConfig->Caps().m_SupportsVertexShaders_2_0 ) + { + float c[4] = { 0.0f, 0.0f, 0.0f, 0.0f }; + ShaderAPI()->SetVertexShaderConstant( VERTEX_SHADER_FLEXSCALE, c, 1 ); + } + + g_bFlexMeshStreamSet = false; + } + } + + // MESHFIXME: Make sure this jives between the mesh/ib/vb version. +#ifdef _X360 + if ( ( g_pLastVertex != m_pVertexBuffer ) || ( m_pVertexBuffer->IsDynamic() ) || ( g_nLastVertOffsetInBytes != nVertOffsetInBytes ) ) +#else + if ( ( g_pLastVertex != m_pVertexBuffer ) || ( g_nLastVertOffsetInBytes != nVertOffsetInBytes ) ) +#endif + { + Assert( m_pVertexBuffer ); + + RECORD_COMMAND( DX8_SET_STREAM_SOURCE, 4 ); + RECORD_INT( m_pVertexBuffer->UID() ); + RECORD_INT( 0 ); + RECORD_INT( nVertOffsetInBytes ); + RECORD_INT( m_pVertexBuffer->VertexSize() ); + + D3DSetStreamSource( 0, m_pVertexBuffer->GetInterface(), nVertOffsetInBytes, m_pVertexBuffer->VertexSize() ); + m_pVertexBuffer->HandlePerFrameTextureStats( ShaderAPI()->GetCurrentFrameCounter() ); + + g_pLastVertex = m_pVertexBuffer; + g_nLastVertOffsetInBytes = nVertOffsetInBytes; + } +} + +void CMeshDX8::SetIndexStreamState( int firstVertexIdx ) +{ +#ifdef _X360 + if ( ( g_pLastIndexBuffer != NULL ) || (g_pLastIndex != m_pIndexBuffer) || ( m_pIndexBuffer->IsDynamic() ) || ( firstVertexIdx != g_LastVertexIdx ) ) +#else + if ( ( g_pLastIndexBuffer != NULL ) || (g_pLastIndex != m_pIndexBuffer) || ( firstVertexIdx != g_LastVertexIdx ) ) +#endif + { + Assert( m_pIndexBuffer ); + + RECORD_COMMAND( DX8_SET_INDICES, 2 ); + RECORD_INT( m_pIndexBuffer->UID() ); + RECORD_INT( firstVertexIdx ); + + Dx9Device()->SetIndices( m_pIndexBuffer->GetInterface() ); + m_pIndexBuffer->HandlePerFrameTextureStats( ShaderAPI()->GetCurrentFrameCounter() ); + m_FirstIndex = firstVertexIdx; + + SafeAssign( &g_pLastIndex, m_pIndexBuffer ); + g_pLastIndexBuffer = NULL; + g_LastVertexIdx = firstVertexIdx; + } +} + +bool CMeshDX8::SetRenderState( int nVertexOffsetInBytes, int nFirstVertexIdx, VertexFormat_t vertexFormat ) +{ + // Can't set the state if we're deactivated + if ( g_pShaderDeviceDx8->IsDeactivated() ) + { + ResetMeshRenderState(); + return false; + } + + g_LastVertexFormat = vertexFormat; + + SetVertexIDStreamState(); + SetColorStreamState(); + SetVertexStreamState( nVertexOffsetInBytes ); + SetIndexStreamState( nFirstVertexIdx ); + + return true; +} + + +//----------------------------------------------------------------------------- +// Draws the static mesh +//----------------------------------------------------------------------------- +void CMeshDX8::Draw( int nFirstIndex, int nIndexCount ) +{ + Assert( m_pVertexBuffer ); + if ( !m_pVertexBuffer ) + { + return; + } + + if ( !ShaderUtil()->OnDrawMesh( this, nFirstIndex, nIndexCount ) ) + { + MarkAsDrawn(); + return; + } + + CPrimList primList; + if( nFirstIndex == -1 || nIndexCount == 0 ) + { + primList.m_FirstIndex = 0; + primList.m_NumIndices = m_NumIndices; + } + else + { + primList.m_FirstIndex = nFirstIndex; + primList.m_NumIndices = nIndexCount; + } + DrawInternal( &primList, 1 ); +} + +void CMeshDX8::Draw( CPrimList *pLists, int nLists ) +{ + Assert( m_pVertexBuffer ); + if ( !m_pVertexBuffer ) + { + return; + } + + if ( !ShaderUtil()->OnDrawMesh( this, pLists, nLists ) ) + { + MarkAsDrawn(); + return; + } + + DrawInternal( pLists, nLists ); +} + + +void CMeshDX8::DrawInternal( CPrimList *pLists, int nLists ) +{ + HandleLateCreation(); + + // Make sure there's something to draw.. + int i; + for ( i=0; i < nLists; i++ ) + { + if ( pLists[i].m_NumIndices > 0 ) + break; + } + if ( i == nLists ) + return; + + // can't do these in selection mode! + Assert( !ShaderAPI()->IsInSelectionMode() ); + + if ( !SetRenderState( 0, 0 ) ) + return; + + s_pPrims = pLists; + s_nPrims = nLists; + +#ifdef _DEBUG + for ( i = 0; i < nLists; ++i) + { + Assert( pLists[i].m_NumIndices > 0 ); + } +#endif + + s_FirstVertex = 0; + s_NumVertices = m_pVertexBuffer->VertexCount(); + + DrawMesh(); +} + + +#ifdef CHECK_INDICES +void CMeshDX8::CheckIndices( CPrimList *pPrim, int numPrimitives ) +{ + // g_pLastVertex - this is the current vertex buffer + // g_pLastColorMesh - this is the current color mesh, if there is one. + // g_pLastIndex - this is the current index buffer. + // vertoffset : m_FirstIndex + if( m_Mode == D3DPT_TRIANGLELIST || m_Mode == D3DPT_TRIANGLESTRIP ) + { + Assert( pPrim->m_FirstIndex >= 0 && pPrim->m_FirstIndex < g_pLastIndex->IndexCount() ); + int i; + for( i = 0; i < 2; i++ ) + { + CVertexBuffer *pMesh; + if( i == 0 ) + { + pMesh = g_pLastVertex; + Assert( pMesh ); + } + else + { + if( !g_pLastColorMesh ) + { + continue; + } + pMesh = g_pLastColorMesh->m_pVertexBuffer; + if( !pMesh ) + { + continue; + } + } + Assert( s_FirstVertex >= 0 && + (int)( s_FirstVertex + m_FirstIndex ) < pMesh->VertexCount() ); + int nIndexCount = 0; + if( m_Mode == D3DPT_TRIANGLELIST ) + { + nIndexCount = numPrimitives * 3; + } + else if( m_Mode == D3DPT_TRIANGLESTRIP ) + { + nIndexCount = numPrimitives + 2; + } + else + { + Assert( 0 ); + } + int j; + for( j = 0; j < nIndexCount; j++ ) + { + int index = g_pLastIndex->GetShadowIndex( j + pPrim->m_FirstIndex ); + if ( ( index < (int)s_FirstVertex ) || ( index >= (int)( s_FirstVertex + s_NumVertices ) ) ) + Warning("%s invalid index: %d [%u..%u]\n", __FUNCTION__, index, s_FirstVertex, s_FirstVertex + s_NumVertices - 1 ); + Assert( index >= (int)s_FirstVertex ); + Assert( index < (int)(s_FirstVertex + s_NumVertices) ); + } + } + } +} +#endif // CHECK_INDICES + + +//----------------------------------------------------------------------------- +// Actually does the dirty deed of rendering +//----------------------------------------------------------------------------- +void CMeshDX8::RenderPass() +{ + LOCK_SHADERAPI(); + VPROF( "CMeshDX8::RenderPass" ); + + HandleLateCreation(); + + Assert( m_Type != MATERIAL_HETEROGENOUS ); + + // make sure the vertex format is a superset of the current material's + // vertex format... + if ( !IsValidVertexFormat( g_LastVertexFormat ) ) + { + Warning( "Material %s does not support vertex format used by the mesh (maybe missing fields or mismatched vertex compression?), mesh will not be rendered. Grab a programmer!\n", + ShaderAPI()->GetBoundMaterial()->GetName() ); + return; + } + + for ( int iPrim=0; iPrim < s_nPrims; iPrim++ ) + { + CPrimList *pPrim = &s_pPrims[iPrim]; + + if ( pPrim->m_NumIndices == 0 ) + continue; + + if ( ( m_Type == MATERIAL_POINTS ) || ( m_Type == MATERIAL_INSTANCED_QUADS ) ) + { + tmZone( TELEMETRY_LEVEL1, TMZF_NONE, "Dx9Device_DrawPrimitive" ); + + // (For point/instanced-quad lists, we don't actually fill in indices, but we treat it as + // though there are indices for the list up until here). + Dx9Device()->DrawPrimitive( m_Mode, s_FirstVertex, pPrim->m_NumIndices ); + } + else + { + int numPrimitives = NumPrimitives( s_NumVertices, pPrim->m_NumIndices ); + +#ifdef CHECK_INDICES + CheckIndices( pPrim, numPrimitives ); +#endif // CHECK_INDICES + { + VPROF( "Dx9Device()->DrawIndexedPrimitive" ); + VPROF_INCREMENT_COUNTER( "DrawIndexedPrimitive", 1 ); + VPROF_INCREMENT_COUNTER( "numPrimitives", numPrimitives ); + VPROF_INCREMENT_GROUP_COUNTER( "render/DrawIndexedPrimitive", COUNTER_GROUP_TELEMETRY, 1 ); + VPROF_INCREMENT_GROUP_COUNTER( "render/numPrimitives", COUNTER_GROUP_TELEMETRY, 1 ); + + Dx9Device()->DrawIndexedPrimitive( + m_Mode, // Member of the D3DPRIMITIVETYPE enumerated type, describing the type of primitive to render. D3DPT_POINTLIST is not supported with this method. + + m_FirstIndex, // Offset from the start of the vertex buffer to the first vertex index. An index of 0 in the index buffer refers to this location in the vertex buffer. + + s_FirstVertex, // Minimum vertex index for vertices used during this call. This is a zero based index relative to BaseVertexIndex. + // The first Vertex in the vertexbuffer that we are currently using for the current batch. + + s_NumVertices, // Number of vertices used during this call. The first vertex is located at index: BaseVertexIndex + MinIndex. + + pPrim->m_FirstIndex, // Index of the first index to use when accesssing the vertex buffer. Beginning at StartIndex to index vertices from the vertex buffer. + + numPrimitives );// Number of primitives to render. The number of vertices used is a function of the primitive count and the primitive type. + } + } + } + + if ( g_pLastVertex ) + { + g_pLastVertex->MarkUsedInRendering(); + } + + if( g_pLastIndex ) + { + g_pLastIndex->MarkUsedInRendering(); + } +} + +//----------------------------------------------------------------------------- +// +// Dynamic mesh implementation +// +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +// constructor, destructor +//----------------------------------------------------------------------------- +CDynamicMeshDX8::CDynamicMeshDX8() : CMeshDX8( "CDynamicMeshDX8" ) +{ + m_nBufferId = 0; + ResetVertexAndIndexCounts(); +} + +CDynamicMeshDX8::~CDynamicMeshDX8() +{ +} + + +//----------------------------------------------------------------------------- +// Initializes the dynamic mesh +//----------------------------------------------------------------------------- +void CDynamicMeshDX8::Init( int nBufferId ) +{ + m_nBufferId = nBufferId; +} + + +//----------------------------------------------------------------------------- +// Resets buffering state +//----------------------------------------------------------------------------- +void CDynamicMeshDX8::ResetVertexAndIndexCounts() +{ + m_TotalVertices = m_TotalIndices = 0; + m_FirstIndex = m_nFirstVertex = -1; + m_HasDrawn = false; +} + + +//----------------------------------------------------------------------------- +// Resets the state in case of a task switch +//----------------------------------------------------------------------------- +void CDynamicMeshDX8::Reset() +{ + m_VertexFormat = 0; + m_pVertexBuffer = 0; + SafeRelease( &m_pIndexBuffer ); + ResetVertexAndIndexCounts(); + + // Force the render state to be updated next time + ResetMeshRenderState(); +} + + +//----------------------------------------------------------------------------- +// Sets the vertex format associated with the dynamic mesh +//----------------------------------------------------------------------------- +void CDynamicMeshDX8::SetVertexFormat( VertexFormat_t format ) +{ + if ( g_pShaderDeviceDx8->IsDeactivated()) + return; + + if ( CompressionType( format ) != VERTEX_COMPRESSION_NONE ) + { + // UNDONE: support compressed dynamic meshes if needed (pro: less VB memory, con: CMeshBuilder gets slower) + Warning( "ERROR: dynamic meshes cannot use compressed vertices!\n" ); + Assert( 0 ); + format &= ~VERTEX_FORMAT_COMPRESSED; + } + + if ((format != m_VertexFormat) || m_VertexOverride || m_IndexOverride) + { + m_VertexFormat = format; + UseVertexBuffer( g_MeshMgr.FindOrCreateVertexBuffer( m_nBufferId, format ) ); + + if ( m_nBufferId == 0 ) + { + UseIndexBuffer( g_MeshMgr.GetDynamicIndexBuffer() ); + } + + m_VertexOverride = m_IndexOverride = false; + } +} + +void CDynamicMeshDX8::OverrideVertexBuffer( CVertexBuffer* pVertexBuffer ) +{ + UseVertexBuffer( pVertexBuffer ); + m_VertexOverride = true; +} + +void CDynamicMeshDX8::OverrideIndexBuffer( CIndexBuffer* pIndexBuffer ) +{ + UseIndexBuffer( pIndexBuffer ); + m_IndexOverride = true; +} + + +//----------------------------------------------------------------------------- +// Do I need to reset the vertex format? +//----------------------------------------------------------------------------- +bool CDynamicMeshDX8::NeedsVertexFormatReset( VertexFormat_t fmt ) const +{ + return m_VertexOverride || m_IndexOverride || (m_VertexFormat != fmt); +} + + + +//----------------------------------------------------------------------------- +// Locks/unlocks the entire mesh +//----------------------------------------------------------------------------- +bool CDynamicMeshDX8::HasEnoughRoom( int nVertexCount, int nIndexCount ) const +{ + if ( g_pShaderDeviceDx8->IsDeactivated() ) + return false; + + Assert( m_pVertexBuffer != NULL ); + + // We need space in both the vertex and index buffer + return m_pVertexBuffer->HasEnoughRoom( nVertexCount ) && + m_pIndexBuffer->HasEnoughRoom( nIndexCount ); +} + + +//----------------------------------------------------------------------------- +// returns the number of indices in the mesh +//----------------------------------------------------------------------------- +int CDynamicMeshDX8::IndexCount( ) const +{ + return m_TotalIndices; +} + + +//----------------------------------------------------------------------------- +// Operation to do pre-lock (only called for buffered meshes) +//----------------------------------------------------------------------------- +void CDynamicMeshDX8::PreLock() +{ + if (m_HasDrawn) + { + // Start again then + ResetVertexAndIndexCounts(); + } +} + + +//----------------------------------------------------------------------------- +// Locks/unlocks the entire mesh +//----------------------------------------------------------------------------- +void CDynamicMeshDX8::LockMesh( int nVertexCount, int nIndexCount, MeshDesc_t& desc ) +{ + tmZoneFiltered( TELEMETRY_LEVEL0, 50, TMZF_NONE, "%s %d %d", __FUNCTION__, nVertexCount, nIndexCount ); + + ShaderUtil()->SyncMatrices(); + + { + tmZoneFiltered( TELEMETRY_LEVEL0, 50, TMZF_NONE, "g_ShaderMutex.Lock" ); + g_ShaderMutex.Lock(); + } + + // Yes, this may well also be called from BufferedMesh but that's ok + { + tmZoneFiltered( TELEMETRY_LEVEL0, 50, TMZF_NONE, "PreLock" ); + PreLock(); + } + + if (m_VertexOverride) + { + nVertexCount = 0; + } + + if (m_IndexOverride) + { + nIndexCount = 0; + } + + { + tmZoneFiltered( TELEMETRY_LEVEL0, 50, TMZF_NONE, "Lock" ); + Lock( nVertexCount, false, *static_cast( &desc ) ); + } + + if (m_nFirstVertex < 0) + { + m_nFirstVertex = desc.m_nFirstVertex; + } + + // When we're using a static index buffer or a flex mesh, the indices assume vertices start at 0 + if ( m_IndexOverride || HasFlexMesh() ) + { + desc.m_nFirstVertex -= m_nFirstVertex; + } + + // Don't add indices for points; DrawIndexedPrimitive not supported for them. + if ( m_Type != MATERIAL_POINTS ) + { + int nFirstIndex; + { + tmZoneFiltered( TELEMETRY_LEVEL0, 50, TMZF_NONE, "Lock nFirstIndex" ); + nFirstIndex = Lock( false, -1, nIndexCount, *static_cast( &desc ) ); + } + if (m_FirstIndex < 0) + { + m_FirstIndex = nFirstIndex; + } + } + else + { + desc.m_pIndices = g_nScratchIndexBuffer; + desc.m_nIndexSize = 0; + } + + CBaseMeshDX8::m_bMeshLocked = true; +} + + +//----------------------------------------------------------------------------- +// Unlocks the mesh +//----------------------------------------------------------------------------- +void CDynamicMeshDX8::UnlockMesh( int nVertexCount, int nIndexCount, MeshDesc_t& desc ) +{ + m_TotalVertices += nVertexCount; + m_TotalIndices += nIndexCount; + + if (DebugTrace()) + { + Spew( nVertexCount, nIndexCount, desc ); + } + + CMeshDX8::UnlockMesh( nVertexCount, nIndexCount, desc ); + + // This is handled in the CMeshDX8::UnlockMesh above. + //CBaseMeshDX8::m_bMeshLocked = false; +} + + +//----------------------------------------------------------------------------- +// Draws it +//----------------------------------------------------------------------------- +void CDynamicMeshDX8::Draw( int nFirstIndex, int nIndexCount ) +{ + if ( !ShaderUtil()->OnDrawMesh( this, nFirstIndex, nIndexCount ) ) + { + MarkAsDrawn(); + return; + } + + VPROF( "CDynamicMeshDX8::Draw" ); + + m_HasDrawn = true; + + if (m_IndexOverride || m_VertexOverride || + ( ( m_TotalVertices > 0 ) && ( m_TotalIndices > 0 || m_Type == MATERIAL_POINTS || m_Type == MATERIAL_INSTANCED_QUADS ) ) ) + { + Assert( !m_IsDrawing ); + + HandleLateCreation( ); + + // only have a non-zero first vertex when we are using static indices + int nFirstVertex = m_VertexOverride ? 0 : m_nFirstVertex; + int actualFirstVertex = m_IndexOverride ? nFirstVertex : 0; + int nVertexOffsetInBytes = HasFlexMesh() ? nFirstVertex * g_MeshMgr.VertexFormatSize( GetVertexFormat() ) : 0; + int baseIndex = m_IndexOverride ? 0 : m_FirstIndex; + + // Overriding with the dynamic index buffer, preserve state! + if ( m_IndexOverride && m_pIndexBuffer == g_MeshMgr.GetDynamicIndexBuffer() ) + { + baseIndex = m_FirstIndex; + } + + VertexFormat_t fmt = m_VertexOverride ? GetVertexFormat() : VERTEX_FORMAT_INVALID; + if ( !SetRenderState( nVertexOffsetInBytes, actualFirstVertex, fmt ) ) + return; + + // Draws a portion of the mesh + int numVertices = m_VertexOverride ? m_pVertexBuffer->VertexCount() : m_TotalVertices; + if ((nFirstIndex != -1) && (nIndexCount != 0)) + { + nFirstIndex += baseIndex; + } + else + { + // by default we draw the whole thing + nFirstIndex = baseIndex; + if( m_IndexOverride ) + { + nIndexCount = m_pIndexBuffer->IndexCount(); + Assert( nIndexCount != 0 ); + } + else + { + nIndexCount = m_TotalIndices; + // Fake out the index count if we're drawing points/instanced-quads + if ( ( m_Type == MATERIAL_POINTS ) || ( m_Type == MATERIAL_INSTANCED_QUADS ) ) + { + nIndexCount = m_TotalVertices; + } + Assert( nIndexCount != 0 ); + } + } + + // Fix up nFirstVertex to indicate the first vertex used in the data + if ( !HasFlexMesh() ) + { + actualFirstVertex = nFirstVertex - actualFirstVertex; + } + + s_FirstVertex = actualFirstVertex; + s_NumVertices = numVertices; + + // Build a primlist with 1 element.. + CPrimList prim; + prim.m_FirstIndex = nFirstIndex; + prim.m_NumIndices = nIndexCount; + Assert( nIndexCount != 0 ); + s_pPrims = &prim; + s_nPrims = 1; + + DrawMesh(); + + s_pPrims = NULL; + } +} + + +//----------------------------------------------------------------------------- +// This is useful when we need to dynamically modify data; just set the +// render state and draw the pass immediately +//----------------------------------------------------------------------------- +void CDynamicMeshDX8::DrawSinglePassImmediately() +{ + if ((m_TotalVertices > 0) || (m_TotalIndices > 0)) + { + Assert( !m_IsDrawing ); + + // Set the render state + if ( SetRenderState( 0, 0 ) ) + { + s_FirstVertex = m_nFirstVertex; + s_NumVertices = m_TotalVertices; + + // Make a temporary PrimList to hold the indices. + CPrimList prim( m_FirstIndex, m_TotalIndices ); + Assert( m_TotalIndices != 0 ); + s_pPrims = &prim; + s_nPrims = 1; + + // Render it + RenderPass(); + } + + // We're done with our data + ResetVertexAndIndexCounts(); + } +} + +//----------------------------------------------------------------------------- +// +// A mesh that stores temporary vertex data in the correct format (for modification) +// +//----------------------------------------------------------------------------- +// Used in rendering sub-parts of the mesh +unsigned int CTempMeshDX8::s_NumIndices; +unsigned int CTempMeshDX8::s_FirstIndex; + +//----------------------------------------------------------------------------- +// constructor, destructor +//----------------------------------------------------------------------------- +CTempMeshDX8::CTempMeshDX8( bool isDynamic ) : m_VertexSize(0xFFFF), m_IsDynamic(isDynamic) +{ +#ifdef DBGFLAG_ASSERT + m_Locked = false; + m_InPass = false; +#endif +} + +CTempMeshDX8::~CTempMeshDX8() +{ +} + +//----------------------------------------------------------------------------- +// Is the temp mesh dynamic? +//----------------------------------------------------------------------------- +bool CTempMeshDX8::IsDynamic() const +{ + return m_IsDynamic; +} + + +//----------------------------------------------------------------------------- +// Sets the vertex format +//----------------------------------------------------------------------------- +void CTempMeshDX8::SetVertexFormat( VertexFormat_t format ) +{ + CBaseMeshDX8::SetVertexFormat(format); + m_VertexSize = g_MeshMgr.VertexFormatSize( format ); +} + +//----------------------------------------------------------------------------- +// returns the # of vertices (static meshes only) +//----------------------------------------------------------------------------- +int CTempMeshDX8::VertexCount() const +{ + return m_VertexSize ? m_VertexData.Count() / m_VertexSize : 0; +} + +//----------------------------------------------------------------------------- +// returns the # of indices +//----------------------------------------------------------------------------- +int CTempMeshDX8::IndexCount( ) const +{ + return m_IndexData.Count(); +} + +void CTempMeshDX8::ModifyBeginEx( bool bReadOnly, int nFirstVertex, int nVertexCount, int nFirstIndex, int nIndexCount, MeshDesc_t& desc ) +{ + Assert( !m_Locked ); + + m_LockedVerts = nVertexCount; + m_LockedIndices = nIndexCount; + + if( nVertexCount > 0 ) + { + int vertexByteOffset = m_VertexSize * nFirstVertex; + + // Lock it baby + unsigned char* pVertexMemory = &m_VertexData[vertexByteOffset]; + + // Compute the vertex index.. + desc.m_nFirstVertex = vertexByteOffset / m_VertexSize; + + // Set up the mesh descriptor + g_MeshMgr.ComputeVertexDescription( pVertexMemory, m_VertexFormat, desc ); + } + else + { + desc.m_nFirstVertex = 0; + // Set up the mesh descriptor + g_MeshMgr.ComputeVertexDescription( 0, 0, desc ); + } + + if (m_Type != MATERIAL_POINTS && nIndexCount > 0 ) + { + desc.m_pIndices = &m_IndexData[nFirstIndex]; + desc.m_nIndexSize = 1; + } + else + { + desc.m_pIndices = g_nScratchIndexBuffer; + desc.m_nIndexSize = 0; + } + +#ifdef DBGFLAG_ASSERT + m_Locked = true; +#endif +} + +void CTempMeshDX8::ModifyBegin( int nFirstVertex, int nVertexCount, int nFirstIndex, int nIndexCount, MeshDesc_t& desc ) +{ + ModifyBeginEx( false, nFirstVertex, nVertexCount, nFirstIndex, nIndexCount, desc ); +} + +void CTempMeshDX8::ModifyEnd( MeshDesc_t& desc ) +{ +#ifdef DBGFLAG_ASSERT + Assert( m_Locked ); + m_Locked = false; +#endif +} + +//----------------------------------------------------------------------------- +// Locks/unlocks the mesh +//----------------------------------------------------------------------------- +void CTempMeshDX8::LockMesh( int nVertexCount, int nIndexCount, MeshDesc_t& desc ) +{ + ShaderUtil()->SyncMatrices(); + + g_ShaderMutex.Lock(); + + Assert( !m_Locked ); + + m_LockedVerts = nVertexCount; + m_LockedIndices = nIndexCount; + + if( nVertexCount > 0 ) + { + int vertexByteOffset = m_VertexData.AddMultipleToTail( m_VertexSize * nVertexCount ); + + // Lock it baby + unsigned char* pVertexMemory = &m_VertexData[vertexByteOffset]; + + // Compute the vertex index.. + desc.m_nFirstVertex = vertexByteOffset / m_VertexSize; + + // Set up the mesh descriptor + g_MeshMgr.ComputeVertexDescription( pVertexMemory, m_VertexFormat, desc ); + } + else + { + desc.m_nFirstVertex = 0; + // Set up the mesh descriptor + g_MeshMgr.ComputeVertexDescription( 0, 0, desc ); + } + + if (m_Type != MATERIAL_POINTS && nIndexCount > 0 ) + { + int nFirstIndex = m_IndexData.AddMultipleToTail( nIndexCount ); + desc.m_pIndices = &m_IndexData[nFirstIndex]; + desc.m_nIndexSize = 1; + } + else + { + desc.m_pIndices = g_nScratchIndexBuffer; + desc.m_nIndexSize = 0; + } + +#ifdef DBGFLAG_ASSERT + m_Locked = true; +#endif + + CBaseMeshDX8::m_bMeshLocked = true; +} + +void CTempMeshDX8::UnlockMesh( int nVertexCount, int nIndexCount, MeshDesc_t& desc ) +{ + Assert( m_Locked ); + + // Remove unused vertices and indices + int verticesToRemove = m_LockedVerts - nVertexCount; + if( verticesToRemove != 0 ) + { + m_VertexData.RemoveMultiple( m_VertexData.Count() - verticesToRemove, verticesToRemove ); + } + + int indicesToRemove = m_LockedIndices - nIndexCount; + if( indicesToRemove != 0 ) + { + m_IndexData.RemoveMultiple( m_IndexData.Count() - indicesToRemove, indicesToRemove ); + } + +#ifdef DBGFLAG_ASSERT + m_Locked = false; +#endif + + CBaseMeshDX8::m_bMeshLocked = false; + + g_ShaderMutex.Unlock(); +} + +//----------------------------------------------------------------------------- +// Sets the primitive type +//----------------------------------------------------------------------------- +void CTempMeshDX8::SetPrimitiveType( MaterialPrimitiveType_t type ) +{ + // FIXME: Support MATERIAL_INSTANCED_QUADS for CTempMeshDX8 (X360 only) + Assert( ( type != MATERIAL_INSTANCED_QUADS ) /* || IsX360() */ ); + m_Type = type; +} + +MaterialPrimitiveType_t CTempMeshDX8::GetPrimitiveType( ) const +{ + return m_Type; +} + +//----------------------------------------------------------------------------- +// Gets the dynamic mesh +//----------------------------------------------------------------------------- +CDynamicMeshDX8* CTempMeshDX8::GetDynamicMesh( ) +{ + return static_cast(g_MeshMgr.GetActualDynamicMesh( m_VertexFormat )); +} + +//----------------------------------------------------------------------------- +// Draws the entire mesh +//----------------------------------------------------------------------------- +void CTempMeshDX8::Draw( int nFirstIndex, int nIndexCount ) +{ + if ( !ShaderUtil()->OnDrawMesh( this, nFirstIndex, nIndexCount ) ) + { + MarkAsDrawn(); + return; + } + + if (m_VertexData.Count() > 0) + { + if ( !g_pShaderDeviceDx8->IsDeactivated() ) + { +#ifdef DRAW_SELECTION + if (!g_bDrawSelection && !ShaderAPI()->IsInSelectionMode()) +#else + if (!ShaderAPI()->IsInSelectionMode()) +#endif + { + s_FirstIndex = nFirstIndex; + s_NumIndices = nIndexCount; + + DrawMesh( ); + + // This assertion fails if a BeginPass() call was not matched by + // a RenderPass() call + Assert(!m_InPass); + } + else + { + TestSelection(); + } + } + + // Clear out the data if this temp mesh is a dynamic one... + if (m_IsDynamic) + { + m_VertexData.RemoveAll(); + m_IndexData.RemoveAll(); + } + } +} + +void CTempMeshDX8::CopyToMeshBuilder( + int iStartVert, // Which vertices to copy. + int nVerts, + int iStartIndex, // Which indices to copy. + int nIndices, + int indexOffset, // This is added to each index. + CMeshBuilder &builder ) +{ + int startOffset = iStartVert * m_VertexSize; + int endOffset = (iStartVert + nVerts) * m_VertexSize; + Assert( startOffset >= 0 && startOffset <= m_VertexData.Count() ); + Assert( endOffset >= 0 && endOffset <= m_VertexData.Count() && endOffset >= startOffset ); + if ( endOffset > startOffset ) + { + // FIXME: make this a method of CMeshBuilder (so the 'Position' pointer accessor can be removed) + // make sure it takes a VertexFormat_t parameter for src/dest match validation + memcpy( (void*)builder.Position(), &m_VertexData[startOffset], endOffset - startOffset ); + builder.AdvanceVertices( nVerts ); + } + + for ( int i = 0; i < nIndices; ++i ) + { + builder.Index( m_IndexData[iStartIndex+i] + indexOffset ); + builder.AdvanceIndex(); + } +} + +//----------------------------------------------------------------------------- +// Selection mode helper functions +//----------------------------------------------------------------------------- +static void ComputeModelToView( D3DXMATRIX& modelToView ) +{ + // Get the modelview matrix... + D3DXMATRIX world, view; + ShaderAPI()->GetMatrix( MATERIAL_MODEL, (float*)&world ); + ShaderAPI()->GetMatrix( MATERIAL_VIEW, (float*)&view ); + D3DXMatrixMultiply( &modelToView, &world, &view ); +} + +static float ComputeCullFactor( ) +{ + D3DCULL cullMode = ShaderAPI()->GetCullMode(); + + float cullFactor; + switch(cullMode) + { + case D3DCULL_CCW: + cullFactor = -1.0f; + break; + + case D3DCULL_CW: + cullFactor = 1.0f; + break; + + default: + cullFactor = 0.0f; + break; + }; + + return cullFactor; +} + +//----------------------------------------------------------------------------- +// Clip to viewport +//----------------------------------------------------------------------------- +static int g_NumClipVerts; +static D3DXVECTOR3 g_ClipVerts[16]; + +static bool PointInsidePlane( D3DXVECTOR3* pVert, int normalInd, float val, bool nearClip ) +{ + if ((val > 0) || nearClip) + return (val - (*pVert)[normalInd] >= 0); + else + return ((*pVert)[normalInd] - val >= 0); +} + +static void IntersectPlane( D3DXVECTOR3* pStart, D3DXVECTOR3* pEnd, + int normalInd, float val, D3DXVECTOR3* pOutVert ) +{ + D3DXVECTOR3 dir; + D3DXVec3Subtract( &dir, pEnd, pStart ); + Assert( dir[normalInd] != 0.0f ); + float t = (val - (*pStart)[normalInd]) / dir[normalInd]; + pOutVert->x = pStart->x + dir.x * t; + pOutVert->y = pStart->y + dir.y * t; + pOutVert->z = pStart->z + dir.z * t; + + // Avoid any precision problems. + (*pOutVert)[normalInd] = val; +} + +static int ClipTriangleAgainstPlane( D3DXVECTOR3** ppVert, int nVertexCount, + D3DXVECTOR3** ppOutVert, int normalInd, float val, bool nearClip = false ) +{ + // Ye Olde Sutherland-Hodgman clipping algorithm + int numOutVerts = 0; + D3DXVECTOR3* pStart = ppVert[nVertexCount-1]; + bool startInside = PointInsidePlane( pStart, normalInd, val, nearClip ); + for (int i = 0; i < nVertexCount; ++i) + { + D3DXVECTOR3* pEnd = ppVert[i]; + bool endInside = PointInsidePlane( pEnd, normalInd, val, nearClip ); + if (endInside) + { + if (!startInside) + { + IntersectPlane( pStart, pEnd, normalInd, val, &g_ClipVerts[g_NumClipVerts] ); + ppOutVert[numOutVerts++] = &g_ClipVerts[g_NumClipVerts++]; + } + ppOutVert[numOutVerts++] = pEnd; + } + else + { + if (startInside) + { + IntersectPlane( pStart, pEnd, normalInd, val, &g_ClipVerts[g_NumClipVerts] ); + ppOutVert[numOutVerts++] = &g_ClipVerts[g_NumClipVerts++]; + } + } + pStart = pEnd; + startInside = endInside; + } + + return numOutVerts; +} + +void CTempMeshDX8::ClipTriangle( D3DXVECTOR3** ppVert, float zNear, D3DXMATRIX& projection ) +{ + int i; + int nVertexCount = 3; + D3DXVECTOR3* ppClipVert1[10]; + D3DXVECTOR3* ppClipVert2[10]; + + g_NumClipVerts = 0; + + // Clip against the near plane in view space to prevent negative w. + // Clip against each plane + nVertexCount = ClipTriangleAgainstPlane( ppVert, nVertexCount, ppClipVert1, 2, zNear, true ); + if (nVertexCount < 3) + return; + + // Sucks that I have to do this, but I have to clip near plane in view space + // Clipping in projection space is screwy when w < 0 + // Transform the clipped points into projection space + Assert( g_NumClipVerts <= 2 ); + for (i = 0; i < nVertexCount; ++i) + { + if (ppClipVert1[i] == &g_ClipVerts[0]) + { + D3DXVec3TransformCoord( &g_ClipVerts[0], ppClipVert1[i], &projection ); + } + else if (ppClipVert1[i] == &g_ClipVerts[1]) + { + D3DXVec3TransformCoord( &g_ClipVerts[1], ppClipVert1[i], &projection ); + } + else + { + D3DXVec3TransformCoord( &g_ClipVerts[g_NumClipVerts], ppClipVert1[i], &projection ); + ppClipVert1[i] = &g_ClipVerts[g_NumClipVerts]; + ++g_NumClipVerts; + } + } + + nVertexCount = ClipTriangleAgainstPlane( ppClipVert1, nVertexCount, ppClipVert2, 2, 1.0f ); + if (nVertexCount < 3) + return; + + nVertexCount = ClipTriangleAgainstPlane( ppClipVert2, nVertexCount, ppClipVert1, 0, 1.0f ); + if (nVertexCount < 3) + return; + + nVertexCount = ClipTriangleAgainstPlane( ppClipVert1, nVertexCount, ppClipVert2, 0, -1.0f ); + if (nVertexCount < 3) + return; + + nVertexCount = ClipTriangleAgainstPlane( ppClipVert2, nVertexCount, ppClipVert1, 1, 1.0f ); + if (nVertexCount < 3) + return; + + nVertexCount = ClipTriangleAgainstPlane( ppClipVert1, nVertexCount, ppClipVert2, 1, -1.0f ); + if (nVertexCount < 3) + return; + +#ifdef DRAW_SELECTION + if( 1 || g_bDrawSelection ) + { + srand( *(int*)(&ppClipVert2[0]->x) ); + unsigned char r = (unsigned char)(rand() * 191.0f / VALVE_RAND_MAX) + 64; + unsigned char g = (unsigned char)(rand() * 191.0f / VALVE_RAND_MAX) + 64; + unsigned char b = (unsigned char)(rand() * 191.0f / VALVE_RAND_MAX) + 64; + + ShaderAPI()->SetupSelectionModeVisualizationState(); + + CMeshBuilder* pMeshBuilder = ShaderAPI()->GetVertexModifyBuilder(); + IMesh* pMesh = GetDynamicMesh(); + pMeshBuilder->Begin( pMesh, MATERIAL_POLYGON, nVertexCount ); + + for ( i = 0; i < nVertexCount; ++i) + { + pMeshBuilder->Position3fv( *ppClipVert2[i] ); + pMeshBuilder->Color3ub( r, g, b ); + pMeshBuilder->AdvanceVertex(); + } + + pMeshBuilder->End(); + pMesh->Draw(); + + pMeshBuilder->Begin( pMesh, MATERIAL_LINE_LOOP, nVertexCount ); + + for ( i = 0; i < nVertexCount; ++i) + { + pMeshBuilder->Position3fv( *ppClipVert2[i] ); + pMeshBuilder->Color3ub( 255, 255, 255 ); + pMeshBuilder->AdvanceVertex(); + } + + pMeshBuilder->End(); + pMesh->Draw(); + } +#endif + + // Compute closest and furthest verts + float minz = ppClipVert2[0]->z; + float maxz = ppClipVert2[0]->z; + for ( i = 1; i < nVertexCount; ++i ) + { + if (ppClipVert2[i]->z < minz) + minz = ppClipVert2[i]->z; + else if (ppClipVert2[i]->z > maxz) + maxz = ppClipVert2[i]->z; + } + + ShaderAPI()->RegisterSelectionHit( minz, maxz ); +} + +//----------------------------------------------------------------------------- +// Selection mode +//----------------------------------------------------------------------------- +void CTempMeshDX8::TestSelection() +{ + // Note that this doesn't take into account any vertex modification + // done in a vertex shader. Also it doesn't take into account any clipping + // done in hardware + + // Blow off points and lines; they don't matter + if ((m_Type != MATERIAL_TRIANGLES) && (m_Type != MATERIAL_TRIANGLE_STRIP)) + return; + + D3DXMATRIX modelToView, projection; + ComputeModelToView( modelToView ); + ShaderAPI()->GetMatrix( MATERIAL_PROJECTION, (float*)&projection ); + float zNear = -projection.m[3][2] / projection.m[2][2]; + + D3DXVECTOR3* pPos[3]; + D3DXVECTOR3 edge[2]; + D3DXVECTOR3 normal; + + int numTriangles; + if (m_Type == MATERIAL_TRIANGLES) + numTriangles = m_IndexData.Count() / 3; + else + numTriangles = m_IndexData.Count() - 2; + + float cullFactor = ComputeCullFactor(); + + // Makes the lovely loop simpler + if (m_Type == MATERIAL_TRIANGLE_STRIP) + cullFactor *= -1.0f; + + // We'll need some temporary memory to tell us if we're transformed the vert + int nVertexCount = m_VertexData.Count() / m_VertexSize; + static CUtlVector< unsigned char > transformedVert; + int transformedVertSize = (nVertexCount + 7) >> 3; + transformedVert.RemoveAll(); + transformedVert.EnsureCapacity( transformedVertSize ); + transformedVert.AddMultipleToTail( transformedVertSize ); + memset( transformedVert.Base(), 0, transformedVertSize ); + + int indexPos; + for (int i = 0; i < numTriangles; ++i) + { + // Get the three indices + if (m_Type == MATERIAL_TRIANGLES) + { + indexPos = i * 3; + } + else + { + Assert( m_Type == MATERIAL_TRIANGLE_STRIP ); + cullFactor *= -1.0f; + indexPos = i; + } + + // BAH. Gotta clip to the near clip plane in view space to prevent + // negative w coords; negative coords throw off the projection-space clipper. + + // Get the three positions in view space + int inFrontIdx = -1; + for (int j = 0; j < 3; ++j) + { + int index = m_IndexData[indexPos]; + D3DXVECTOR3* pPosition = (D3DXVECTOR3*)&m_VertexData[index * m_VertexSize]; + if ((transformedVert[index >> 3] & (1 << (index & 0x7))) == 0) + { + D3DXVec3TransformCoord( pPosition, pPosition, &modelToView ); + transformedVert[index >> 3] |= (1 << (index & 0x7)); + } + + pPos[j] = pPosition; + if (pPos[j]->z < 0.0f) + inFrontIdx = j; + ++indexPos; + } + + // all points are behind the camera + if (inFrontIdx < 0) + continue; + + // backface cull.... + D3DXVec3Subtract( &edge[0], pPos[1], pPos[0] ); + D3DXVec3Subtract( &edge[1], pPos[2], pPos[0] ); + D3DXVec3Cross( &normal, &edge[0], &edge[1] ); + float dot = D3DXVec3Dot( &normal, pPos[inFrontIdx] ); + if (dot * cullFactor > 0.0f) + continue; + + // Clip to viewport + ClipTriangle( pPos, zNear, projection ); + } +} + +//----------------------------------------------------------------------------- +// Begins a render pass +//----------------------------------------------------------------------------- +void CTempMeshDX8::BeginPass( ) +{ + Assert( !m_InPass ); + +#ifdef DBGFLAG_ASSERT + m_InPass = true; +#endif + + CMeshBuilder* pMeshBuilder = ShaderAPI()->GetVertexModifyBuilder(); + + CDynamicMeshDX8* pMesh = GetDynamicMesh( ); + + int nIndexCount; + int nFirstIndex; + if ((s_FirstIndex == -1) && (s_NumIndices == 0)) + { + nIndexCount = m_IndexData.Count(); + nFirstIndex = 0; + } + else + { + nIndexCount = s_NumIndices; + nFirstIndex = s_FirstIndex; + } + + int i; + int nVertexCount = m_VertexData.Count() / m_VertexSize; + pMeshBuilder->Begin( pMesh, m_Type, nVertexCount, nIndexCount ); + + // Copy in the vertex data... + // Note that since we pad the vertices, it's faster for us to simply + // copy the fields we're using... + Assert( pMeshBuilder->BaseVertexData() ); + memcpy( pMeshBuilder->BaseVertexData(), m_VertexData.Base(), m_VertexData.Count() ); + pMeshBuilder->AdvanceVertices( m_VertexData.Count() / m_VertexSize ); + + for ( i = 0; i < nIndexCount; ++i ) + { + pMeshBuilder->Index( m_IndexData[nFirstIndex+i] ); + pMeshBuilder->AdvanceIndex(); + } + + // NOTE: The client is expected to modify the data after this call is made + pMeshBuilder->Reset(); +} + +//----------------------------------------------------------------------------- +// Draws a single pass +//----------------------------------------------------------------------------- +void CTempMeshDX8::RenderPass() +{ + Assert( m_InPass ); + +#ifdef DBGFLAG_ASSERT + m_InPass = false; +#endif + + // Have the shader API modify the vertex data as it needs + // This vertex data is modified based on state set by the material + ShaderAPI()->ModifyVertexData( ); + + // Done building the mesh + ShaderAPI()->GetVertexModifyBuilder()->End(); + + // Have the dynamic mesh render a single pass... + GetDynamicMesh()->DrawSinglePassImmediately(); +} + +//----------------------------------------------------------------------------- +// +// Buffered mesh implementation +// +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +// constructor, destructor +//----------------------------------------------------------------------------- + +CBufferedMeshDX8::CBufferedMeshDX8() : m_IsFlushing(false), m_WasRendered(true) +{ + m_pMesh = NULL; +#ifdef DEBUG_BUFFERED_STATE + m_BufferedStateSet = false; +#endif +} + +CBufferedMeshDX8::~CBufferedMeshDX8() +{ +} + + +//----------------------------------------------------------------------------- +// Sets the mesh +//----------------------------------------------------------------------------- +void CBufferedMeshDX8::SetMesh( CBaseMeshDX8* pMesh ) +{ + if (m_pMesh != pMesh) + { + ShaderAPI()->FlushBufferedPrimitives(); + m_pMesh = pMesh; + } +} + + +//----------------------------------------------------------------------------- +// Spews the mesh data +//----------------------------------------------------------------------------- +void CBufferedMeshDX8::Spew( int nVertexCount, int nIndexCount, const MeshDesc_t &spewDesc ) +{ + if ( m_pMesh ) + { + m_pMesh->Spew( nVertexCount, nIndexCount, spewDesc ); + } +} + + +//----------------------------------------------------------------------------- +// Sets the material +//----------------------------------------------------------------------------- +void CBufferedMeshDX8::SetVertexFormat( VertexFormat_t format ) +{ + Assert( m_pMesh ); + if (m_pMesh->NeedsVertexFormatReset(format)) + { + ShaderAPI()->FlushBufferedPrimitives(); + m_pMesh->SetVertexFormat( format ); + } +} + +void CBufferedMeshDX8::SetMorphFormat( MorphFormat_t format ) +{ + Assert( m_pMesh ); + m_pMesh->SetMorphFormat( format ); +} + +VertexFormat_t CBufferedMeshDX8::GetVertexFormat( ) const +{ + Assert( m_pMesh ); + return m_pMesh->GetVertexFormat(); +} + +void CBufferedMeshDX8::SetMaterial( IMaterial* pMaterial ) +{ +#if _DEBUG + Assert( m_pMesh ); + m_pMesh->SetMaterial( pMaterial ); +#endif +} + +void CBufferedMeshDX8::ValidateData( int nVertexCount, int nIndexCount, const MeshDesc_t & spewDesc ) +{ +#if _DEBUG + Assert( m_pMesh ); + m_pMesh->ValidateData( nVertexCount, nIndexCount, spewDesc ); +#endif +} + + +//----------------------------------------------------------------------------- +// Sets the flex mesh to render with this mesh +//----------------------------------------------------------------------------- +void CBufferedMeshDX8::SetFlexMesh( IMesh *pMesh, int nVertexOffsetInBytes ) +{ + // FIXME: Probably are situations where we don't need to flush, + // but this is going to look different in a very short while, so I'm not going to bother + ShaderAPI()->FlushBufferedPrimitives(); + m_pMesh->SetFlexMesh( pMesh, nVertexOffsetInBytes ); +} + + +//----------------------------------------------------------------------------- +// checks to see if it was rendered.. +//----------------------------------------------------------------------------- +void CBufferedMeshDX8::ResetRendered() +{ + m_WasRendered = false; +} + +bool CBufferedMeshDX8::WasNotRendered() const +{ + return !m_WasRendered; +} + + +//----------------------------------------------------------------------------- +// "Draws" it +//----------------------------------------------------------------------------- +void CBufferedMeshDX8::Draw( int nFirstIndex, int nIndexCount ) +{ + if ( !ShaderUtil()->OnDrawMesh( this, nFirstIndex, nIndexCount ) ) + { + m_WasRendered = true; + MarkAsDrawn(); + return; + } + + Assert( !m_IsFlushing && !m_WasRendered ); + + // Gotta draw all of the buffered mesh + Assert( (nFirstIndex == -1) && (nIndexCount == 0) ); + + // No need to draw it more than once... + m_WasRendered = true; + + // We've got something to flush + m_FlushNeeded = true; + + // Less than 0 indices indicates we were using a standard buffer + if ( m_pMesh->HasFlexMesh() || !ShaderUtil()->GetConfig().bBufferPrimitives ) + { + ShaderAPI()->FlushBufferedPrimitives(); + } +} + + +//----------------------------------------------------------------------------- +// Sets the primitive mode +//----------------------------------------------------------------------------- + +void CBufferedMeshDX8::SetPrimitiveType( MaterialPrimitiveType_t type ) +{ + Assert( IsX360() || ( type != MATERIAL_INSTANCED_QUADS ) ); + Assert( type != MATERIAL_HETEROGENOUS ); + + if (type != GetPrimitiveType()) + { + ShaderAPI()->FlushBufferedPrimitives(); + m_pMesh->SetPrimitiveType(type); + } +} + +MaterialPrimitiveType_t CBufferedMeshDX8::GetPrimitiveType( ) const +{ + return m_pMesh->GetPrimitiveType(); +} + + +//----------------------------------------------------------------------------- +// Locks/unlocks the entire mesh +//----------------------------------------------------------------------------- +void CBufferedMeshDX8::LockMesh( int nVertexCount, int nIndexCount, MeshDesc_t& desc ) +{ + ShaderUtil()->SyncMatrices(); + + Assert( m_pMesh ); + Assert( m_WasRendered ); + + // Do some pre-lock processing + m_pMesh->PreLock(); + + // for tristrips, gotta make degenerate ones... + m_ExtraIndices = 0; + bool tristripFixup = (m_pMesh->IndexCount() != 0) && + (m_pMesh->GetPrimitiveType() == MATERIAL_TRIANGLE_STRIP); + if (tristripFixup) + { + m_ExtraIndices = (m_pMesh->IndexCount() & 0x1) != 0 ? 3 : 2; + nIndexCount += m_ExtraIndices; + } + + // Flush if we gotta + if (!m_pMesh->HasEnoughRoom(nVertexCount, nIndexCount)) + { + ShaderAPI()->FlushBufferedPrimitives(); + } + + m_pMesh->LockMesh( nVertexCount, nIndexCount, desc ); + +// This is taken care of in the function above. +// CBaseMeshDX8::m_bMeshLocked = true; + + // Deal with fixing up the tristrip.. + if ( tristripFixup && desc.m_nIndexSize ) + { + char buf[32]; + if (DebugTrace()) + { + if (m_ExtraIndices == 3) + sprintf(buf,"Link Index: %d %d\n", m_LastIndex, m_LastIndex); + else + sprintf(buf,"Link Index: %d\n", m_LastIndex); + Plat_DebugString(buf); + } + *desc.m_pIndices++ = m_LastIndex; + if (m_ExtraIndices == 3) + { + *desc.m_pIndices++ = m_LastIndex; + } + + // Leave room for the last padding index + ++desc.m_pIndices; + } + + m_WasRendered = false; + +#ifdef DEBUG_BUFFERED_MESHES + if (m_BufferedStateSet) + { + BufferedState_t compare; + ShaderAPI()->GetBufferedState( compare ); + Assert( !memcmp( &compare, &m_BufferedState, sizeof(compare) ) ); + } + else + { + ShaderAPI()->GetBufferedState( m_BufferedState ); + m_BufferedStateSet = true; + } +#endif +} + + +//----------------------------------------------------------------------------- +// Locks/unlocks the entire mesh +//----------------------------------------------------------------------------- +void CBufferedMeshDX8::UnlockMesh( int nVertexCount, int nIndexCount, MeshDesc_t& desc ) +{ + Assert( m_pMesh ); + + // Gotta fix up the first index to batch strips reasonably + if ((m_pMesh->GetPrimitiveType() == MATERIAL_TRIANGLE_STRIP) && desc.m_nIndexSize ) + { + if (m_ExtraIndices > 0) + { + *(desc.m_pIndices - 1) = *desc.m_pIndices; + + if (DebugTrace()) + { + char buf[32]; + sprintf(buf,"Link Index: %d\n", *desc.m_pIndices); + Plat_DebugString(buf); + } + } + + // Remember the last index for next time + m_LastIndex = desc.m_pIndices[nIndexCount - 1]; + + nIndexCount += m_ExtraIndices; + } + + m_pMesh->UnlockMesh( nVertexCount, nIndexCount, desc ); + +// This is taken care of in the function above. +// CBaseMeshDX8::m_bMeshLocked = false; +} + + +//----------------------------------------------------------------------------- +// Renders a pass +//----------------------------------------------------------------------------- + +void CBufferedMeshDX8::RenderPass() +{ + // this should never be called! + Assert(0); +} + +//----------------------------------------------------------------------------- +// Flushes queued data +//----------------------------------------------------------------------------- + +void CBufferedMeshDX8::Flush( ) +{ + // If you are hitting this assert you are causing a flush between a + // meshbuilder begin/end and you are more than likely losing rendering data. + AssertOnce( !CBaseMeshDX8::m_bMeshLocked ); + + if ( m_pMesh && !m_IsFlushing && m_FlushNeeded ) + { + VPROF( "CBufferedMeshDX8::Flush" ); + +#ifdef DEBUG_BUFFERED_MESHES + if( m_BufferedStateSet ) + { + BufferedState_t compare; + ShaderAPI()->GetBufferedState( compare ); + Assert( !memcmp( &compare, &m_BufferedState, sizeof(compare) ) ); + m_BufferedStateSet = false; + } +#endif + + m_IsFlushing = true; + + // Actually draws the data using the mesh's material + static_cast(m_pMesh)->Draw(); + + m_IsFlushing = false; + m_FlushNeeded = false; + + m_pMesh->SetFlexMesh( NULL, 0 ); + } +} + +//----------------------------------------------------------------------------- +// +// Mesh manager implementation +// +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +// Constructor, destructor +//----------------------------------------------------------------------------- +CMeshMgr::CMeshMgr() : + m_pDynamicIndexBuffer(0), + m_DynamicTempMesh(true), + m_pVertexIDBuffer(0), + m_pCurrentVertexBuffer( NULL ), + m_CurrentVertexFormat( 0 ), + m_pCurrentIndexBuffer( NULL ), + m_DynamicIndexBuffer( SHADER_BUFFER_TYPE_DYNAMIC, MATERIAL_INDEX_FORMAT_16BIT, INDEX_BUFFER_SIZE, "dynamic" ), + m_DynamicVertexBuffer( SHADER_BUFFER_TYPE_DYNAMIC, VERTEX_FORMAT_UNKNOWN, DYNAMIC_VERTEX_BUFFER_MEMORY, "dynamic" ) +{ + m_bUseFatVertices = false; + m_nIndexBufferOffset = 0; + memset( m_pVertexBufferOffset, 0, sizeof(m_pVertexBufferOffset) ); + memset( m_pCurrentVertexStride, 0, sizeof(m_pCurrentVertexStride) ); + memset( m_pFirstVertex, 0, sizeof(m_pFirstVertex) ); + memset( m_pVertexCount, 0, sizeof(m_pVertexCount) ); + m_nUnusedVertexFields = 0; + m_nUnusedTextureCoords = 0; + m_pZeroVertexBuffer = NULL; +} + +CMeshMgr::~CMeshMgr() +{ +} + + +//----------------------------------------------------------------------------- +// Initialize, shutdown +//----------------------------------------------------------------------------- +void CMeshMgr::Init() +{ + m_DynamicMesh.Init( 0 ); + m_DynamicFlexMesh.Init( 1 ); + + CreateDynamicIndexBuffer(); + + // If we're running in vs3.0, allocate a vertexID buffer + CreateVertexIDBuffer(); + + CreateZeroVertexBuffer(); + + m_BufferedMode = !IsX360(); +} + +void CMeshMgr::Shutdown() +{ + CleanUp(); +} + + +//----------------------------------------------------------------------------- +// Task switch... +//----------------------------------------------------------------------------- +void CMeshMgr::ReleaseBuffers() +{ + if ( IsPC() && mat_debugalttab.GetBool() ) + { + Warning( "mat_debugalttab: CMeshMgr::ReleaseBuffers\n" ); + } + + CleanUp(); + m_DynamicMesh.Reset( ); + m_DynamicFlexMesh.Reset( ); +} + +void CMeshMgr::RestoreBuffers() +{ + if ( IsPC() && mat_debugalttab.GetBool() ) + { + Warning( "mat_debugalttab: CMeshMgr::RestoreBuffers\n" ); + } + Init(); +} + + +//----------------------------------------------------------------------------- +// Cleans up vertex and index buffers +//----------------------------------------------------------------------------- +void CMeshMgr::CleanUp() +{ + DestroyDynamicIndexBuffer(); + + DestroyVertexBuffers(); + + // If we're running in vs3.0, allocate a vertexID buffer + DestroyVertexIDBuffer(); + DestroyZeroVertexBuffer(); +} + +//----------------------------------------------------------------------------- +// Fills a vertexID buffer +//----------------------------------------------------------------------------- +void CMeshMgr::FillVertexIDBuffer( CVertexBuffer *pVertexIDBuffer, int nCount ) +{ + if ( IsX360() ) + return; + + // Fill the buffer with the values 0->(nCount-1) + int nBaseVertexIndex = 0; + float *pBuffer = (float*)pVertexIDBuffer->Lock( nCount, nBaseVertexIndex ); + for ( int i = 0; i < nCount; ++i ) + { + *pBuffer++ = (float)i; + } + pVertexIDBuffer->Unlock( nCount ); +} + +//----------------------------------------------------------------------------- +// Creates, destroys the dynamic index buffer +//----------------------------------------------------------------------------- +void CMeshMgr::CreateDynamicIndexBuffer() +{ + DestroyDynamicIndexBuffer(); + SafeAssign( &m_pDynamicIndexBuffer, new CIndexBuffer( Dx9Device(), INDEX_BUFFER_SIZE, ShaderAPI()->UsingSoftwareVertexProcessing(), true ) ); +} + +void CMeshMgr::DestroyDynamicIndexBuffer() +{ + SafeRelease( &m_pDynamicIndexBuffer ); +} + +//----------------------------------------------------------------------------- +// Creates, destroys the vertexID buffer +//----------------------------------------------------------------------------- +void CMeshMgr::CreateVertexIDBuffer() +{ + if ( IsX360() ) + return; + + DestroyVertexIDBuffer(); + + // Track mesh allocations + g_VBAllocTracker->TrackMeshAllocations( "CreateVertexIDBuffer" ); + if ( g_pHardwareConfig->HasFastVertexTextures() ) + { + m_pVertexIDBuffer = new CVertexBuffer( Dx9Device(), 0, 0, sizeof(float), + VERTEX_BUFFER_SIZE, TEXTURE_GROUP_STATIC_VERTEX_BUFFER_OTHER, ShaderAPI()->UsingSoftwareVertexProcessing() ); + FillVertexIDBuffer( m_pVertexIDBuffer, VERTEX_BUFFER_SIZE ); + } + g_VBAllocTracker->TrackMeshAllocations( NULL ); +} + +void CMeshMgr::CreateZeroVertexBuffer() +{ + if ( !m_pZeroVertexBuffer ) + { + // In GL glVertexAttribPointer() doesn't support strides of 0, so we need to allocate a dummy vertex buffer large enough to handle 16-bit indices with a stride of 4 byte per vertex, plus a bit more for safety (in case basevertexindex is > 0). + // We could also try just disabling any vertex attribs that fetch from stream 2 and need 0's, but AMD reports this could hit a slow path in the driver. Argh. + uint nBufSize = IsOpenGL() ? ( 65536 * 2 * 4 ) : 4096; + HRESULT hr = Dx9Device()->CreateVertexBuffer( nBufSize, D3DUSAGE_WRITEONLY, 0, D3DPOOL_DEFAULT, &m_pZeroVertexBuffer, NULL ); + if ( !FAILED( hr ) ) + { + void *pData = NULL; + m_pZeroVertexBuffer->Lock( 0, nBufSize, &pData, D3DLOCK_NOSYSLOCK ); + if ( pData ) + { + V_memset( pData, 0, nBufSize ); + m_pZeroVertexBuffer->Unlock(); + } + } + } +} + +void CMeshMgr::DestroyZeroVertexBuffer() +{ + if ( m_pZeroVertexBuffer ) + { + m_pZeroVertexBuffer->Release(); + m_pZeroVertexBuffer = NULL; + } +} + +void CMeshMgr::DestroyVertexIDBuffer() +{ + if ( m_pVertexIDBuffer ) + { + delete m_pVertexIDBuffer; + m_pVertexIDBuffer = NULL; + } +} + +CVertexBuffer *CMeshMgr::GetVertexIDBuffer( ) +{ + return m_pVertexIDBuffer; +} + + +//----------------------------------------------------------------------------- +// Unused vertex fields +//----------------------------------------------------------------------------- +void CMeshMgr::MarkUnusedVertexFields( unsigned int nFlags, int nTexCoordCount, bool *pUnusedTexCoords ) +{ + m_nUnusedVertexFields = nFlags; + m_nUnusedTextureCoords = 0; + for ( int i = 0; i < nTexCoordCount; ++i ) + { + if ( pUnusedTexCoords[i] ) + { + m_nUnusedTextureCoords |= ( 1 << i ); + } + } +} + + +//----------------------------------------------------------------------------- +// Is the mesh dynamic? +//----------------------------------------------------------------------------- +bool CMeshMgr::IsDynamicMesh( IMesh* pMesh ) const +{ + return ( pMesh == &m_DynamicMesh ) || ( pMesh == &m_DynamicFlexMesh ); +} + +bool CMeshMgr::IsBufferedDynamicMesh( IMesh* pMesh ) const +{ + return ( pMesh == &m_BufferedMesh ); +} + +bool CMeshMgr::IsDynamicVertexBuffer( IVertexBuffer *pVertexBuffer ) const +{ + return ( pVertexBuffer == &m_DynamicVertexBuffer ); +} + +bool CMeshMgr::IsDynamicIndexBuffer( IIndexBuffer *pIndexBuffer ) const +{ + return ( pIndexBuffer == &m_DynamicIndexBuffer ); +} + +//----------------------------------------------------------------------------- +// Discards the dynamic vertex and index buffer +//----------------------------------------------------------------------------- +void CMeshMgr::DiscardVertexBuffers() +{ + VPROF_BUDGET( "CMeshMgr::DiscardVertexBuffers", VPROF_BUDGETGROUP_SWAP_BUFFERS ); + // This shouldn't be necessary, but it seems to be on GeForce 2 + // It helps when running WC and the engine simultaneously. + ResetMeshRenderState(); + + if ( !g_pShaderDeviceDx8->IsDeactivated() ) + { + for (int i = m_DynamicVertexBuffers.Count(); --i >= 0; ) + { + m_DynamicVertexBuffers[i].m_pBuffer->FlushAtFrameStart(); + } + m_pDynamicIndexBuffer->FlushAtFrameStart(); + } +} + + +//----------------------------------------------------------------------------- +// Releases all dynamic vertex buffers +//----------------------------------------------------------------------------- +void CMeshMgr::DestroyVertexBuffers() +{ + // Necessary for cleanup + RECORD_COMMAND( DX8_SET_STREAM_SOURCE, 4 ); + RECORD_INT( -1 ); + RECORD_INT( 0 ); + RECORD_INT( 0 ); + RECORD_INT( 0 ); + D3DSetStreamSource( 0, 0, 0, 0 ); + + RECORD_COMMAND( DX8_SET_STREAM_SOURCE, 4 ); + RECORD_INT( -1 ); + RECORD_INT( 1 ); + RECORD_INT( 0 ); + RECORD_INT( 0 ); + D3DSetStreamSource( 1, 0, 0, 0 ); + + RECORD_COMMAND( DX8_SET_STREAM_SOURCE, 4 ); + RECORD_INT( -1 ); + RECORD_INT( 2 ); + RECORD_INT( 0 ); + RECORD_INT( 0 ); + D3DSetStreamSource( 2, 0, 0, 0 ); + +#ifndef _X360 + RECORD_COMMAND( DX8_SET_STREAM_SOURCE, 4 ); + RECORD_INT( -1 ); + RECORD_INT( 3 ); + RECORD_INT( 0 ); + RECORD_INT( 0 ); + D3DSetStreamSource( 3, 0, 0, 0 ); +#endif + + for (int i = m_DynamicVertexBuffers.Count(); --i >= 0; ) + { + if (m_DynamicVertexBuffers[i].m_pBuffer) + { + delete m_DynamicVertexBuffers[i].m_pBuffer; + } + } + m_DynamicVertexBuffers.RemoveAll(); + m_DynamicMesh.Reset(); + m_DynamicFlexMesh.Reset(); +} + + +//----------------------------------------------------------------------------- +// Flushes the dynamic mesh +//----------------------------------------------------------------------------- +void CMeshMgr::Flush() +{ + if ( IsPC() ) + { + m_BufferedMesh.HandleLateCreation(); + m_BufferedMesh.Flush(); + } +} + + +//----------------------------------------------------------------------------- +// Creates, destroys static meshes +//----------------------------------------------------------------------------- +IMesh* CMeshMgr::CreateStaticMesh( VertexFormat_t format, const char *pTextureBudgetGroup, IMaterial * pMaterial ) +{ + // FIXME: Use a fixed-size allocator + CMeshDX8* pNewMesh = new CMeshDX8( pTextureBudgetGroup ); + pNewMesh->SetVertexFormat( format ); + if ( pMaterial != NULL ) + { + pNewMesh->SetMorphFormat( pMaterial->GetMorphFormat() ); + pNewMesh->SetMaterial( pMaterial ); + } + return pNewMesh; +} + +void CMeshMgr::DestroyStaticMesh( IMesh* pMesh ) +{ + // Don't destroy the dynamic mesh! + Assert( !IsDynamicMesh( pMesh ) ); + CBaseMeshDX8* pMeshImp = static_cast(pMesh); + if (pMeshImp) + { + delete pMeshImp; + } +} + +//----------------------------------------------------------------------------- +// Gets at the *real* dynamic mesh +//----------------------------------------------------------------------------- +IMesh* CMeshMgr::GetActualDynamicMesh( VertexFormat_t format ) +{ + m_DynamicMesh.SetVertexFormat( format ); + return &m_DynamicMesh; +} + +//----------------------------------------------------------------------------- +// Copy a static mesh index buffer to a dynamic mesh index buffer +//----------------------------------------------------------------------------- +void CMeshMgr::CopyStaticMeshIndexBufferToTempMeshIndexBuffer( CTempMeshDX8 *pDstIndexMesh, + CMeshDX8 *pSrcIndexMesh ) +{ + tmZoneFiltered( TELEMETRY_LEVEL0, 50, TMZF_NONE, "%s", __FUNCTION__ ); + + Assert( !pSrcIndexMesh->IsDynamic() ); + int nIndexCount = pSrcIndexMesh->IndexCount(); + + CMeshBuilder dstMeshBuilder; + dstMeshBuilder.Begin( pDstIndexMesh, pSrcIndexMesh->GetPrimitiveType(), 0, nIndexCount ); + CIndexBuffer *srcIndexBuffer = pSrcIndexMesh->GetIndexBuffer(); + int dummy = 0; + unsigned short *srcIndexArray = srcIndexBuffer->Lock( false, nIndexCount, dummy, 0 ); + int i; + for( i = 0; i < nIndexCount; i++ ) + { + dstMeshBuilder.Index( srcIndexArray[i] ); + dstMeshBuilder.AdvanceIndex(); + } + srcIndexBuffer->Unlock( 0 ); + dstMeshBuilder.End(); +} + + +IMesh *CMeshMgr::GetFlexMesh() +{ + if ( g_pMaterialSystemHardwareConfig->SupportsPixelShaders_2_b() ) + { + // FIXME: Kinda ugly size.. 28 bytes + m_DynamicFlexMesh.SetVertexFormat( VERTEX_POSITION | VERTEX_NORMAL | VERTEX_WRINKLE | VERTEX_FORMAT_USE_EXACT_FORMAT ); + } + else + { + // Same size as a pair of float3s (24 bytes) + m_DynamicFlexMesh.SetVertexFormat( VERTEX_POSITION | VERTEX_NORMAL | VERTEX_FORMAT_USE_EXACT_FORMAT ); + } + return &m_DynamicFlexMesh; +} + +//----------------------------------------------------------------------------- +// Gets at the dynamic mesh +//----------------------------------------------------------------------------- +IMesh* CMeshMgr::GetDynamicMesh( IMaterial* pMaterial, VertexFormat_t vertexFormat, int nHWSkinBoneCount, + bool buffered, IMesh* pVertexOverride, IMesh* pIndexOverride ) +{ + tmZoneFiltered( TELEMETRY_LEVEL0, 50, TMZF_NONE, "%s", __FUNCTION__ ); + + Assert( (pMaterial == NULL) || ((IMaterialInternal *)pMaterial)->IsRealTimeVersion() ); + + if ( IsX360() ) + { + buffered = false; + } + + // Can't be buffered if we're overriding the buffers + if ( pVertexOverride || pIndexOverride ) + { + buffered = false; + } + + // When going from buffered to unbuffered mode, need to flush.. + if ((m_BufferedMode != buffered) && m_BufferedMode) + { + m_BufferedMesh.SetMesh(0); + } + m_BufferedMode = buffered; + + IMaterialInternal* pMatInternal = static_cast(pMaterial); + + bool needTempMesh = ShaderAPI()->IsInSelectionMode(); + +#ifdef DRAW_SELECTION + if( g_bDrawSelection ) + { + needTempMesh = true; + } +#endif + + CBaseMeshDX8* pMesh; + + if ( needTempMesh ) + { + // These haven't been implemented yet for temp meshes! + // I'm not a hundred percent sure how to implement them; it would + // involve a lock and a copy at least, which would stall the entire + // rendering pipeline. + Assert( !pVertexOverride ); + + if( pIndexOverride ) + { + CopyStaticMeshIndexBufferToTempMeshIndexBuffer( &m_DynamicTempMesh, + ( CMeshDX8 * )pIndexOverride ); + } + pMesh = &m_DynamicTempMesh; + } + else + { + pMesh = &m_DynamicMesh; + } + + if ( m_BufferedMode ) + { + Assert( !m_BufferedMesh.WasNotRendered() ); + m_BufferedMesh.SetMesh( pMesh ); + pMesh = &m_BufferedMesh; + } + + if( !pVertexOverride ) + { + // Remove VERTEX_FORMAT_COMPRESSED from the material's format (dynamic meshes don't + // support compression, and all materials should support uncompressed verts too) + VertexFormat_t materialFormat = pMatInternal->GetVertexFormat() & ~VERTEX_FORMAT_COMPRESSED; + VertexFormat_t fmt = ( vertexFormat != 0 ) ? vertexFormat : materialFormat; + if ( vertexFormat != 0 ) + { + int nVertexFormatBoneWeights = NumBoneWeights( vertexFormat ); + if ( nHWSkinBoneCount < nVertexFormatBoneWeights ) + { + nHWSkinBoneCount = nVertexFormatBoneWeights; + } + } + + // Force the requested number of bone weights + fmt &= ~VERTEX_BONE_WEIGHT_MASK; + if ( nHWSkinBoneCount > 0 ) + { + fmt |= VERTEX_BONEWEIGHT( 2 ); + fmt |= VERTEX_BONE_INDEX; + } + + pMesh->SetVertexFormat( fmt ); + } + else + { + CBaseMeshDX8 *pDX8Mesh = static_cast(pVertexOverride); + pMesh->SetVertexFormat( pDX8Mesh->GetVertexFormat() ); + } + pMesh->SetMorphFormat( pMatInternal->GetMorphFormat() ); + pMesh->SetMaterial( pMatInternal ); + + // Note this works because we're guaranteed to not be using a buffered mesh + // when we have overrides on + // FIXME: Make work for temp meshes + if ( pMesh == &m_DynamicMesh ) + { + CBaseMeshDX8* pBaseVertex = static_cast( pVertexOverride ); + if ( pBaseVertex ) + { + m_DynamicMesh.OverrideVertexBuffer( pBaseVertex->GetVertexBuffer() ); + } + + CBaseMeshDX8* pBaseIndex = static_cast( pIndexOverride ); + if ( pBaseIndex ) + { + m_DynamicMesh.OverrideIndexBuffer( pBaseIndex->GetIndexBuffer() ); + } + } + + return pMesh; +} + + +//----------------------------------------------------------------------------- +// Used to construct vertex data +//----------------------------------------------------------------------------- +void CMeshMgr::ComputeVertexDescription( unsigned char* pBuffer, + VertexFormat_t vertexFormat, MeshDesc_t& desc ) const +{ + ComputeVertexDesc( pBuffer, vertexFormat, (VertexDesc_t &)desc ); +} + + +//----------------------------------------------------------------------------- +// Computes the vertex format +//----------------------------------------------------------------------------- +VertexFormat_t CMeshMgr::ComputeVertexFormat( unsigned int flags, + int nTexCoordArraySize, int* pTexCoordDimensions, int numBoneWeights, + int userDataSize ) const +{ + // Construct a bitfield that makes sense and is unique from the standard FVF formats + VertexFormat_t fmt = flags & ~VERTEX_FORMAT_USE_EXACT_FORMAT; + + if ( g_pHardwareConfig->SupportsCompressedVertices() == VERTEX_COMPRESSION_NONE ) + { + // Vertex compression is disabled - make sure all materials + // say "No!" to compressed verts ( tested in IsValidVertexFormat() ) + fmt &= ~VERTEX_FORMAT_COMPRESSED; + } + + // This'll take 3 bits at most + Assert( numBoneWeights <= 4 ); + + if ( numBoneWeights > 0 ) + { + fmt |= VERTEX_BONEWEIGHT( 2 ); // Always exactly two weights + } + + // Size is measured in # of floats + Assert( userDataSize <= 4 ); + fmt |= VERTEX_USERDATA_SIZE(userDataSize); + + // NOTE: If pTexCoordDimensions isn't specified, then nTexCoordArraySize + // is interpreted as meaning that we have n 2D texcoords in the first N texcoord slots + nTexCoordArraySize = min( nTexCoordArraySize, (int)VERTEX_MAX_TEXTURE_COORDINATES ); + for ( int i = 0; i < nTexCoordArraySize; ++i ) + { + if ( pTexCoordDimensions ) + { + Assert( pTexCoordDimensions[i] >= 0 && pTexCoordDimensions[i] <= 4 ); + fmt |= VERTEX_TEXCOORD_SIZE( (TextureStage_t)i, pTexCoordDimensions[i] ); + } + else + { + fmt |= VERTEX_TEXCOORD_SIZE( (TextureStage_t)i, 2 ); + } + } + + return fmt; +} + + +//----------------------------------------------------------------------------- +// Use fat vertices (for tools) +//----------------------------------------------------------------------------- +void CMeshMgr::UseFatVertices( bool bUseFat ) +{ + m_bUseFatVertices = bUseFat; +} + + +//----------------------------------------------------------------------------- +// Returns the number of vertices we can render using the dynamic mesh +//----------------------------------------------------------------------------- +void CMeshMgr::GetMaxToRender( IMesh *pMesh, bool bMaxUntilFlush, int *pMaxVerts, int *pMaxIndices ) +{ + CBaseMeshDX8 *pBaseMesh = static_cast( pMesh ); + if ( !pBaseMesh ) + { + *pMaxVerts = 0; + *pMaxIndices = m_pDynamicIndexBuffer->IndexCount(); + return; + } + + if ( IsBufferedDynamicMesh( pMesh ) ) + { + pBaseMesh = (CBaseMeshDX8*)static_cast( pBaseMesh )->GetMesh(); + pMesh = pBaseMesh; + } + + // Static mesh? Max you can use is 65535 + if ( !IsDynamicMesh( pMesh ) ) + { + *pMaxVerts = 65535; + *pMaxIndices = 65535; + return; + } + + CVertexBuffer *pVertexBuffer = pBaseMesh->GetVertexBuffer(); + CIndexBuffer *pIndexBuffer = pBaseMesh->GetIndexBuffer(); + + if ( !pVertexBuffer ) + { + *pMaxVerts = 0; + *pMaxIndices = 0; + return; + } + + if ( !bMaxUntilFlush ) + { + *pMaxVerts = ShaderAPI()->GetCurrentDynamicVBSize() / pVertexBuffer->VertexSize(); + if ( *pMaxVerts > 65535 ) + { + *pMaxVerts = 65535; + } + *pMaxIndices = pIndexBuffer ? pIndexBuffer->IndexCount() : 0; + return; + } + + *pMaxVerts = pVertexBuffer->NumVerticesUntilFlush(); + *pMaxIndices = pIndexBuffer ? pIndexBuffer->IndexCount() - pIndexBuffer->IndexPosition() : 0; + if ( *pMaxVerts == 0 ) + { + *pMaxVerts = ShaderAPI()->GetCurrentDynamicVBSize() / pVertexBuffer->VertexSize(); + } + if ( *pMaxVerts > 65535 ) + { + *pMaxVerts = 65535; + } + if ( *pMaxIndices == 0 ) + { + *pMaxIndices = pIndexBuffer ? pIndexBuffer->IndexCount() : 0; + } +} + +int CMeshMgr::GetMaxVerticesToRender( IMaterial *pMaterial ) +{ + Assert( (pMaterial == NULL) || ((IMaterialInternal *)pMaterial)->IsRealTimeVersion() ); + + // Be conservative, assume no compression (in here, we don't know if the caller will used a compressed VB or not) + // FIXME: allow the caller to specify which compression type should be used to compute size from the vertex format + // (this can vary between multiple VBs/Meshes using the same material) + VertexFormat_t fmt = pMaterial->GetVertexFormat() & ~VERTEX_FORMAT_COMPRESSED; + int nVertexSize = VertexFormatSize( fmt ); + if ( nVertexSize == 0 ) + { + // unable to determine vertex format information, possibly due to device loss. + Warning( "bad vertex size for material %s\n", pMaterial->GetName() ); + return 0; + } + + int nMaxVerts = ShaderAPI()->GetCurrentDynamicVBSize() / nVertexSize; + return MIN( nMaxVerts, 65535 ); +} + +int CMeshMgr::GetMaxIndicesToRender( ) +{ + return INDEX_BUFFER_SIZE; +} + +//----------------------------------------------------------------------------- +// Returns a vertex buffer appropriate for the flags +//----------------------------------------------------------------------------- +CVertexBuffer *CMeshMgr::FindOrCreateVertexBuffer( int nDynamicBufferId, VertexFormat_t vertexFormat ) +{ + int vertexSize = VertexFormatSize( vertexFormat ); + + while ( m_DynamicVertexBuffers.Count() <= nDynamicBufferId ) + { + // Track VB allocations (override any prior allocator string set higher up on the callstack) + g_VBAllocTracker->TrackMeshAllocations( NULL ); + g_VBAllocTracker->TrackMeshAllocations( "CMeshMgr::FindOrCreateVertexBuffer (dynamic VB)" ); + + // create the single 1MB dynamic vb that will be shared amongst all consumers + // the correct thing is to use the largest expected vertex format size of max elements, but this + // creates an undesirably large buffer - instead create the buffer we want, and fix consumers that bork + // NOTE: GetCurrentDynamicVBSize returns a smaller value during level transitions + int nBufferMemory = ShaderAPI()->GetCurrentDynamicVBSize(); + int nIndex = m_DynamicVertexBuffers.AddToTail(); + m_DynamicVertexBuffers[nIndex].m_VertexSize = 0; + m_DynamicVertexBuffers[nIndex].m_pBuffer = new CVertexBuffer( Dx9Device(), 0, 0, + nBufferMemory / VERTEX_BUFFER_SIZE, VERTEX_BUFFER_SIZE, TEXTURE_GROUP_STATIC_VERTEX_BUFFER_OTHER, ShaderAPI()->UsingSoftwareVertexProcessing(), true ); + + g_VBAllocTracker->TrackMeshAllocations( NULL ); + } + + if ( m_DynamicVertexBuffers[nDynamicBufferId].m_VertexSize != vertexSize ) + { + // provide caller with dynamic vb in expected format + // NOTE: GetCurrentDynamicVBSize returns a smaller value during level transitions + int nBufferMemory = ShaderAPI()->GetCurrentDynamicVBSize(); + m_DynamicVertexBuffers[nDynamicBufferId].m_VertexSize = vertexSize; + m_DynamicVertexBuffers[nDynamicBufferId].m_pBuffer->ChangeConfiguration( vertexSize, nBufferMemory ); + + // size changed means stream stride needs update + // mark cached stream state as invalid to reset stream + if ( nDynamicBufferId == 0 ) + { + g_pLastVertex = NULL; + } + } + + return m_DynamicVertexBuffers[nDynamicBufferId].m_pBuffer; +} + +CIndexBuffer *CMeshMgr::GetDynamicIndexBuffer() +{ + return m_pDynamicIndexBuffer; +} + +IVertexBuffer *CMeshMgr::GetDynamicVertexBuffer( IMaterial *pMaterial, bool buffered ) +{ + Assert( 0 ); + return NULL; +// return ( IMeshDX8 * )GetDynamicMesh( pMaterial, buffered, NULL, NULL ); +} + +IIndexBuffer *CMeshMgr::GetDynamicIndexBuffer( IMaterial *pMaterial, bool buffered ) +{ + Assert( 0 ); + return NULL; +// return ( IMeshDX8 * )GetDynamicMesh( pMaterial, buffered, NULL, NULL ); +} + + +//----------------------------------------------------------------------------- +IVertexBuffer *CMeshMgr::CreateVertexBuffer( ShaderBufferType_t type, VertexFormat_t fmt, int nVertexCount, const char *pBudgetGroup ) +{ + // FIXME: Use a fixed-size allocator + CVertexBufferDx8 *pNewVertexBuffer = new CVertexBufferDx8( type, fmt, nVertexCount, pBudgetGroup ); + return pNewVertexBuffer; +} + +IIndexBuffer *CMeshMgr::CreateIndexBuffer( ShaderBufferType_t bufferType, MaterialIndexFormat_t fmt, int nIndexCount, const char *pBudgetGroup ) +{ + switch( bufferType ) + { + case SHADER_BUFFER_TYPE_STATIC: + case SHADER_BUFFER_TYPE_DYNAMIC: + { + CIndexBufferDx8 *pIndexBuffer = new CIndexBufferDx8( bufferType, fmt, nIndexCount, pBudgetGroup ); + return pIndexBuffer; + } + case SHADER_BUFFER_TYPE_STATIC_TEMP: + case SHADER_BUFFER_TYPE_DYNAMIC_TEMP: + Assert( 0 ); + return NULL; + default: + Assert( 0 ); + return NULL; + } +} + +void CMeshMgr::DestroyVertexBuffer( IVertexBuffer *pVertexBuffer ) +{ + if ( pVertexBuffer && !IsDynamicVertexBuffer( pVertexBuffer ) ) + { + delete pVertexBuffer; + } +} + +void CMeshMgr::DestroyIndexBuffer( IIndexBuffer *pIndexBuffer ) +{ + if ( pIndexBuffer && !IsDynamicIndexBuffer( pIndexBuffer ) ) + { + delete pIndexBuffer; + } +} + +// Do we need to specify the stream here in the case of locking multiple dynamic VBs on different streams? +IVertexBuffer *CMeshMgr::GetDynamicVertexBuffer( int streamID, VertexFormat_t vertexFormat, bool bBuffered ) +{ + if ( IsX360() ) + { + bBuffered = false; + } + + if ( CompressionType( vertexFormat ) != VERTEX_COMPRESSION_NONE ) + { + // UNDONE: support compressed dynamic meshes if needed (pro: less VB memory, con: time spent compressing) + DebuggerBreak(); + return NULL; + } + + // MESHFIXME +#if 0 + if ( ( m_BufferedMode != bBuffered ) && m_BufferedMode ) + { + m_BufferedIndexBuffer.SetIndexBuffer( NULL ); + } +#endif + + m_BufferedMode = bBuffered; + Assert( !m_BufferedMode ); // MESHFIXME: don't deal with buffered VBs yet. + + bool needTempMesh = ShaderAPI()->IsInSelectionMode(); + +#ifdef DRAW_SELECTION + if( g_bDrawSelection ) + { + needTempMesh = true; + } +#endif + + Assert( !needTempMesh ); // MESHFIXME: don't support temp meshes here yet. + + CVertexBufferDx8 *pVertexBuffer; + + if ( needTempMesh ) + { + Assert( 0 ); // MESHFIXME: don't do this yet. +// pVertexBuffer = &m_DynamicTempVertexBuffer; + pVertexBuffer = NULL; + } + else + { + pVertexBuffer = &m_DynamicVertexBuffer; + } + + if ( m_BufferedMode ) + { + Assert( 0 ); // don't support this yet. +#if 0 + Assert( !m_BufferedMesh.WasNotRendered() ); + m_BufferedMesh.SetMesh( pMesh ); + pMesh = &m_BufferedMesh; +#endif + } + + return pVertexBuffer; +} + +IIndexBuffer *CMeshMgr::GetDynamicIndexBuffer( MaterialIndexFormat_t fmt, bool bBuffered ) +{ + if ( IsX360() ) + { + bBuffered = false; + } + + m_BufferedMode = bBuffered; + + Assert( !m_BufferedMode ); + +#ifdef DBGFLAG_ASSERT + bool needTempMesh = +#endif + ShaderAPI()->IsInSelectionMode(); + +#ifdef DRAW_SELECTION + if( g_bDrawSelection ) + { + needTempMesh = true; + } +#endif + + Assert( !needTempMesh ); // don't handle this yet. MESHFIXME + + CIndexBufferBase *pIndexBuffer = &m_DynamicIndexBuffer; + return pIndexBuffer; +} + +void CMeshMgr::SetVertexIDStreamState() +{ + if ( IsX360() ) + return; + + // MESHFIXME : This path is only used for the new index/vertex buffer interfaces. + // MESHFIXME : This path is only used for the new index/vertex buffer interfaces. + bool bUsingVertexID = false;//IsUsingVertexID(); +// if ( bUsingVertexID != g_bUsingVertexID ) + { + if ( bUsingVertexID ) + { + // NOTE: Morphing doesn't work with dynamic buffers!!! BLEAH + // It's because the indices (which are not 0 based for dynamic buffers) + // are accessing both the vertexID buffer + the regular vertex buffer. + // This *might* be fixable with baseVertexIndex? + Assert( !m_pCurrentVertexBuffer->IsDynamic() ); + + CVertexBuffer *pVertexIDBuffer = g_MeshMgr.GetVertexIDBuffer( ); + RECORD_COMMAND( DX8_SET_STREAM_SOURCE, 4 ); + RECORD_INT( pVertexIDBuffer->UID() ); + RECORD_INT( 3 ); + RECORD_INT( 0 ); + RECORD_INT( pVertexIDBuffer->VertexSize() ); + + D3DSetStreamSource( 3, pVertexIDBuffer->GetInterface(), 0, pVertexIDBuffer->VertexSize() ); + pVertexIDBuffer->HandlePerFrameTextureStats( ShaderAPI()->GetCurrentFrameCounter() ); + } + else + { + RECORD_COMMAND( DX8_SET_STREAM_SOURCE, 4 ); + RECORD_INT( -1 ); // vertex buffer id + RECORD_INT( 3 ); // stream + RECORD_INT( 0 ); // vertex offset + RECORD_INT( 0 ); // vertex size + + D3DSetStreamSource( 3, 0, 0, 0 ); + } + g_bUsingVertexID = bUsingVertexID; + } +} + +void CMeshMgr::SetColorStreamState() +{ + if ( g_pLastColorMesh ) + { + RECORD_COMMAND( DX8_SET_STREAM_SOURCE, 4 ); + RECORD_INT( -1 ); // vertex buffer id + RECORD_INT( 1 ); // stream + RECORD_INT( 0 ); // vertex offset + RECORD_INT( 0 ); // vertex size + + D3DSetStreamSource( 1, 0, 0, 0 ); + } + g_pLastColorMesh = NULL; + g_nLastColorMeshVertOffsetInBytes = 0; +} + +void CMeshMgr::SetVertexStreamState( int nVertOffsetInBytes, int nVertexStride ) +{ + // Calls in here assume shader support... + if ( HardwareConfig()->SupportsVertexAndPixelShaders() ) + { + // Set a 4kb all-zero static VB into the flex/wrinkle stream with a stride of 0 bytes, so the vertex shader always reads valid floating point values (otherwise it can get NaN's/Inf's, and under OpenGL this is bad on NVidia) + // togl requires non-zero strides, but on D3D9 we can set a stride of 0 for a little more efficiency. + D3DSetStreamSource( 2, g_MeshMgr.GetZeroVertexBuffer(), 0, IsOpenGL() ? 4 : 0 ); + + // cFlexScale.x masks flex in vertex shader + if ( g_pHardwareConfig->Caps().m_SupportsVertexShaders_2_0 ) + { + float c[4] = { 0.0f, 0.0f, 0.0f, 0.0f }; + ShaderAPI()->SetVertexShaderConstant( VERTEX_SHADER_FLEXSCALE, c, 1 ); + } + + g_bFlexMeshStreamSet = false; + } + + // MESHFIXME : This path is only used for the new index/vertex buffer interfaces. + if ( g_pLastVertex || ( g_pLastVertexBuffer != m_pCurrentVertexBuffer->GetDx9Buffer() ) || + ( g_nLastVertOffsetInBytes != nVertOffsetInBytes ) || ( g_nLastVertStride != nVertexStride )) + { + Assert( m_pCurrentVertexBuffer && m_pCurrentVertexBuffer->GetDx9Buffer() ); + + D3DSetStreamSource( 0, m_pCurrentVertexBuffer->GetDx9Buffer(), nVertOffsetInBytes, nVertexStride ); + m_pCurrentVertexBuffer->HandlePerFrameTextureStats( ShaderAPI()->GetCurrentFrameCounter() ); + + g_pLastVertex = NULL; + g_nLastVertStride = nVertexStride; + g_pLastVertexBuffer = m_pCurrentVertexBuffer->GetDx9Buffer(); + g_nLastVertOffsetInBytes = nVertOffsetInBytes; + } +} + +bool CMeshMgr::SetRenderState( int nVertexOffsetInBytes, int nFirstVertexIdx, VertexFormat_t vertexFormat, int nVertexStride ) +{ + // Can't set the state if we're deactivated + if ( g_pShaderDeviceDx8->IsDeactivated() ) + { + ResetMeshRenderState(); + return false; + } + + // make sure the vertex format is a superset of the current material's + // vertex format... + // MESHFIXME : This path is only used for the new index/vertex buffer interfaces. +#if 0 + // FIXME + if ( !IsValidVertexFormat( vertexFormat ) ) + { + Warning( "Material %s is being applied to a model, you need $model=1 in the .vmt file!\n", + ShaderAPI()->GetBoundMaterial()->GetName() ); + return false; + } +#endif + + SetVertexIDStreamState(); + SetColorStreamState(); + SetVertexStreamState( nVertexOffsetInBytes, nVertexStride ); + SetIndexStreamState( nFirstVertexIdx ); + + return true; +} + +void CMeshMgr::BindVertexBuffer( int nStreamID, IVertexBuffer *pVertexBuffer, int nOffsetInBytes, int nFirstVertex, int nVertexCount, VertexFormat_t fmt, int nRepetitions ) +{ + // FIXME: Multiple stream support isn't implemented yet + Assert( nStreamID == 0 ); + + m_pCurrentVertexBuffer = static_cast< CVertexBufferDx8 * >( pVertexBuffer ); + m_CurrentVertexFormat = fmt; + m_pVertexBufferOffset[nStreamID] = nOffsetInBytes; + m_pCurrentVertexStride[nStreamID] = m_pCurrentVertexBuffer->VertexSize(); + m_pFirstVertex[nStreamID] = nFirstVertex; + m_pVertexCount[nStreamID] = nVertexCount, + m_pVertexIDBuffer = NULL; +} + +void CMeshMgr::BindIndexBuffer( IIndexBuffer *pIndexBuffer, int nOffsetInBytes ) +{ + m_pCurrentIndexBuffer = static_cast< CIndexBufferBase * >( pIndexBuffer ); + m_nIndexBufferOffset = nOffsetInBytes; +} + +void CMeshMgr::Draw( MaterialPrimitiveType_t primitiveType, int nFirstIndex, int nIndexCount ) +{ + // MESHFIXME : This path is only used for the new index/vertex buffer interfaces. + // make sure we aren't using a morph stream for this path. +// Assert( !IsUsingMorphData() ); +// Assert( !m_pColorMesh ); + + SetRenderState( m_pVertexBufferOffset[0], /* nFirstVertexIdx */0, m_CurrentVertexFormat, m_pCurrentVertexStride[0] ); + + m_PrimitiveType = MATERIAL_TRIANGLES; + Assert( primitiveType == MATERIAL_TRIANGLES ); + + m_nFirstIndex = nFirstIndex; + m_nNumIndices = nIndexCount; + + ShaderAPI()->DrawWithVertexAndIndexBuffers(); +} + +void CMeshMgr::RenderPassWithVertexAndIndexBuffers( void ) +{ +// LOCK_SHADERAPI(); MESHFIXME + VPROF( "CShaderAPIDX8::RenderPassWithVertexAndIndexBuffers" ); + + Assert( m_PrimitiveType != MATERIAL_HETEROGENOUS ); + +// for ( int iPrim=0; iPrim < s_nPrims; iPrim++ ) + { +// CPrimList *pPrim = &s_pPrims[iPrim]; + +// if ( pPrim->m_NumIndices == 0 ) +// continue; + + if ( m_PrimitiveType == MATERIAL_POINTS ) + { + // (For point lists, we don't actually fill in indices, but we treat it as + // though there are indices for the list up until here). + Assert( 0 ); +// Dx9Device()->DrawPrimitive( ComputeMode( m_PrimitiveType ), s_FirstVertex, pPrim->m_NumIndices ); + } + else + { +// int numPrimitives = NumPrimitives( s_NumVertices, pPrim->m_NumIndices ); + +// Warning( "CMeshMgr::RenderPassWithVertexAndIndexBuffers: DrawIndexedPrimitive: m_nFirstIndex = %d numPrimitives = %d\n", ( int )( ( CDynamiCIndexBufferDx8 * )m_pCurrentIndexBuffer )->m_FirstIndex, ( int )( m_nNumIndices / 3 ) ); + { + VPROF( "Dx9Device()->DrawIndexedPrimitive" ); +// VPROF_INCREMENT_COUNTER( "DrawIndexedPrimitive", 1 ); +// VPROF_INCREMENT_COUNTER( "numPrimitives", numPrimitives ); + +// Dx9Device()->DrawIndexedPrimitive( +// m_Mode, +// m_FirstIndex, +// s_FirstVertex, +// s_NumVertices, +// pPrim->m_FirstIndex, +// numPrimitives ); + + Assert( m_nFirstIndex >= 0 ); + +#ifdef CHECK_INDICES + // g_pLastVertex - this is the current vertex buffer + // g_pLastColorMesh - this is the curent color mesh, if there is one. + // g_pLastIndex - this is the current index buffer. + // vertoffset : m_FirstIndex + CIndexBufferDx8 *pIndexBuffer = assert_cast< CIndexBufferDx8 * >( m_pCurrentIndexBuffer ); + if( m_PrimitiveType == MATERIAL_TRIANGLES || m_PrimitiveType == MATERIAL_TRIANGLE_STRIP ) + { + // FIXME: need to be able to deal with multiple stream here, but don't bother for now. + int j; + int numVerts = m_pVertexCount[0]; + for( j = 0; j < m_nNumIndices; j++ ) + { + int index = pIndexBuffer->GetShadowIndex( j + m_nFirstIndex ); + Assert( index >= m_pFirstVertex[0] ); + Assert( index < m_pFirstVertex[0] + numVerts ); + } + } +#endif // CHECK_INDICES + Dx9Device()->DrawIndexedPrimitive( + ComputeMode( m_PrimitiveType ), // Member of the D3DPRIMITIVETYPE enumerated type, describing the type of primitive to render. D3DPT_POINTLIST is not supported with this method. + + /*m_FirstIndex*/ 0, // Offset from the start of the vertex buffer to the first vertex index. An index of 0 in the index buffer refers to this location in the vertex buffer. + + /*s_FirstVertex*/ m_pFirstVertex[0],// Minimum vertex index for vertices used during this call. This is a zero based index relative to BaseVertexIndex. + // This is zero for now since we don't do more than one batch yet with the new mesh interface. + + /*s_NumVertices*/ m_pVertexCount[0], + // Number of vertices used during this call. The first vertex is located at index: BaseVertexIndex + MinIndex. + // This is simple the number of verts in the current vertex buffer for now since we don't do more than one batch with the new mesh interface. + + m_nFirstIndex /*pPrim->m_FirstIndex*/, // Index of the first index to use when accesssing the vertex buffer. Beginning at StartIndex to index vertices from the vertex buffer. + + m_nNumIndices / 3/*numPrimitives*/ // Number of primitives to render. The number of vertices used is a function of the primitive count and the primitive type. + ); + + Assert( CMeshDX8::s_FirstVertex == 0 ); + Assert( CMeshDX8::s_NumVertices == 0 ); + } + } + } +} + +//----------------------------------------------------------------------------- +void CMeshMgr::SetIndexStreamState( int firstVertexIdx ) +{ + CIndexBufferDx8 *pIndexBuffer = assert_cast< CIndexBufferDx8* >( m_pCurrentIndexBuffer ); + IDirect3DIndexBuffer9 *pDx9Buffer = pIndexBuffer ? pIndexBuffer->GetDx9Buffer() : NULL; + if ( g_pLastIndex || g_pLastIndexBuffer != pDx9Buffer ) + { + Dx9Device()->SetIndices( pDx9Buffer ); + pIndexBuffer->HandlePerFrameTextureStats( ShaderAPI()->GetCurrentFrameCounter() ); + + g_pLastIndexBuffer = pDx9Buffer; + SafeRelease( &g_pLastIndex ); + g_LastVertexIdx = -1; + } +} + + diff --git a/materialsystem/shaderapidx9/recording.cpp b/materialsystem/shaderapidx9/recording.cpp new file mode 100644 index 0000000..4d2344a --- /dev/null +++ b/materialsystem/shaderapidx9/recording.cpp @@ -0,0 +1,157 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// + +#include "togl/rendermechanism.h" +#include "recording.h" +#include "shaderapi/ishaderutil.h" +#include "materialsystem/imaterialsystem.h" +#include "shaderapidx8_global.h" +#include "utlvector.h" +#include + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +#ifdef RECORDING + +//----------------------------------------------------------------------------- +// Globals +//----------------------------------------------------------------------------- + +static CUtlVector g_pRecordingBuffer; +static int g_ArgsRemaining = 0; +static int g_CommandStartIdx = 0; + +//----------------------------------------------------------------------------- +// Opens the recording file +//----------------------------------------------------------------------------- + +static FILE* OpenRecordingFile() +{ +#ifdef CRASH_RECORDING + static FILE *fp = 0; +#else + FILE* fp = 0; +#endif + static bool g_CantOpenFile = false; + static bool g_NeverOpened = true; + if (!g_CantOpenFile) + { +#ifdef CRASH_RECORDING + if( g_NeverOpened ) + { + fp = fopen( "shaderdx8.rec", "wbc" ); + } +#else + fp = fopen( "shaderdx8.rec", g_NeverOpened ? "wb" : "ab" ); +#endif + if (!fp) + { + Warning("Unable to open recording file shaderdx8.rec!\n"); + g_CantOpenFile = true; + } + g_NeverOpened = false; + } + return fp; +} + +//----------------------------------------------------------------------------- +// Writes to the recording file +//----------------------------------------------------------------------------- + +#define COMMAND_BUFFER_SIZE 32768 + +static void WriteRecordingFile() +{ + // Store the command size + *(int*)&g_pRecordingBuffer[g_CommandStartIdx] = + g_pRecordingBuffer.Size() - g_CommandStartIdx; + +#ifndef CRASH_RECORDING + // When not crash recording, flush when buffer gets too big, + // or when Present() is called + if ((g_pRecordingBuffer.Size() < COMMAND_BUFFER_SIZE) && + (g_pRecordingBuffer[g_CommandStartIdx+4] != DX8_PRESENT)) + return; +#endif + + FILE* fp = OpenRecordingFile(); + if (fp) + { + // store the command size + fwrite( g_pRecordingBuffer.Base(), 1, g_pRecordingBuffer.Size(), fp ); + fflush( fp ); +#ifndef CRASH_RECORDING + fclose( fp ); +#endif + } + + g_pRecordingBuffer.RemoveAll(); +} + +// Write the buffered crap out on shutdown. +void FinishRecording() +{ +#ifndef CRASH_RECORDING + FILE* fp = OpenRecordingFile(); + if (fp) + { + // store the command size + fwrite( g_pRecordingBuffer.Base(), 1, g_pRecordingBuffer.Size(), fp ); + fflush( fp ); + } + + g_pRecordingBuffer.RemoveAll(); +#endif +} + +// set this to true in the debugger to actually record commands. +static bool g_bDoRecord = true; + +//----------------------------------------------------------------------------- +// Records a command +//----------------------------------------------------------------------------- + +void RecordCommand( RecordingCommands_t cmd, int numargs ) +{ + if( !g_bDoRecord ) + { + return; + } + Assert( g_ArgsRemaining == 0 ); + + g_CommandStartIdx = g_pRecordingBuffer.AddMultipleToTail( 6 ); + + // save space for the total command size + g_pRecordingBuffer[g_CommandStartIdx+4] = cmd; + g_pRecordingBuffer[g_CommandStartIdx+5] = numargs; + g_ArgsRemaining = numargs; + if (g_ArgsRemaining == 0) + WriteRecordingFile(); +} + +//----------------------------------------------------------------------------- +// Records an argument for a command, flushes when the command is done +//----------------------------------------------------------------------------- + +void RecordArgument( void const* pMemory, int size ) +{ + if( !g_bDoRecord ) + { + return; + } + Assert( g_ArgsRemaining > 0 ); + int tail = g_pRecordingBuffer.Size(); + g_pRecordingBuffer.AddMultipleToTail( size ); + memcpy( &g_pRecordingBuffer[tail], pMemory, size ); + if (--g_ArgsRemaining == 0) + WriteRecordingFile(); +} + + +#endif // RECORDING diff --git a/materialsystem/shaderapidx9/recording.h b/materialsystem/shaderapidx9/recording.h new file mode 100644 index 0000000..81c50fc --- /dev/null +++ b/materialsystem/shaderapidx9/recording.h @@ -0,0 +1,198 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// + +#ifndef RECORDING_H +#define RECORDING_H +#pragma once + +//----------------------------------------------------------------------------- +// Use this to put us into a 'recording' mode +//----------------------------------------------------------------------------- + +//#define RECORDING + +//----------------------------------------------------------------------------- +// Uncomment these to record special frames in the recording +// that reset the entire render state. +//----------------------------------------------------------------------------- + +//#define RECORD_KEYFRAMES 1 +#define KEYFRAME_INTERVAL 100 // number of actual frames between each keyframe + +//----------------------------------------------------------------------------- +// Use this to allow us to record crashes (write every command immediately) +//----------------------------------------------------------------------------- + +//#define CRASH_RECORDING + +//----------------------------------------------------------------------------- +// Use this to record textures (checkboards are used for textures otherwise) +//----------------------------------------------------------------------------- + +#define RECORD_TEXTURES + +//----------------------------------------------------------------------------- +// Use this to record debug strings . .these are only useful if you are doing "playback -list" +//----------------------------------------------------------------------------- + +#define RECORD_DEBUG_STRINGS + +//----------------------------------------------------------------------------- +// Recording state, if you change this, change the table in playback/playback.cpp +//----------------------------------------------------------------------------- + +enum RecordingCommands_t +{ + DX8_CREATE_DEVICE = 0, + DX8_DESTROY_DEVICE, + DX8_RESET, + DX8_SHOW_CURSOR, + DX8_BEGIN_SCENE, + DX8_END_SCENE, + DX8_PRESENT, + DX8_CREATE_TEXTURE, + DX8_DESTROY_TEXTURE, + DX8_SET_TEXTURE, + DX8_SET_TRANSFORM, + DX8_CREATE_VERTEX_SHADER, + DX8_CREATE_PIXEL_SHADER, + DX8_DESTROY_VERTEX_SHADER, + DX8_DESTROY_PIXEL_SHADER, + DX8_SET_VERTEX_SHADER, + DX8_SET_PIXEL_SHADER, + DX8_SET_VERTEX_SHADER_CONSTANT, + DX8_SET_PIXEL_SHADER_CONSTANT, + DX8_SET_MATERIAL, + DX8_LIGHT_ENABLE, + DX8_SET_LIGHT, + DX8_SET_VIEWPORT, + DX8_CLEAR, + DX8_VALIDATE_DEVICE, + DX8_SET_RENDER_STATE, + DX8_SET_TEXTURE_STAGE_STATE, + + DX8_CREATE_VERTEX_BUFFER, + DX8_DESTROY_VERTEX_BUFFER, + DX8_LOCK_VERTEX_BUFFER, + DX8_VERTEX_DATA, + DX8_UNLOCK_VERTEX_BUFFER, + + DX8_CREATE_INDEX_BUFFER, + DX8_DESTROY_INDEX_BUFFER, + DX8_LOCK_INDEX_BUFFER, + DX8_INDEX_DATA, + DX8_UNLOCK_INDEX_BUFFER, + + DX8_SET_STREAM_SOURCE, + DX8_SET_INDICES, + DX8_DRAW_PRIMITIVE, + DX8_DRAW_INDEXED_PRIMITIVE, + + DX8_LOCK_TEXTURE, + DX8_UNLOCK_TEXTURE, + + DX8_KEYFRAME, // isn't actually a dx8 command, used to help find particular frames + + DX8_SET_TEXTURE_DATA, + DX8_BLIT_TEXTURE_BITS, + + DX8_GET_DEVICE_CAPS, + DX8_GET_ADAPTER_IDENTIFIER, + + DX8_HARDWARE_SYNC, + + DX8_COPY_FRAMEBUFFER_TO_TEXTURE, + DX8_DEBUG_STRING, + DX8_CREATE_DEPTH_TEXTURE, + DX8_DESTROY_DEPTH_TEXTURE, + DX8_SET_RENDER_TARGET, + + DX8_TEST_COOPERATIVE_LEVEL, + + DX8_SET_VERTEX_BUFFER_FORMAT, // isn't actually a dx8 command. . let's playback know what format a buffer is for listing info + + DX8_SET_SAMPLER_STATE, + DX8_SET_VERTEX_DECLARATION, + DX8_CREATE_VERTEX_DECLARATION, + DX8_SET_FVF, + DX8_SET_CLIP_PLANE, + + DX8_SYNC_TOKEN, + + DX8_LOCK_VERTEX_TEXTURE, + DX8_UNLOCK_VERTEX_TEXTURE, + + DX8_SET_SCISSOR_RECT, + + DX8_NUM_RECORDING_COMMANDS +}; + +#ifdef RECORDING + +void RecordCommand( RecordingCommands_t cmd, int numargs ); +void RecordArgument( void const* pMemory, int size ); +void FinishRecording( void ); + +inline void RecordInt( int i ) +{ + RecordArgument( &i, sizeof(int) ); +} + +inline void RecordFloat( float f ) +{ + RecordArgument( &f, sizeof(float) ); +} + +# define RECORD_COMMAND( _cmd, _numargs ) RecordCommand( _cmd, _numargs ) +# define RECORD_INT( _int ) RecordInt( _int ) +# define RECORD_FLOAT( _float ) RecordFloat( _float ) +# define RECORD_STRING( _string ) RecordArgument( _string, strlen(_string) + 1 ) +# define RECORD_STRUCT( _struct, _size ) RecordArgument( _struct, _size ) + +# define RECORD_RENDER_STATE( _state, _val ) \ + RECORD_COMMAND( DX8_SET_RENDER_STATE, 2 ); \ + RECORD_INT( _state ); \ + RECORD_INT( _val ) + +# define RECORD_TEXTURE_STAGE_STATE( _stage, _state, _val ) \ + RECORD_COMMAND( DX8_SET_TEXTURE_STAGE_STATE, 3 ); \ + RECORD_INT( _stage ); \ + RECORD_INT( _state ); \ + RECORD_INT( _val ) + +# define RECORD_SAMPLER_STATE( _stage, _state, _val ) \ + RECORD_COMMAND( DX8_SET_SAMPLER_STATE, 3 ); \ + RECORD_INT( _stage ); \ + RECORD_INT( _state ); \ + RECORD_INT( _val ) + +# ifdef RECORD_DEBUG_STRINGS +# define RECORD_DEBUG_STRING( _str ) \ + RECORD_COMMAND( DX8_DEBUG_STRING, 1 ); \ + RECORD_STRING( _str ) +# else +# define RECORD_DEBUG_STRING( _str ) 0 +# endif + +#else // not RECORDING + +# undef RECORD_TEXTURES + +# define RECORD_COMMAND( _cmd, _numargs ) 0 +# define RECORD_INT( _int ) 0 +# define RECORD_FLOAT( _float ) 0 +# define RECORD_STRING( _string ) 0 +# define RECORD_STRUCT( _struct, _size ) 0 +# define RECORD_RENDER_STATE( _state, _val ) 0 +# define RECORD_TEXTURE_STAGE_STATE( _stage, _state, _val ) 0 +# define RECORD_SAMPLER_STATE( _stage, _state, _val ) 0 +# define RECORD_DEBUG_STRING( _str ) 0 + +#endif // RECORDING + +#endif // RECORDING_H diff --git a/materialsystem/shaderapidx9/shaderapi_global.h b/materialsystem/shaderapidx9/shaderapi_global.h new file mode 100644 index 0000000..ae8f6ab --- /dev/null +++ b/materialsystem/shaderapidx9/shaderapi_global.h @@ -0,0 +1,105 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//===========================================================================// + +#ifndef SHADERAPI_GLOBAL_H +#define SHADERAPI_GLOBAL_H + +#ifdef _WIN32 +#pragma once +#endif + +#include "tier0/threadtools.h" + +//----------------------------------------------------------------------------- +// Use this to fill in structures with the current board state +//----------------------------------------------------------------------------- +#ifdef _DEBUG +#define DEBUG_BOARD_STATE 0 +#endif + + +//----------------------------------------------------------------------------- +// Forward declarations +//----------------------------------------------------------------------------- +class IShaderUtil; +class CShaderDeviceBase; +class CShaderDeviceMgrBase; +class CShaderAPIBase; +class IShaderShadow; + + +//----------------------------------------------------------------------------- +// Global interfaces +//----------------------------------------------------------------------------- +extern IShaderUtil* g_pShaderUtil; +inline IShaderUtil* ShaderUtil() +{ + return g_pShaderUtil; +} + +extern CShaderDeviceBase *g_pShaderDevice; +extern CShaderDeviceMgrBase *g_pShaderDeviceMgr; +extern CShaderAPIBase *g_pShaderAPI; +extern IShaderShadow *g_pShaderShadow; + + +//----------------------------------------------------------------------------- +// Memory debugging +//----------------------------------------------------------------------------- +#define MEM_ALLOC_D3D_CREDIT() MEM_ALLOC_CREDIT_("D3D:" __FILE__) +#define BEGIN_D3D_ALLOCATION() MemAlloc_PushAllocDbgInfo("D3D:" __FILE__, __LINE__) +#define END_D3D_ALLOCATION() MemAlloc_PopAllocDbgInfo() + + +//----------------------------------------------------------------------------- +// Threading +//----------------------------------------------------------------------------- +extern bool g_bUseShaderMutex; + +//#define USE_SHADER_DISALLOW 1 +//#define STRICT_MT_SHADERAPI 1 + +#if defined(_DEBUG) +#if !defined(STRICT_MT_SHADERAPI) +#define UNCONDITIONAL_MT_SHADERAPI 1 +#endif +#else +#if !defined(STRICT_MT_SHADERAPI) && !defined(UNCONDITIONAL_MT_SHADERAPI) +#define ST_SHADERAPI 1 +#endif +#endif + + +#if defined(ST_SHADERAPI) +typedef CThreadNullMutex CShaderMutex; +#elif defined(STRICT_MT_SHADERAPI) +typedef CThreadConditionalMutex, &g_bUseShaderMutex> CShaderMutex; +#elif defined(UNCONDITIONAL_MT_SHADERAPI) +typedef CThreadFastMutex CShaderMutex; +#else +typedef CThreadConditionalMutex CShaderMutex; +#endif + +extern CShaderMutex g_ShaderMutex; + +extern bool g_bShaderAccessDisallowed; + +#ifdef USE_SHADER_DISALLOW +#define TestShaderPermission() do { if ( (!g_bUseShaderMutex || g_ShaderMutex.GetDepth() == 0) && g_bShaderAccessDisallowed ) { ExecuteOnce( DebuggerBreakIfDebugging() ); } } while (0) +#define LOCK_SHADERAPI() TestShaderPermission(); AUTO_LOCK_( CShaderMutex, g_ShaderMutex ) +#define LockShaderMutex() TestShaderPermission(); g_ShaderMutex.Lock(); +#define UnlockShaderMutex() TestShaderPermission(); g_ShaderMutex.Unlock(); +#else +#define TestShaderPermission() ((void)0) +#define LOCK_SHADERAPI() ((void)0) +#define LockShaderMutex() ((void)0) +#define UnlockShaderMutex() ((void)0) +#endif + + +#endif // SHADERAPI_GLOBAL_H diff --git a/materialsystem/shaderapidx9/shaderapibase.cpp b/materialsystem/shaderapidx9/shaderapibase.cpp new file mode 100644 index 0000000..1563a12 --- /dev/null +++ b/materialsystem/shaderapidx9/shaderapibase.cpp @@ -0,0 +1,42 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//===========================================================================// + +#undef PROTECTED_THINGS_ENABLE // prevent warnings when windows.h gets included + +#include "shaderapibase.h" +#include "shaderapi/ishaderutil.h" + + +//----------------------------------------------------------------------------- +// +// The Base implementation of the shader render class +// +//----------------------------------------------------------------------------- + + +//----------------------------------------------------------------------------- +// constructor, destructor +//----------------------------------------------------------------------------- +CShaderAPIBase::CShaderAPIBase() +{ +} + +CShaderAPIBase::~CShaderAPIBase() +{ +} + + +//----------------------------------------------------------------------------- +// Methods of IShaderDynamicAPI +//----------------------------------------------------------------------------- +void CShaderAPIBase::GetCurrentColorCorrection( ShaderColorCorrectionInfo_t* pInfo ) +{ + g_pShaderUtil->GetCurrentColorCorrection( pInfo ); +} + + diff --git a/materialsystem/shaderapidx9/shaderapibase.h b/materialsystem/shaderapidx9/shaderapibase.h new file mode 100644 index 0000000..fe8d928 --- /dev/null +++ b/materialsystem/shaderapidx9/shaderapibase.h @@ -0,0 +1,86 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//===========================================================================// + +#ifndef SHADERRENDERBASE_H +#define SHADERRENDERBASE_H + +#ifdef _WIN32 +#pragma once +#endif + + +#include "togl/rendermechanism.h" +#include "shaderapi/ishaderapi.h" +#include "shaderapi_global.h" +#include "locald3dtypes.h" + +// Colors for PIX graphs +#define PIX_VALVE_ORANGE 0xFFF5940F + + +//----------------------------------------------------------------------------- +// The Base implementation of the shader rendering interface +//----------------------------------------------------------------------------- +class CShaderAPIBase : public IShaderAPI +{ +public: + // constructor, destructor + CShaderAPIBase(); + virtual ~CShaderAPIBase(); + + // Called when the device is initializing or shutting down + virtual bool OnDeviceInit() = 0; + virtual void OnDeviceShutdown() = 0; + + // Pix events + virtual void BeginPIXEvent( unsigned long color, const char *szName ) = 0; + virtual void EndPIXEvent() = 0; + virtual void AdvancePIXFrame() = 0; + + // Release, reacquire objects + virtual void ReleaseShaderObjects() = 0; + virtual void RestoreShaderObjects() = 0; + + // Resets the render state to its well defined initial value + virtual void ResetRenderState( bool bFullReset = true ) = 0; + + // Returns a d3d texture associated with a texture handle + virtual IDirect3DBaseTexture* GetD3DTexture( ShaderAPITextureHandle_t hTexture ) = 0; + + // Queues a non-full reset of render state next BeginFrame. + virtual void QueueResetRenderState() = 0; + + // Methods of IShaderDynamicAPI +public: + virtual void GetCurrentColorCorrection( ShaderColorCorrectionInfo_t* pInfo ); + +protected: +}; + + +//----------------------------------------------------------------------------- +// Pix measurement class +//----------------------------------------------------------------------------- +class CPixEvent +{ +public: + CPixEvent( unsigned long color, const char *szName ) + { + if ( g_pShaderAPI ) + g_pShaderAPI->BeginPIXEvent( color, szName ); + } + + ~CPixEvent() + { + if ( g_pShaderAPI ) + g_pShaderAPI->EndPIXEvent(); + } +}; + + +#endif // SHADERRENDERBASE_H diff --git a/materialsystem/shaderapidx9/shaderapidx10.cpp b/materialsystem/shaderapidx9/shaderapidx10.cpp new file mode 100644 index 0000000..9bd1039 --- /dev/null +++ b/materialsystem/shaderapidx9/shaderapidx10.cpp @@ -0,0 +1,1461 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//===========================================================================// + +#include "shaderapidx10.h" +#include "shaderapibase.h" +#include "shaderapi/ishaderutil.h" +#include "materialsystem/idebugtextureinfo.h" +#include "materialsystem/materialsystem_config.h" +#include "meshdx10.h" +#include "shadershadowdx10.h" +#include "shaderdevicedx10.h" +#include "shaderapidx10_global.h" +#include "imaterialinternal.h" + + +//----------------------------------------------------------------------------- +// Methods related to queuing functions to be called prior to rendering +//----------------------------------------------------------------------------- +CFunctionCommit::CFunctionCommit() +{ + m_pCommitFlags = NULL; + m_nCommitBufferSize = 0; +} + +CFunctionCommit::~CFunctionCommit() +{ + if ( m_pCommitFlags ) + { + delete[] m_pCommitFlags; + m_pCommitFlags = NULL; + } +} + +void CFunctionCommit::Init( int nFunctionCount ) +{ + m_nCommitBufferSize = ( nFunctionCount + 7 ) >> 3; + Assert( !m_pCommitFlags ); + m_pCommitFlags = new unsigned char[ m_nCommitBufferSize ]; + memset( m_pCommitFlags, 0, m_nCommitBufferSize ); +} + + +//----------------------------------------------------------------------------- +// Methods related to queuing functions to be called per-(pMesh->Draw call) or per-pass +//----------------------------------------------------------------------------- +inline bool CFunctionCommit::IsCommitFuncInUse( int nFunc ) const +{ + Assert( nFunc >> 3 < m_nCommitBufferSize ); + return ( m_pCommitFlags[ nFunc >> 3 ] & ( 1 << ( nFunc & 0x7 ) ) ) != 0; +} + +inline void CFunctionCommit::MarkCommitFuncInUse( int nFunc ) +{ + Assert( nFunc >> 3 < m_nCommitBufferSize ); + m_pCommitFlags[ nFunc >> 3 ] |= 1 << ( nFunc & 0x7 ); +} + +inline void CFunctionCommit::AddCommitFunc( StateCommitFunc_t f ) +{ + m_CommitFuncs.AddToTail( f ); +} + + +//----------------------------------------------------------------------------- +// Clears all commit functions +//----------------------------------------------------------------------------- +inline void CFunctionCommit::ClearAllCommitFuncs( ) +{ + memset( m_pCommitFlags, 0, m_nCommitBufferSize ); + m_CommitFuncs.RemoveAll(); +} + + +//----------------------------------------------------------------------------- +// Calls all commit functions in a particular list +//----------------------------------------------------------------------------- +void CFunctionCommit::CallCommitFuncs( ID3D10Device *pDevice, const ShaderStateDx10_t &desiredState, ShaderStateDx10_t ¤tState, bool bForce ) +{ + int nCount = m_CommitFuncs.Count(); + for ( int i = 0; i < nCount; ++i ) + { + m_CommitFuncs[i]( pDevice, desiredState, currentState, bForce ); + } + + ClearAllCommitFuncs( ); +} + + +//----------------------------------------------------------------------------- +// Helpers for commit functions +//----------------------------------------------------------------------------- +#define ADD_COMMIT_FUNC( _func_name ) \ + if ( !m_Commit.IsCommitFuncInUse( COMMIT_FUNC_ ## _func_name ) ) \ + { \ + m_Commit.AddCommitFunc( _func_name ); \ + m_Commit.MarkCommitFuncInUse( COMMIT_FUNC_ ## _func_name ); \ + } + +#define ADD_RENDERSTATE_FUNC( _func_name, _state, _val ) \ + if ( m_bResettingRenderState || ( m_DesiredState. ## _state != _val ) ) \ + { \ + m_DesiredState. ## _state = _val; \ + ADD_COMMIT_FUNC( _func_name ) \ + } + +#define IMPLEMENT_RENDERSTATE_FUNC( _func_name, _state, _d3dFunc ) \ + static void _func_name( ID3D10Device *pDevice, const ShaderStateDx10_t &desiredState, ShaderStateDx10_t ¤tState, bool bForce ) \ + { \ + if ( bForce || ( desiredState. ## _state != currentState. ## _state ) ) \ + { \ + pDevice->_d3dFunc( desiredState. ## _state ); \ + currentState. ## _state = desiredState. ## _state; \ + } \ + } + +//----------------------------------------------------------------------------- +// D3D state setting methods +//----------------------------------------------------------------------------- + +// NOTE: For each commit func you create, add to this enumeration. +enum CommitFunc_t +{ + COMMIT_FUNC_CommitSetViewports = 0, + COMMIT_FUNC_CommitSetVertexShader, + COMMIT_FUNC_CommitSetGeometryShader, + COMMIT_FUNC_CommitSetPixelShader, + COMMIT_FUNC_CommitSetVertexBuffer, + COMMIT_FUNC_CommitSetIndexBuffer, + COMMIT_FUNC_CommitSetInputLayout, + COMMIT_FUNC_CommitSetTopology, + COMMIT_FUNC_CommitSetRasterState, + + COMMIT_FUNC_COUNT, +}; + +IMPLEMENT_RENDERSTATE_FUNC( CommitSetTopology, m_Topology, IASetPrimitiveTopology ) +IMPLEMENT_RENDERSTATE_FUNC( CommitSetVertexShader, m_pVertexShader, VSSetShader ) +IMPLEMENT_RENDERSTATE_FUNC( CommitSetGeometryShader, m_pGeometryShader, GSSetShader ) +IMPLEMENT_RENDERSTATE_FUNC( CommitSetPixelShader, m_pPixelShader, PSSetShader ) + +static void CommitSetInputLayout( ID3D10Device *pDevice, const ShaderStateDx10_t &desiredState, ShaderStateDx10_t ¤tState, bool bForce ) +{ + const ShaderInputLayoutStateDx10_t& newState = desiredState.m_InputLayout; + if ( bForce || memcmp( &newState, ¤tState.m_InputLayout, sizeof(ShaderInputLayoutStateDx10_t) ) ) + { + // FIXME: Deal with multiple streams + ID3D10InputLayout *pInputLayout = g_pShaderDeviceDx10->GetInputLayout( + newState.m_hVertexShader, newState.m_pVertexDecl[0] ); + pDevice->IASetInputLayout( pInputLayout ); + + currentState.m_InputLayout = newState; + } +} + +static void CommitSetViewports( ID3D10Device *pDevice, const ShaderStateDx10_t &desiredState, ShaderStateDx10_t ¤tState, bool bForce ) +{ + bool bChanged = bForce || ( desiredState.m_nViewportCount != currentState.m_nViewportCount ); + if ( !bChanged && desiredState.m_nViewportCount > 0 ) + { + bChanged = memcmp( desiredState.m_pViewports, currentState.m_pViewports, + desiredState.m_nViewportCount * sizeof( D3D10_VIEWPORT ) ) != 0; + } + + if ( !bChanged ) + return; + + pDevice->RSSetViewports( desiredState.m_nViewportCount, desiredState.m_pViewports ); + currentState.m_nViewportCount = desiredState.m_nViewportCount; + +#ifdef _DEBUG + memset( currentState.m_pViewports, 0xDD, sizeof( currentState.m_pViewports ) ); +#endif + + memcpy( currentState.m_pViewports, desiredState.m_pViewports, + desiredState.m_nViewportCount * sizeof( D3D10_VIEWPORT ) ); +} + +static void CommitSetIndexBuffer( ID3D10Device *pDevice, const ShaderStateDx10_t &desiredState, ShaderStateDx10_t ¤tState, bool bForce ) +{ + const ShaderIndexBufferStateDx10_t &newState = desiredState.m_IndexBuffer; + bool bChanged = bForce || memcmp( &newState, ¤tState.m_IndexBuffer, sizeof(ShaderIndexBufferStateDx10_t) ); + if ( !bChanged ) + return; + + pDevice->IASetIndexBuffer( newState.m_pBuffer, newState.m_Format, newState.m_nOffset ); + memcpy( ¤tState.m_IndexBuffer, &newState, sizeof( ShaderIndexBufferStateDx10_t ) ); +} + +static void CommitSetVertexBuffer( ID3D10Device *pDevice, const ShaderStateDx10_t &desiredState, ShaderStateDx10_t ¤tState, bool bForce ) +{ + ID3D10Buffer *ppVertexBuffers[ MAX_DX10_STREAMS ]; + UINT pStrides[ MAX_DX10_STREAMS ]; + UINT pOffsets[ MAX_DX10_STREAMS ]; + + UINT nFirstBuffer = 0; + UINT nBufferCount = 0; + bool bInMatch = true; + for ( int i = 0; i < MAX_DX10_STREAMS; ++i ) + { + const ShaderVertexBufferStateDx10_t &newState = desiredState.m_pVertexBuffer[i]; + bool bMatch = !bForce && !memcmp( &newState, ¤tState.m_pVertexBuffer[i], sizeof(ShaderVertexBufferStateDx10_t) ); + if ( !bMatch ) + { + ppVertexBuffers[i] = newState.m_pBuffer; + pStrides[i] = newState.m_nStride; + pOffsets[i] = newState.m_nOffset; + ++nBufferCount; + memcpy( ¤tState.m_pVertexBuffer[i], &newState, sizeof( ShaderVertexBufferStateDx10_t ) ); + } + + if ( bInMatch ) + { + if ( !bMatch ) + { + bInMatch = false; + nFirstBuffer = i; + } + continue; + } + + if ( bMatch ) + { + bInMatch = true; + pDevice->IASetVertexBuffers( nFirstBuffer, nBufferCount, + &ppVertexBuffers[nFirstBuffer], &pStrides[nFirstBuffer], &pOffsets[nFirstBuffer] ); + nBufferCount = 0; + } + } + + if ( !bInMatch ) + { + pDevice->IASetVertexBuffers( nFirstBuffer, nBufferCount, + &ppVertexBuffers[nFirstBuffer], &pStrides[nFirstBuffer], &pOffsets[nFirstBuffer] ); + } +} + +static void GenerateRasterizerDesc( D3D10_RASTERIZER_DESC* pDesc, const ShaderRasterState_t& state ) +{ + pDesc->FillMode = ( state.m_FillMode == SHADER_FILL_WIREFRAME ) ? D3D10_FILL_WIREFRAME : D3D10_FILL_SOLID; + + // Cull state + if ( state.m_bCullEnable ) + { + pDesc->CullMode = D3D10_CULL_NONE; + } + else + { + pDesc->CullMode = ( state.m_CullMode == MATERIAL_CULLMODE_CW ) ? D3D10_CULL_BACK : D3D10_CULL_FRONT; + } + pDesc->FrontCounterClockwise = TRUE; + + // Depth bias state + if ( !state.m_bDepthBias ) + { + pDesc->DepthBias = 0; + pDesc->DepthBiasClamp = 0.0f; + pDesc->SlopeScaledDepthBias = 0.0f; + pDesc->DepthClipEnable = FALSE; + } + else + { + // FIXME: Implement! Read ConVars + } + + pDesc->ScissorEnable = state.m_bScissorEnable ? TRUE : FALSE; + pDesc->MultisampleEnable = state.m_bMultisampleEnable ? TRUE : FALSE; + pDesc->AntialiasedLineEnable = FALSE; +} + +static void CommitSetRasterState( ID3D10Device *pDevice, const ShaderStateDx10_t &desiredState, ShaderStateDx10_t ¤tState, bool bForce ) +{ + const ShaderRasterState_t& newState = desiredState.m_RasterState; + if ( bForce || memcmp( &newState, ¤tState.m_RasterState, sizeof(ShaderRasterState_t) ) ) + { + // Clear out the existing state + if ( currentState.m_pRasterState ) + { + currentState.m_pRasterState->Release(); + } + + D3D10_RASTERIZER_DESC desc; + GenerateRasterizerDesc( &desc, newState ); + + // NOTE: This does a search for existing matching state objects + ID3D10RasterizerState *pState = NULL; + HRESULT hr = pDevice->CreateRasterizerState( &desc, &pState ); + if ( !FAILED(hr) ) + { + Warning( "Unable to create rasterizer state object!\n" ); + } + + pDevice->RSSetState( pState ); + + currentState.m_pRasterState = pState; + memcpy( ¤tState.m_RasterState, &newState, sizeof( ShaderRasterState_t ) ); + } +} + + +//----------------------------------------------------------------------------- +// +// Shader API Dx10 +// +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +// Class Factory +//----------------------------------------------------------------------------- +static CShaderAPIDx10 s_ShaderAPIDx10; +CShaderAPIDx10* g_pShaderAPIDx10 = &s_ShaderAPIDx10; + +EXPOSE_SINGLE_INTERFACE_GLOBALVAR( CShaderAPIDx10, IShaderAPI, + SHADERAPI_INTERFACE_VERSION, s_ShaderAPIDx10 ) + +EXPOSE_SINGLE_INTERFACE_GLOBALVAR( CShaderAPIDx10, IDebugTextureInfo, + DEBUG_TEXTURE_INFO_VERSION, s_ShaderAPIDx10 ) + + +//----------------------------------------------------------------------------- +// Constructor, destructor +//----------------------------------------------------------------------------- +CShaderAPIDx10::CShaderAPIDx10() +{ + m_bResettingRenderState = false; + m_Commit.Init( COMMIT_FUNC_COUNT ); + ClearShaderState( &m_DesiredState ); + ClearShaderState( &m_CurrentState ); +} + +CShaderAPIDx10::~CShaderAPIDx10() +{ +} + + +//----------------------------------------------------------------------------- +// Clears the shader state to a well-defined value +//----------------------------------------------------------------------------- +void CShaderAPIDx10::ClearShaderState( ShaderStateDx10_t* pState ) +{ + memset( pState, 0, sizeof( ShaderStateDx10_t ) ); +} + + +//----------------------------------------------------------------------------- +// Resets the render state +//----------------------------------------------------------------------------- +void CShaderAPIDx10::ResetRenderState( bool bFullReset ) +{ + D3D10_RASTERIZER_DESC rDesc; + memset( &rDesc, 0, sizeof(rDesc) ); + rDesc.FillMode = D3D10_FILL_SOLID; + rDesc.CullMode = D3D10_CULL_NONE; + rDesc.FrontCounterClockwise = TRUE; // right-hand rule + + ID3D10RasterizerState *pRasterizerState; + HRESULT hr = D3D10Device()->CreateRasterizerState( &rDesc, &pRasterizerState ); + Assert( !FAILED(hr) ); + D3D10Device()->RSSetState( pRasterizerState ); + + D3D10_DEPTH_STENCIL_DESC dsDesc; + memset( &dsDesc, 0, sizeof(dsDesc) ); + + ID3D10DepthStencilState *pDepthStencilState; + hr = D3D10Device()->CreateDepthStencilState( &dsDesc, &pDepthStencilState ); + Assert( !FAILED(hr) ); + D3D10Device()->OMSetDepthStencilState( pDepthStencilState, 0 ); + + D3D10_BLEND_DESC bDesc; + memset( &bDesc, 0, sizeof(bDesc) ); + bDesc.SrcBlend = D3D10_BLEND_ONE; + bDesc.DestBlend = D3D10_BLEND_ZERO; + bDesc.BlendOp = D3D10_BLEND_OP_ADD; + bDesc.SrcBlendAlpha = D3D10_BLEND_ONE; + bDesc.DestBlendAlpha = D3D10_BLEND_ZERO; + bDesc.BlendOpAlpha = D3D10_BLEND_OP_ADD; + bDesc.RenderTargetWriteMask[0] = D3D10_COLOR_WRITE_ENABLE_ALL; + + FLOAT pBlendFactor[4] = { 1.0f, 1.0f, 1.0f, 1.0f }; + ID3D10BlendState *pBlendState; + hr = D3D10Device()->CreateBlendState( &bDesc, &pBlendState ); + Assert( !FAILED(hr) ); + D3D10Device()->OMSetBlendState( pBlendState, pBlendFactor, 0xFFFFFFFF ); +} + + +//----------------------------------------------------------------------------- +// Commits queued-up state change requests +//----------------------------------------------------------------------------- +void CShaderAPIDx10::CommitStateChanges( bool bForce ) +{ + // Don't bother committing anything if we're deactivated + if ( g_pShaderDevice->IsDeactivated() ) + return; + + m_Commit.CallCommitFuncs( D3D10Device(), m_DesiredState, m_CurrentState, bForce ); +} + + +//----------------------------------------------------------------------------- +// Methods of IShaderDynamicAPI +//----------------------------------------------------------------------------- +void CShaderAPIDx10::GetBackBufferDimensions( int& nWidth, int& nHeight ) const +{ + g_pShaderDeviceDx10->GetBackBufferDimensions( nWidth, nHeight ); +} + + +//----------------------------------------------------------------------------- +// Viewport-related methods +//----------------------------------------------------------------------------- +void CShaderAPIDx10::SetViewports( int nCount, const ShaderViewport_t* pViewports ) +{ + nCount = min( nCount, MAX_DX10_VIEWPORTS ); + m_DesiredState.m_nViewportCount = nCount; + + for ( int i = 0; i < nCount; ++i ) + { + Assert( pViewports[i].m_nVersion == SHADER_VIEWPORT_VERSION ); + + D3D10_VIEWPORT& viewport = m_DesiredState.m_pViewports[i]; + viewport.TopLeftX = pViewports[i].m_nTopLeftX; + viewport.TopLeftY = pViewports[i].m_nTopLeftY; + viewport.Width = pViewports[i].m_nWidth; + viewport.Height = pViewports[i].m_nHeight; + viewport.MinDepth = pViewports[i].m_flMinZ; + viewport.MaxDepth = pViewports[i].m_flMaxZ; + } + + ADD_COMMIT_FUNC( CommitSetViewports ); +} + +int CShaderAPIDx10::GetViewports( ShaderViewport_t* pViewports, int nMax ) const +{ + int nCount = m_DesiredState.m_nViewportCount; + if ( pViewports && nMax ) + { + nCount = min( nCount, nMax ); + memcpy( pViewports, m_DesiredState.m_pViewports, nCount * sizeof( ShaderViewport_t ) ); + } + return nCount; +} + + +//----------------------------------------------------------------------------- +// Methods related to state objects +//----------------------------------------------------------------------------- +void CShaderAPIDx10::SetRasterState( const ShaderRasterState_t& state ) +{ + if ( memcmp( &state, &m_DesiredState.m_RasterState, sizeof(ShaderRasterState_t) ) ) + { + memcpy( &m_DesiredState.m_RasterState, &state, sizeof(ShaderRasterState_t) ); + ADD_COMMIT_FUNC( CommitSetRasterState ); + } +} + + +//----------------------------------------------------------------------------- +// Methods related to clearing buffers +//----------------------------------------------------------------------------- +void CShaderAPIDx10::ClearColor3ub( unsigned char r, unsigned char g, unsigned char b ) +{ + m_DesiredState.m_ClearColor[0] = r / 255.0f; + m_DesiredState.m_ClearColor[1] = g / 255.0f; + m_DesiredState.m_ClearColor[2] = b / 255.0f; + m_DesiredState.m_ClearColor[3] = 1.0f; +} + +void CShaderAPIDx10::ClearColor4ub( unsigned char r, unsigned char g, unsigned char b, unsigned char a ) +{ + m_DesiredState.m_ClearColor[0] = r / 255.0f; + m_DesiredState.m_ClearColor[1] = g / 255.0f; + m_DesiredState.m_ClearColor[2] = b / 255.0f; + m_DesiredState.m_ClearColor[3] = a / 255.0f; +} + +void CShaderAPIDx10::ClearBuffers( bool bClearColor, bool bClearDepth, bool bClearStencil, int renderTargetWidth, int renderTargetHeight ) +{ + // NOTE: State change commit isn't necessary since clearing doesn't use state +// CommitStateChanges(); + + // FIXME: This implementation is totally bust0red [doesn't guarantee exact color specified] + if ( bClearColor ) + { + D3D10Device()->ClearRenderTargetView( D3D10RenderTargetView(), m_DesiredState.m_ClearColor ); + } +} + + +//----------------------------------------------------------------------------- +// Methods related to binding shaders +//----------------------------------------------------------------------------- +void CShaderAPIDx10::BindVertexShader( VertexShaderHandle_t hVertexShader ) +{ + ID3D10VertexShader *pVertexShader = g_pShaderDeviceDx10->GetVertexShader( hVertexShader ); + ADD_RENDERSTATE_FUNC( CommitSetVertexShader, m_pVertexShader, pVertexShader ); + + if ( m_bResettingRenderState || ( m_DesiredState.m_InputLayout.m_hVertexShader != hVertexShader ) ) + { + m_DesiredState.m_InputLayout.m_hVertexShader = hVertexShader; + ADD_COMMIT_FUNC( CommitSetInputLayout ); + } +} + +void CShaderAPIDx10::BindGeometryShader( GeometryShaderHandle_t hGeometryShader ) +{ + ID3D10GeometryShader *pGeometryShader = g_pShaderDeviceDx10->GetGeometryShader( hGeometryShader ); + ADD_RENDERSTATE_FUNC( CommitSetGeometryShader, m_pGeometryShader, pGeometryShader ); +} + +void CShaderAPIDx10::BindPixelShader( PixelShaderHandle_t hPixelShader ) +{ + ID3D10PixelShader *pPixelShader = g_pShaderDeviceDx10->GetPixelShader( hPixelShader ); + ADD_RENDERSTATE_FUNC( CommitSetPixelShader, m_pPixelShader, pPixelShader ); +} + +void CShaderAPIDx10::BindVertexBuffer( int nStreamID, IVertexBuffer *pVertexBuffer, int nOffsetInBytes, int nFirstVertex, int nVertexCount, VertexFormat_t fmt, int nRepetitions ) +{ + // FIXME: What to do about repetitions? + CVertexBufferDx10 *pVertexBufferDx10 = static_cast( pVertexBuffer ); + + ShaderVertexBufferStateDx10_t state; + if ( pVertexBufferDx10 ) + { + state.m_pBuffer = pVertexBufferDx10->GetDx10Buffer(); + state.m_nStride = pVertexBufferDx10->VertexSize(); + } + else + { + state.m_pBuffer = NULL; + state.m_nStride = 0; + } + state.m_nOffset = nOffsetInBytes; + + if ( m_bResettingRenderState || memcmp( &m_DesiredState.m_pVertexBuffer[ nStreamID ], &state, sizeof( ShaderVertexBufferStateDx10_t ) ) ) + { + m_DesiredState.m_pVertexBuffer[ nStreamID ] = state; + ADD_COMMIT_FUNC( CommitSetVertexBuffer ); + } + + if ( m_bResettingRenderState || ( m_DesiredState.m_InputLayout.m_pVertexDecl[ nStreamID ] != fmt ) ) + { + m_DesiredState.m_InputLayout.m_pVertexDecl[ nStreamID ] = fmt; + ADD_COMMIT_FUNC( CommitSetInputLayout ); + } +} + +void CShaderAPIDx10::BindIndexBuffer( IIndexBuffer *pIndexBuffer, int nOffsetInBytes ) +{ + CIndexBufferDx10 *pIndexBufferDx10 = static_cast( pIndexBuffer ); + + ShaderIndexBufferStateDx10_t state; + if ( pIndexBufferDx10 ) + { + state.m_pBuffer = pIndexBufferDx10->GetDx10Buffer(); + state.m_Format = ( pIndexBufferDx10->GetIndexFormat() == MATERIAL_INDEX_FORMAT_16BIT ) ? + DXGI_FORMAT_R16_UINT : DXGI_FORMAT_R32_UINT; + } + else + { + state.m_pBuffer = NULL; + state.m_Format = DXGI_FORMAT_R16_UINT; + } + state.m_nOffset = nOffsetInBytes; + + ADD_RENDERSTATE_FUNC( CommitSetIndexBuffer, m_IndexBuffer, state ); +} + + +//----------------------------------------------------------------------------- +// Unbinds resources because they are about to be deleted +//----------------------------------------------------------------------------- +void CShaderAPIDx10::Unbind( VertexShaderHandle_t hShader ) +{ + ID3D10VertexShader* pShader = g_pShaderDeviceDx10->GetVertexShader( hShader ); + Assert ( pShader ); + if ( m_DesiredState.m_pVertexShader == pShader ) + { + BindVertexShader( VERTEX_SHADER_HANDLE_INVALID ); + } + if ( m_CurrentState.m_pVertexShader == pShader ) + { + CommitStateChanges(); + } +} + +void CShaderAPIDx10::Unbind( GeometryShaderHandle_t hShader ) +{ + ID3D10GeometryShader* pShader = g_pShaderDeviceDx10->GetGeometryShader( hShader ); + Assert ( pShader ); + if ( m_DesiredState.m_pGeometryShader == pShader ) + { + BindGeometryShader( GEOMETRY_SHADER_HANDLE_INVALID ); + } + if ( m_CurrentState.m_pGeometryShader == pShader ) + { + CommitStateChanges(); + } +} + +void CShaderAPIDx10::Unbind( PixelShaderHandle_t hShader ) +{ + ID3D10PixelShader* pShader = g_pShaderDeviceDx10->GetPixelShader( hShader ); + Assert ( pShader ); + if ( m_DesiredState.m_pPixelShader == pShader ) + { + BindPixelShader( PIXEL_SHADER_HANDLE_INVALID ); + } + if ( m_CurrentState.m_pPixelShader == pShader ) + { + CommitStateChanges(); + } +} + +void CShaderAPIDx10::UnbindVertexBuffer( ID3D10Buffer *pBuffer ) +{ + Assert ( pBuffer ); + + for ( int i = 0; i < MAX_DX10_STREAMS; ++i ) + { + if ( m_DesiredState.m_pVertexBuffer[i].m_pBuffer == pBuffer ) + { + BindVertexBuffer( i, NULL, 0, 0, 0, VERTEX_POSITION, 0 ); + } + } + for ( int i = 0; i < MAX_DX10_STREAMS; ++i ) + { + if ( m_CurrentState.m_pVertexBuffer[i].m_pBuffer == pBuffer ) + { + CommitStateChanges(); + break; + } + } +} + +void CShaderAPIDx10::UnbindIndexBuffer( ID3D10Buffer *pBuffer ) +{ + Assert ( pBuffer ); + + if ( m_DesiredState.m_IndexBuffer.m_pBuffer == pBuffer ) + { + BindIndexBuffer( NULL, 0 ); + } + if ( m_CurrentState.m_IndexBuffer.m_pBuffer == pBuffer ) + { + CommitStateChanges(); + } +} + + +//----------------------------------------------------------------------------- +// Sets the topology state +//----------------------------------------------------------------------------- +void CShaderAPIDx10::SetTopology( MaterialPrimitiveType_t topology ) +{ + D3D10_PRIMITIVE_TOPOLOGY d3dTopology; + switch( topology ) + { + case MATERIAL_POINTS: + d3dTopology = D3D10_PRIMITIVE_TOPOLOGY_POINTLIST; + break; + + case MATERIAL_LINES: + d3dTopology = D3D10_PRIMITIVE_TOPOLOGY_LINELIST; + break; + + case MATERIAL_TRIANGLES: + d3dTopology = D3D10_PRIMITIVE_TOPOLOGY_TRIANGLELIST; + break; + + case MATERIAL_TRIANGLE_STRIP: + d3dTopology = D3D10_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP; + break; + + case MATERIAL_LINE_STRIP: + d3dTopology = D3D10_PRIMITIVE_TOPOLOGY_LINESTRIP; + break; + + default: + case MATERIAL_LINE_LOOP: + case MATERIAL_POLYGON: + case MATERIAL_QUADS: + Assert( 0 ); + d3dTopology = D3D10_PRIMITIVE_TOPOLOGY_UNDEFINED; + break; + } + + ADD_RENDERSTATE_FUNC( CommitSetTopology, m_Topology, d3dTopology ); +} + + +//----------------------------------------------------------------------------- +// Main entry point for rendering +//----------------------------------------------------------------------------- +void CShaderAPIDx10::Draw( MaterialPrimitiveType_t primitiveType, int nFirstIndex, int nIndexCount ) +{ + SetTopology( primitiveType ); + + CommitStateChanges(); + + // FIXME: How do I set the base vertex location!? + D3D10Device()->DrawIndexed( (UINT)nIndexCount, (UINT)nFirstIndex, 0 ); +} + + +//----------------------------------------------------------------------------- +// +// Abandon all hope below this point +// +//----------------------------------------------------------------------------- + +bool CShaderAPIDx10::DoRenderTargetsNeedSeparateDepthBuffer() const +{ + return false; +} + +// Can we download textures? +bool CShaderAPIDx10::CanDownloadTextures() const +{ + return false; +} + +// Used to clear the transition table when we know it's become invalid. +void CShaderAPIDx10::ClearSnapshots() +{ +} + +// Sets the default *dynamic* state +void CShaderAPIDx10::SetDefaultState() +{ +} + + +// Returns the snapshot id for the shader state +StateSnapshot_t CShaderAPIDx10::TakeSnapshot( ) +{ + StateSnapshot_t id = 0; + if (g_pShaderShadowDx10->m_IsTranslucent) + id |= TRANSLUCENT; + if (g_pShaderShadowDx10->m_IsAlphaTested) + id |= ALPHATESTED; + if (g_pShaderShadowDx10->m_bUsesVertexAndPixelShaders) + id |= VERTEX_AND_PIXEL_SHADERS; + if (g_pShaderShadowDx10->m_bIsDepthWriteEnabled) + id |= DEPTHWRITE; + return id; +} + +// Returns true if the state snapshot is transparent +bool CShaderAPIDx10::IsTranslucent( StateSnapshot_t id ) const +{ + return (id & TRANSLUCENT) != 0; +} + +bool CShaderAPIDx10::IsAlphaTested( StateSnapshot_t id ) const +{ + return (id & ALPHATESTED) != 0; +} + +bool CShaderAPIDx10::IsDepthWriteEnabled( StateSnapshot_t id ) const +{ + return (id & DEPTHWRITE) != 0; +} + +bool CShaderAPIDx10::UsesVertexAndPixelShaders( StateSnapshot_t id ) const +{ + return (id & VERTEX_AND_PIXEL_SHADERS) != 0; +} + +// Gets the vertex format for a set of snapshot ids +VertexFormat_t CShaderAPIDx10::ComputeVertexFormat( int numSnapshots, StateSnapshot_t* pIds ) const +{ + return 0; +} + +// Gets the vertex format for a set of snapshot ids +VertexFormat_t CShaderAPIDx10::ComputeVertexUsage( int numSnapshots, StateSnapshot_t* pIds ) const +{ + return 0; +} + +// Uses a state snapshot +void CShaderAPIDx10::UseSnapshot( StateSnapshot_t snapshot ) +{ +} + +// Sets the color to modulate by +void CShaderAPIDx10::Color3f( float r, float g, float b ) +{ +} + +void CShaderAPIDx10::Color3fv( float const* pColor ) +{ +} + +void CShaderAPIDx10::Color4f( float r, float g, float b, float a ) +{ +} + +void CShaderAPIDx10::Color4fv( float const* pColor ) +{ +} + +// Faster versions of color +void CShaderAPIDx10::Color3ub( unsigned char r, unsigned char g, unsigned char b ) +{ +} + +void CShaderAPIDx10::Color3ubv( unsigned char const* rgb ) +{ +} + +void CShaderAPIDx10::Color4ub( unsigned char r, unsigned char g, unsigned char b, unsigned char a ) +{ +} + +void CShaderAPIDx10::Color4ubv( unsigned char const* rgba ) +{ +} + +void CShaderAPIDx10::GetStandardTextureDimensions( int *pWidth, int *pHeight, StandardTextureId_t id ) +{ + ShaderUtil()->GetStandardTextureDimensions( pWidth, pHeight, id ); +} + + +// The shade mode +void CShaderAPIDx10::ShadeMode( ShaderShadeMode_t mode ) +{ +} + +// Binds a particular material to render with +void CShaderAPIDx10::Bind( IMaterial* pMaterial ) +{ +} + +// Cull mode +void CShaderAPIDx10::CullMode( MaterialCullMode_t cullMode ) +{ +} + +void CShaderAPIDx10::ForceDepthFuncEquals( bool bEnable ) +{ +} + +// Forces Z buffering on or off +void CShaderAPIDx10::OverrideDepthEnable( bool bEnable, bool bDepthEnable ) +{ +} + +void CShaderAPIDx10::OverrideAlphaWriteEnable( bool bOverrideEnable, bool bAlphaWriteEnable ) +{ +} + +void CShaderAPIDx10::OverrideColorWriteEnable( bool bOverrideEnable, bool bColorWriteEnable ) +{ +} + + +//legacy fast clipping linkage +void CShaderAPIDx10::SetHeightClipZ( float z ) +{ +} + +void CShaderAPIDx10::SetHeightClipMode( enum MaterialHeightClipMode_t heightClipMode ) +{ +} + + +// Sets the lights +void CShaderAPIDx10::SetLight( int lightNum, const LightDesc_t& desc ) +{ +} + +void CShaderAPIDx10::SetAmbientLight( float r, float g, float b ) +{ +} + +void CShaderAPIDx10::SetAmbientLightCube( Vector4D cube[6] ) +{ +} + +// Get lights +int CShaderAPIDx10::GetMaxLights( void ) const +{ + return 0; +} + +const LightDesc_t& CShaderAPIDx10::GetLight( int lightNum ) const +{ + static LightDesc_t blah; + return blah; +} + +// Render state for the ambient light cube (vertex shaders) +void CShaderAPIDx10::SetVertexShaderStateAmbientLightCube() +{ +} + +void CShaderAPIDx10::SetSkinningMatrices() +{ +} + +// Lightmap texture binding +void CShaderAPIDx10::BindLightmap( TextureStage_t stage ) +{ +} + +void CShaderAPIDx10::BindBumpLightmap( TextureStage_t stage ) +{ +} + +void CShaderAPIDx10::BindFullbrightLightmap( TextureStage_t stage ) +{ +} + +void CShaderAPIDx10::BindWhite( TextureStage_t stage ) +{ +} + +void CShaderAPIDx10::BindBlack( TextureStage_t stage ) +{ +} + +void CShaderAPIDx10::BindGrey( TextureStage_t stage ) +{ +} + +// Gets the lightmap dimensions +void CShaderAPIDx10::GetLightmapDimensions( int *w, int *h ) +{ + g_pShaderUtil->GetLightmapDimensions( w, h ); +} + +// Special system flat normal map binding. +void CShaderAPIDx10::BindFlatNormalMap( TextureStage_t stage ) +{ +} + +void CShaderAPIDx10::BindNormalizationCubeMap( TextureStage_t stage ) +{ +} + +void CShaderAPIDx10::BindSignedNormalizationCubeMap( TextureStage_t stage ) +{ +} + +void CShaderAPIDx10::BindFBTexture( TextureStage_t stage, int textureIndex ) +{ +} + +// Flushes any primitives that are buffered +void CShaderAPIDx10::FlushBufferedPrimitives() +{ +} + +// Creates/destroys Mesh +IMesh* CShaderAPIDx10::CreateStaticMesh( VertexFormat_t fmt, const char *pTextureBudgetGroup, IMaterial * pMaterial ) +{ + return &m_Mesh; +} + +void CShaderAPIDx10::DestroyStaticMesh( IMesh* mesh ) +{ +} + +// Gets the dynamic mesh; note that you've got to render the mesh +// before calling this function a second time. Clients should *not* +// call DestroyStaticMesh on the mesh returned by this call. +IMesh* CShaderAPIDx10::GetDynamicMesh( IMaterial* pMaterial, int nHWSkinBoneCount, bool buffered, IMesh* pVertexOverride, IMesh* pIndexOverride ) +{ + Assert( (pMaterial == NULL) || ((IMaterialInternal *)pMaterial)->IsRealTimeVersion() ); + return &m_Mesh; +} + +IMesh* CShaderAPIDx10::GetDynamicMeshEx( IMaterial* pMaterial, VertexFormat_t fmt, int nHWSkinBoneCount, bool buffered, IMesh* pVertexOverride, IMesh* pIndexOverride ) +{ + // UNDONE: support compressed dynamic meshes if needed (pro: less VB memory, con: time spent compressing) + Assert( CompressionType( pVertexOverride->GetVertexFormat() ) != VERTEX_COMPRESSION_NONE ); + Assert( (pMaterial == NULL) || ((IMaterialInternal *)pMaterial)->IsRealTimeVersion() ); + return &m_Mesh; +} + +IMesh* CShaderAPIDx10::GetFlexMesh() +{ + return &m_Mesh; +} + +// Begins a rendering pass that uses a state snapshot +void CShaderAPIDx10::BeginPass( StateSnapshot_t snapshot ) +{ +} + +// Renders a single pass of a material +void CShaderAPIDx10::RenderPass( int nPass, int nPassCount ) +{ +} + +// stuff related to matrix stacks +void CShaderAPIDx10::MatrixMode( MaterialMatrixMode_t matrixMode ) +{ +} + +void CShaderAPIDx10::PushMatrix() +{ +} + +void CShaderAPIDx10::PopMatrix() +{ +} + +void CShaderAPIDx10::LoadMatrix( float *m ) +{ +} + +void CShaderAPIDx10::MultMatrix( float *m ) +{ +} + +void CShaderAPIDx10::MultMatrixLocal( float *m ) +{ +} + +void CShaderAPIDx10::GetMatrix( MaterialMatrixMode_t matrixMode, float *dst ) +{ +} + +void CShaderAPIDx10::LoadIdentity( void ) +{ +} + +void CShaderAPIDx10::LoadCameraToWorld( void ) +{ +} + +void CShaderAPIDx10::Ortho( double left, double top, double right, double bottom, double zNear, double zFar ) +{ +} + +void CShaderAPIDx10::PerspectiveX( double fovx, double aspect, double zNear, double zFar ) +{ +} + +void CShaderAPIDx10::PerspectiveOffCenterX( double fovx, double aspect, double zNear, double zFar, double bottom, double top, double left, double right ) +{ +} + +void CShaderAPIDx10::PickMatrix( int x, int y, int width, int height ) +{ +} + +void CShaderAPIDx10::Rotate( float angle, float x, float y, float z ) +{ +} + +void CShaderAPIDx10::Translate( float x, float y, float z ) +{ +} + +void CShaderAPIDx10::Scale( float x, float y, float z ) +{ +} + +void CShaderAPIDx10::ScaleXY( float x, float y ) +{ +} + +// Fog methods... +void CShaderAPIDx10::FogMode( MaterialFogMode_t fogMode ) +{ +} + +void CShaderAPIDx10::FogStart( float fStart ) +{ +} + +void CShaderAPIDx10::FogEnd( float fEnd ) +{ +} + +void CShaderAPIDx10::SetFogZ( float fogZ ) +{ +} + +void CShaderAPIDx10::FogMaxDensity( float flMaxDensity ) +{ +} + + +void CShaderAPIDx10::GetFogDistances( float *fStart, float *fEnd, float *fFogZ ) +{ +} + + +void CShaderAPIDx10::SceneFogColor3ub( unsigned char r, unsigned char g, unsigned char b ) +{ +} + + +void CShaderAPIDx10::SceneFogMode( MaterialFogMode_t fogMode ) +{ +} + +void CShaderAPIDx10::GetSceneFogColor( unsigned char *rgb ) +{ +} + +MaterialFogMode_t CShaderAPIDx10::GetSceneFogMode( ) +{ + return MATERIAL_FOG_NONE; +} + +int CShaderAPIDx10::GetPixelFogCombo( ) +{ + return 0; //FIXME +} + +void CShaderAPIDx10::FogColor3f( float r, float g, float b ) +{ +} + +void CShaderAPIDx10::FogColor3fv( float const* rgb ) +{ +} + +void CShaderAPIDx10::FogColor3ub( unsigned char r, unsigned char g, unsigned char b ) +{ +} + +void CShaderAPIDx10::FogColor3ubv( unsigned char const* rgb ) +{ +} + +void CShaderAPIDx10::Viewport( int x, int y, int width, int height ) +{ +} + +void CShaderAPIDx10::GetViewport( int& x, int& y, int& width, int& height ) const +{ +} + +// Sets the vertex and pixel shaders +void CShaderAPIDx10::SetVertexShaderIndex( int vshIndex ) +{ +} + +void CShaderAPIDx10::SetPixelShaderIndex( int pshIndex ) +{ +} + +// Sets the constant register for vertex and pixel shaders +void CShaderAPIDx10::SetVertexShaderConstant( int var, float const* pVec, int numConst, bool bForce ) +{ +} + +void CShaderAPIDx10::SetPixelShaderConstant( int var, float const* pVec, int numConst, bool bForce ) +{ +} + +void CShaderAPIDx10::InvalidateDelayedShaderConstants( void ) +{ +} + +//Set's the linear->gamma conversion textures to use for this hardware for both srgb writes enabled and disabled(identity) +void CShaderAPIDx10::SetLinearToGammaConversionTextures( ShaderAPITextureHandle_t hSRGBWriteEnabledTexture, ShaderAPITextureHandle_t hIdentityTexture ) +{ +} + + +// Returns the nearest supported format +ImageFormat CShaderAPIDx10::GetNearestSupportedFormat( ImageFormat fmt, bool bFilteringRequired /* = true */ ) const +{ + return fmt; +} + +ImageFormat CShaderAPIDx10::GetNearestRenderTargetFormat( ImageFormat fmt ) const +{ + return fmt; +} + +// Sets the texture state +void CShaderAPIDx10::BindTexture( Sampler_t stage, ShaderAPITextureHandle_t textureHandle ) +{ +} + +// Indicates we're going to be modifying this texture +// TexImage2D, TexSubImage2D, TexWrap, TexMinFilter, and TexMagFilter +// all use the texture specified by this function. +void CShaderAPIDx10::ModifyTexture( ShaderAPITextureHandle_t textureHandle ) +{ +} + +// Texture management methods +void CShaderAPIDx10::TexImage2D( int level, int cubeFace, ImageFormat dstFormat, int zOffset, int width, int height, + ImageFormat srcFormat, bool bSrcIsTiled, void *imageData ) +{ +} + +void CShaderAPIDx10::TexSubImage2D( int level, int cubeFace, int xOffset, int yOffset, int zOffset, int width, int height, + ImageFormat srcFormat, int srcStride, bool bSrcIsTiled, void *imageData ) +{ +} + +void CShaderAPIDx10::TexImageFromVTF( IVTFTexture *pVTF, int iVTFFrame ) +{ +} + +bool CShaderAPIDx10::TexLock( int level, int cubeFaceID, int xOffset, int yOffset, + int width, int height, CPixelWriter& writer ) +{ + return false; +} + +void CShaderAPIDx10::TexUnlock( ) +{ +} + + +// These are bound to the texture, not the texture environment +void CShaderAPIDx10::TexMinFilter( ShaderTexFilterMode_t texFilterMode ) +{ +} + +void CShaderAPIDx10::TexMagFilter( ShaderTexFilterMode_t texFilterMode ) +{ +} + +void CShaderAPIDx10::TexWrap( ShaderTexCoordComponent_t coord, ShaderTexWrapMode_t wrapMode ) +{ +} + +void CShaderAPIDx10::TexSetPriority( int priority ) +{ +} + +ShaderAPITextureHandle_t CShaderAPIDx10::CreateTexture( + int width, + int height, + int depth, + ImageFormat dstImageFormat, + int numMipLevels, + int numCopies, + int flags, + const char *pDebugName, + const char *pTextureGroupName ) +{ + ShaderAPITextureHandle_t handle; + CreateTextures( &handle, 1, width, height, depth, dstImageFormat, numMipLevels, numCopies, flags, pDebugName, pTextureGroupName ); + return handle; +} + +void CShaderAPIDx10::CreateTextures( + ShaderAPITextureHandle_t *pHandles, + int count, + int width, + int height, + int depth, + ImageFormat dstImageFormat, + int numMipLevels, + int numCopies, + int flags, + const char *pDebugName, + const char *pTextureGroupName ) +{ + for ( int k = 0; k < count; ++ k ) + { + pHandles[ k ] = 0; + } +} + +ShaderAPITextureHandle_t CShaderAPIDx10::CreateDepthTexture( ImageFormat renderFormat, int width, int height, const char *pDebugName, bool bTexture ) +{ + return 0; +} + +void CShaderAPIDx10::DeleteTexture( ShaderAPITextureHandle_t textureHandle ) +{ +} + +bool CShaderAPIDx10::IsTexture( ShaderAPITextureHandle_t textureHandle ) +{ + return true; +} + +bool CShaderAPIDx10::IsTextureResident( ShaderAPITextureHandle_t textureHandle ) +{ + return false; +} + +// stuff that isn't to be used from within a shader +void CShaderAPIDx10::ClearBuffersObeyStencil( bool bClearColor, bool bClearDepth ) +{ +} + +void CShaderAPIDx10::ClearBuffersObeyStencilEx( bool bClearColor, bool bClearAlpha, bool bClearDepth ) +{ +} + +void CShaderAPIDx10::PerformFullScreenStencilOperation( void ) +{ +} + +void CShaderAPIDx10::ReadPixels( int x, int y, int width, int height, unsigned char *data, ImageFormat dstFormat ) +{ +} + +void CShaderAPIDx10::ReadPixels( Rect_t *pSrcRect, Rect_t *pDstRect, unsigned char *data, ImageFormat dstFormat, int nDstStride ) +{ +} + +void CShaderAPIDx10::FlushHardware() +{ +} + +// Set the number of bone weights +void CShaderAPIDx10::SetNumBoneWeights( int numBones ) +{ +} + +// Selection mode methods +int CShaderAPIDx10::SelectionMode( bool selectionMode ) +{ + return 0; +} + +void CShaderAPIDx10::SelectionBuffer( unsigned int* pBuffer, int size ) +{ +} + +void CShaderAPIDx10::ClearSelectionNames( ) +{ +} + +void CShaderAPIDx10::LoadSelectionName( int name ) +{ +} + +void CShaderAPIDx10::PushSelectionName( int name ) +{ +} + +void CShaderAPIDx10::PopSelectionName() +{ +} + + +// Use this to get the mesh builder that allows us to modify vertex data +CMeshBuilder* CShaderAPIDx10::GetVertexModifyBuilder() +{ + return 0; +} + +// Board-independent calls, here to unify how shaders set state +// Implementations should chain back to IShaderUtil->BindTexture(), etc. + +// Use this to begin and end the frame +void CShaderAPIDx10::BeginFrame() +{ +} + +void CShaderAPIDx10::EndFrame() +{ +} + +// returns the current time in seconds.... +double CShaderAPIDx10::CurrentTime() const +{ + return Sys_FloatTime(); +} + +// Get the current camera position in world space. +void CShaderAPIDx10::GetWorldSpaceCameraPosition( float * pPos ) const +{ +} + +void CShaderAPIDx10::ForceHardwareSync( void ) +{ +} + +void CShaderAPIDx10::SetClipPlane( int index, const float *pPlane ) +{ +} + +void CShaderAPIDx10::EnableClipPlane( int index, bool bEnable ) +{ +} + +void CShaderAPIDx10::SetFastClipPlane( const float *pPlane ) +{ +} + +void CShaderAPIDx10::EnableFastClip( bool bEnable ) +{ +} + +int CShaderAPIDx10::GetCurrentNumBones( void ) const +{ + return 0; +} + +// Is hardware morphing enabled? +bool CShaderAPIDx10::IsHWMorphingEnabled( ) const +{ + return false; +} + +int CShaderAPIDx10::GetCurrentLightCombo( void ) const +{ + return 0; +} + +int CShaderAPIDx10::MapLightComboToPSLightCombo( int nLightCombo ) const +{ + return 0; +} + +MaterialFogMode_t CShaderAPIDx10::GetCurrentFogType( void ) const +{ + return MATERIAL_FOG_NONE; +} + +void CShaderAPIDx10::RecordString( const char *pStr ) +{ +} + +void CShaderAPIDx10::DestroyVertexBuffers( bool bExitingLevel ) +{ +} + +int CShaderAPIDx10::GetCurrentDynamicVBSize( void ) +{ + return 0; +} + +void CShaderAPIDx10::EvictManagedResources() +{ +} + +void CShaderAPIDx10::ReleaseShaderObjects() +{ +} + +void CShaderAPIDx10::RestoreShaderObjects() +{ +} + +void CShaderAPIDx10::SetTextureTransformDimension( TextureStage_t textureStage, int dimension, bool projected ) +{ +} + +void CShaderAPIDx10::SetBumpEnvMatrix( TextureStage_t textureStage, float m00, float m01, float m10, float m11 ) +{ +} + +void CShaderAPIDx10::SyncToken( const char *pToken ) +{ +} diff --git a/materialsystem/shaderapidx9/shaderapidx10.h b/materialsystem/shaderapidx9/shaderapidx10.h new file mode 100644 index 0000000..c6b131f --- /dev/null +++ b/materialsystem/shaderapidx9/shaderapidx10.h @@ -0,0 +1,940 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//===========================================================================// + +#ifndef SHADERAPIDX10_H +#define SHADERAPIDX10_H + +#ifdef _WIN32 +#pragma once +#endif + +#include + +#include "shaderapibase.h" +#include "materialsystem/idebugtextureinfo.h" +#include "meshdx10.h" + + +//----------------------------------------------------------------------------- +// Forward declarations +//----------------------------------------------------------------------------- +struct MaterialSystemHardwareIdentifier_t; + + +//----------------------------------------------------------------------------- +// DX10 enumerations that don't appear to exist +//----------------------------------------------------------------------------- +#define MAX_DX10_VIEWPORTS 16 +#define MAX_DX10_STREAMS 16 + + +//----------------------------------------------------------------------------- +// A record describing the state on the board +//----------------------------------------------------------------------------- +struct ShaderIndexBufferStateDx10_t +{ + ID3D10Buffer *m_pBuffer; + DXGI_FORMAT m_Format; + UINT m_nOffset; + + bool operator!=( const ShaderIndexBufferStateDx10_t& src ) const + { + return memcmp( this, &src, sizeof(ShaderIndexBufferStateDx10_t) ) != 0; + } +}; + +struct ShaderVertexBufferStateDx10_t +{ + ID3D10Buffer *m_pBuffer; + UINT m_nStride; + UINT m_nOffset; +}; + +struct ShaderInputLayoutStateDx10_t +{ + VertexShaderHandle_t m_hVertexShader; + VertexFormat_t m_pVertexDecl[ MAX_DX10_STREAMS ]; +}; + +struct ShaderStateDx10_t +{ + int m_nViewportCount; + D3D10_VIEWPORT m_pViewports[ MAX_DX10_VIEWPORTS ]; + FLOAT m_ClearColor[4]; + ShaderRasterState_t m_RasterState; + ID3D10RasterizerState *m_pRasterState; + ID3D10VertexShader *m_pVertexShader; + ID3D10GeometryShader *m_pGeometryShader; + ID3D10PixelShader *m_pPixelShader; + ShaderVertexBufferStateDx10_t m_pVertexBuffer[ MAX_DX10_STREAMS ]; + ShaderIndexBufferStateDx10_t m_IndexBuffer; + ShaderInputLayoutStateDx10_t m_InputLayout; + D3D10_PRIMITIVE_TOPOLOGY m_Topology; +}; + + +//----------------------------------------------------------------------------- +// Commit function helper class +//----------------------------------------------------------------------------- +typedef void (*StateCommitFunc_t)( ID3D10Device *pDevice, const ShaderStateDx10_t &desiredState, ShaderStateDx10_t ¤tState, bool bForce ); + +class CFunctionCommit +{ +public: + CFunctionCommit(); + ~CFunctionCommit(); + + void Init( int nFunctionCount ); + + // Methods related to queuing functions to be called per-(pMesh->Draw call) or per-pass + void ClearAllCommitFuncs( ); + void CallCommitFuncs( bool bForce ); + bool IsCommitFuncInUse( int nFunc ) const; + void MarkCommitFuncInUse( int nFunc ); + void AddCommitFunc( StateCommitFunc_t f ); + void CallCommitFuncs( ID3D10Device *pDevice, const ShaderStateDx10_t &desiredState, ShaderStateDx10_t ¤tState, bool bForce = false ); + +private: + // A list of state commit functions to run as per-draw call commit time + unsigned char* m_pCommitFlags; + int m_nCommitBufferSize; + CUtlVector< StateCommitFunc_t > m_CommitFuncs; +}; + + +//----------------------------------------------------------------------------- +// The Dx10 implementation of the shader API +//----------------------------------------------------------------------------- +class CShaderAPIDx10 : public CShaderAPIBase, public IDebugTextureInfo +{ + typedef CShaderAPIBase BaseClass; + +public: + // constructor, destructor + CShaderAPIDx10( ); + virtual ~CShaderAPIDx10(); + + // Methods of IShaderAPI + // NOTE: These methods have been ported over +public: + virtual void SetViewports( int nCount, const ShaderViewport_t* pViewports ); + virtual int GetViewports( ShaderViewport_t* pViewports, int nMax ) const; + virtual void ClearBuffers( bool bClearColor, bool bClearDepth, bool bClearStencil, int renderTargetWidth, int renderTargetHeight ); + virtual void ClearColor3ub( unsigned char r, unsigned char g, unsigned char b ); + virtual void ClearColor4ub( unsigned char r, unsigned char g, unsigned char b, unsigned char a ); + virtual void SetRasterState( const ShaderRasterState_t& state ); + virtual void BindVertexShader( VertexShaderHandle_t hVertexShader ); + virtual void BindGeometryShader( GeometryShaderHandle_t hGeometryShader ); + virtual void BindPixelShader( PixelShaderHandle_t hPixelShader ); + virtual void BindVertexBuffer( int nStreamID, IVertexBuffer *pVertexBuffer, int nOffsetInBytes, int nFirstVertex, int nVertexCount, VertexFormat_t fmt, int nRepetitions = 1 ); + virtual void BindIndexBuffer( IIndexBuffer *pIndexBuffer, int nOffsetInBytes ); + virtual void Draw( MaterialPrimitiveType_t primitiveType, int nFirstIndex, int nIndexCount ); + + // Methods of IShaderDynamicAPI +public: + virtual void GetBackBufferDimensions( int& nWidth, int& nHeight ) const; + +public: + // Methods of CShaderAPIBase + virtual bool OnDeviceInit() { ResetRenderState(); return true; } + virtual void OnDeviceShutdown() {} + virtual void ReleaseShaderObjects(); + virtual void RestoreShaderObjects(); + virtual void BeginPIXEvent( unsigned long color, const char *szName ) {} + virtual void EndPIXEvent() {} + virtual void AdvancePIXFrame() {} + + // NOTE: These methods have not been ported over. + // IDebugTextureInfo implementation. +public: + + virtual bool IsDebugTextureListFresh( int numFramesAllowed = 1 ) { return false; } + virtual void EnableDebugTextureList( bool bEnable ) {} + virtual void EnableGetAllTextures( bool bEnable ) {} + virtual KeyValues* GetDebugTextureList() { return NULL; } + virtual int GetTextureMemoryUsed( TextureMemoryType eTextureMemory ) { return 0; } + virtual bool SetDebugTextureRendering( bool bEnable ) { return false; } + +public: + // Other public methods + void Unbind( VertexShaderHandle_t hShader ); + void Unbind( GeometryShaderHandle_t hShader ); + void Unbind( PixelShaderHandle_t hShader ); + void UnbindVertexBuffer( ID3D10Buffer *pBuffer ); + void UnbindIndexBuffer( ID3D10Buffer *pBuffer ); + + void PrintfVA( char *fmt, va_list vargs ) {} + void Printf( PRINTF_FORMAT_STRING const char *fmt, ... ) {} + float Knob( char *knobname, float *setvalue = NULL ) { return 0.0f;} + +private: + + // Returns a d3d texture associated with a texture handle + virtual IDirect3DBaseTexture* GetD3DTexture( ShaderAPITextureHandle_t hTexture ) { Assert(0); return NULL; } + virtual void QueueResetRenderState() {} + + void SetTopology( MaterialPrimitiveType_t topology ); + + virtual bool DoRenderTargetsNeedSeparateDepthBuffer() const; + + void SetHardwareGammaRamp( float fGamma ) + { + } + + // Used to clear the transition table when we know it's become invalid. + void ClearSnapshots(); + + // Sets the mode... + bool SetMode( void* hwnd, int nAdapter, const ShaderDeviceInfo_t &info ) + { + return true; + } + + void ChangeVideoMode( const ShaderDeviceInfo_t &info ) + { + } + + // Called when the dx support level has changed + virtual void DXSupportLevelChanged() {} + + virtual void EnableUserClipTransformOverride( bool bEnable ) {} + virtual void UserClipTransform( const VMatrix &worldToView ) {} + virtual bool GetUserClipTransform( VMatrix &worldToView ) { return false; } + + // Sets the default *dynamic* state + void SetDefaultState( ); + + // Returns the snapshot id for the shader state + StateSnapshot_t TakeSnapshot( ); + + // Returns true if the state snapshot is transparent + bool IsTranslucent( StateSnapshot_t id ) const; + bool IsAlphaTested( StateSnapshot_t id ) const; + bool UsesVertexAndPixelShaders( StateSnapshot_t id ) const; + virtual bool IsDepthWriteEnabled( StateSnapshot_t id ) const; + + // Gets the vertex format for a set of snapshot ids + VertexFormat_t ComputeVertexFormat( int numSnapshots, StateSnapshot_t* pIds ) const; + + // Gets the vertex format for a set of snapshot ids + VertexFormat_t ComputeVertexUsage( int numSnapshots, StateSnapshot_t* pIds ) const; + + // Begins a rendering pass that uses a state snapshot + void BeginPass( StateSnapshot_t snapshot ); + + // Uses a state snapshot + void UseSnapshot( StateSnapshot_t snapshot ); + + // Use this to get the mesh builder that allows us to modify vertex data + CMeshBuilder* GetVertexModifyBuilder(); + + // Sets the color to modulate by + void Color3f( float r, float g, float b ); + void Color3fv( float const* pColor ); + void Color4f( float r, float g, float b, float a ); + void Color4fv( float const* pColor ); + + // Faster versions of color + void Color3ub( unsigned char r, unsigned char g, unsigned char b ); + void Color3ubv( unsigned char const* rgb ); + void Color4ub( unsigned char r, unsigned char g, unsigned char b, unsigned char a ); + void Color4ubv( unsigned char const* rgba ); + + // Sets the lights + void SetLight( int lightNum, const LightDesc_t& desc ); + void SetAmbientLight( float r, float g, float b ); + void SetAmbientLightCube( Vector4D cube[6] ); + virtual void SetLightingOrigin( Vector vLightingOrigin ) {} + + // Get the lights + int GetMaxLights( void ) const; + const LightDesc_t& GetLight( int lightNum ) const; + + // Render state for the ambient light cube (vertex shaders) + void SetVertexShaderStateAmbientLightCube(); + virtual void SetPixelShaderStateAmbientLightCube( int pshReg, bool bForceToBlack = false ) {} + void SetPixelShaderStateAmbientLightCube( int pshReg ) + { + } + virtual void GetDX9LightState( LightState_t *state ) const {} + + float GetAmbientLightCubeLuminance(void) + { + return 0.0f; + } + + void SetSkinningMatrices(); + + // Lightmap texture binding + void BindLightmap( TextureStage_t stage ); + void BindLightmapAlpha( TextureStage_t stage ) + { + } + void BindBumpLightmap( TextureStage_t stage ); + void BindFullbrightLightmap( TextureStage_t stage ); + void BindWhite( TextureStage_t stage ); + void BindBlack( TextureStage_t stage ); + void BindGrey( TextureStage_t stage ); + void BindFBTexture( TextureStage_t stage, int textureIdex ); + void CopyRenderTargetToTexture( ShaderAPITextureHandle_t texID ) + { + } + + void CopyRenderTargetToTextureEx( ShaderAPITextureHandle_t texID, int nRenderTargetID, Rect_t *pSrcRect, Rect_t *pDstRect ) + { + } + + void CopyTextureToRenderTargetEx( int nRenderTargetID, ShaderAPITextureHandle_t textureHandle, Rect_t *pSrcRect = NULL, Rect_t *pDstRect = NULL ) + { + } + + // Special system flat normal map binding. + void BindFlatNormalMap( TextureStage_t stage ); + void BindNormalizationCubeMap( TextureStage_t stage ); + void BindSignedNormalizationCubeMap( TextureStage_t stage ); + + // Set the number of bone weights + void SetNumBoneWeights( int numBones ); + + // Flushes any primitives that are buffered + void FlushBufferedPrimitives(); + + // Creates/destroys Mesh + IMesh* CreateStaticMesh( VertexFormat_t fmt, const char *pTextureBudgetGroup, IMaterial * pMaterial = NULL ); + void DestroyStaticMesh( IMesh* mesh ); + + // Gets the dynamic mesh; note that you've got to render the mesh + // before calling this function a second time. Clients should *not* + // call DestroyStaticMesh on the mesh returned by this call. + IMesh* GetDynamicMesh( IMaterial* pMaterial, int nHWSkinBoneCount, bool buffered, IMesh* pVertexOverride, IMesh* pIndexOverride ); + IMesh* GetDynamicMeshEx( IMaterial* pMaterial, VertexFormat_t fmt, int nHWSkinBoneCount, bool buffered, IMesh* pVertexOverride, IMesh* pIndexOverride ); + IVertexBuffer *GetDynamicVertexBuffer( IMaterial* pMaterial, bool buffered ) + { + Assert( 0 ); + return NULL; + } + IIndexBuffer *GetDynamicIndexBuffer( IMaterial* pMaterial, bool buffered ) + { + Assert( 0 ); + return NULL; + } + IMesh* GetFlexMesh(); + + // Renders a single pass of a material + void RenderPass( int nPass, int nPassCount ); + + // stuff related to matrix stacks + void MatrixMode( MaterialMatrixMode_t matrixMode ); + void PushMatrix(); + void PopMatrix(); + void LoadMatrix( float *m ); + void LoadBoneMatrix( int boneIndex, const float *m ) {} + void MultMatrix( float *m ); + void MultMatrixLocal( float *m ); + void GetMatrix( MaterialMatrixMode_t matrixMode, float *dst ); + void LoadIdentity( void ); + void LoadCameraToWorld( void ); + void Ortho( double left, double top, double right, double bottom, double zNear, double zFar ); + void PerspectiveX( double fovx, double aspect, double zNear, double zFar ); + void PerspectiveOffCenterX( double fovx, double aspect, double zNear, double zFar, double bottom, double top, double left, double right ); + void PickMatrix( int x, int y, int width, int height ); + void Rotate( float angle, float x, float y, float z ); + void Translate( float x, float y, float z ); + void Scale( float x, float y, float z ); + void ScaleXY( float x, float y ); + + void Viewport( int x, int y, int width, int height ); + void GetViewport( int& x, int& y, int& width, int& height ) const; + + // Fog methods... + void FogMode( MaterialFogMode_t fogMode ); + void FogStart( float fStart ); + void FogEnd( float fEnd ); + void SetFogZ( float fogZ ); + void FogMaxDensity( float flMaxDensity ); + void GetFogDistances( float *fStart, float *fEnd, float *fFogZ ); + void FogColor3f( float r, float g, float b ); + void FogColor3fv( float const* rgb ); + void FogColor3ub( unsigned char r, unsigned char g, unsigned char b ); + void FogColor3ubv( unsigned char const* rgb ); + + virtual void SceneFogColor3ub( unsigned char r, unsigned char g, unsigned char b ); + virtual void SceneFogMode( MaterialFogMode_t fogMode ); + virtual void GetSceneFogColor( unsigned char *rgb ); + virtual MaterialFogMode_t GetSceneFogMode( ); + virtual int GetPixelFogCombo( ); + + void SetHeightClipZ( float z ); + void SetHeightClipMode( enum MaterialHeightClipMode_t heightClipMode ); + + void SetClipPlane( int index, const float *pPlane ); + void EnableClipPlane( int index, bool bEnable ); + + void SetFastClipPlane( const float *pPlane ); + void EnableFastClip( bool bEnable ); + + // We use smaller dynamic VBs during level transitions, to free up memory + virtual int GetCurrentDynamicVBSize( void ); + virtual void DestroyVertexBuffers( bool bExitingLevel = false ); + + // Sets the vertex and pixel shaders + void SetVertexShaderIndex( int vshIndex ); + void SetPixelShaderIndex( int pshIndex ); + + // Sets the constant register for vertex and pixel shaders + void SetVertexShaderConstant( int var, float const* pVec, int numConst = 1, bool bForce = false ); + void SetPixelShaderConstant( int var, float const* pVec, int numConst = 1, bool bForce = false ); + + void SetBooleanVertexShaderConstant( int var, BOOL const* pVec, int numBools = 1, bool bForce = false ) + { + Assert(0); + } + + + void SetIntegerVertexShaderConstant( int var, int const* pVec, int numIntVecs = 1, bool bForce = false ) + { + Assert(0); + } + + + void SetBooleanPixelShaderConstant( int var, BOOL const* pVec, int numBools = 1, bool bForce = false ) + { + Assert(0); + } + + void SetIntegerPixelShaderConstant( int var, int const* pVec, int numIntVecs = 1, bool bForce = false ) + { + Assert(0); + } + + bool ShouldWriteDepthToDestAlpha( void ) const + { + Assert(0); + return false; + } + + + void InvalidateDelayedShaderConstants( void ); + + // Gamma<->Linear conversions according to the video hardware we're running on + float GammaToLinear_HardwareSpecific( float fGamma ) const + { + return 0.; + } + + float LinearToGamma_HardwareSpecific( float fLinear ) const + { + return 0.; + } + + //Set's the linear->gamma conversion textures to use for this hardware for both srgb writes enabled and disabled(identity) + void SetLinearToGammaConversionTextures( ShaderAPITextureHandle_t hSRGBWriteEnabledTexture, ShaderAPITextureHandle_t hIdentityTexture ); + + // Cull mode + void CullMode( MaterialCullMode_t cullMode ); + + // Force writes only when z matches. . . useful for stenciling things out + // by rendering the desired Z values ahead of time. + void ForceDepthFuncEquals( bool bEnable ); + + // Forces Z buffering on or off + void OverrideDepthEnable( bool bEnable, bool bDepthEnable ); + // Forces alpha writes on or off + void OverrideAlphaWriteEnable( bool bOverrideEnable, bool bAlphaWriteEnable ); + //forces color writes on or off + void OverrideColorWriteEnable( bool bOverrideEnable, bool bColorWriteEnable ); + + // Sets the shade mode + void ShadeMode( ShaderShadeMode_t mode ); + + // Binds a particular material to render with + void Bind( IMaterial* pMaterial ); + + // Returns the nearest supported format + ImageFormat GetNearestSupportedFormat( ImageFormat fmt, bool bFilteringRequired = true ) const; + ImageFormat GetNearestRenderTargetFormat( ImageFormat fmt ) const; + + // Sets the texture state + void BindTexture( Sampler_t stage, ShaderAPITextureHandle_t textureHandle ); + + void SetRenderTarget( ShaderAPITextureHandle_t colorTextureHandle, ShaderAPITextureHandle_t depthTextureHandle ) + { + } + + void SetRenderTargetEx( int nRenderTargetID, ShaderAPITextureHandle_t colorTextureHandle, ShaderAPITextureHandle_t depthTextureHandle ) + { + } + + // Indicates we're going to be modifying this texture + // TexImage2D, TexSubImage2D, TexWrap, TexMinFilter, and TexMagFilter + // all use the texture specified by this function. + void ModifyTexture( ShaderAPITextureHandle_t textureHandle ); + + // Texture management methods + void TexImage2D( int level, int cubeFace, ImageFormat dstFormat, int zOffset, int width, int height, + ImageFormat srcFormat, bool bSrcIsTiled, void *imageData ); + void TexSubImage2D( int level, int cubeFace, int xOffset, int yOffset, int zOffset, int width, int height, + ImageFormat srcFormat, int srcStride, bool bSrcIsTiled, void *imageData ); + void TexImageFromVTF( IVTFTexture *pVTF, int iVTFFrame ); + + bool TexLock( int level, int cubeFaceID, int xOffset, int yOffset, + int width, int height, CPixelWriter& writer ); + void TexUnlock( ); + + // These are bound to the texture, not the texture environment + void TexMinFilter( ShaderTexFilterMode_t texFilterMode ); + void TexMagFilter( ShaderTexFilterMode_t texFilterMode ); + void TexWrap( ShaderTexCoordComponent_t coord, ShaderTexWrapMode_t wrapMode ); + void TexSetPriority( int priority ); + + ShaderAPITextureHandle_t CreateTexture( + int width, + int height, + int depth, + ImageFormat dstImageFormat, + int numMipLevels, + int numCopies, + int flags, + const char *pDebugName, + const char *pTextureGroupName ); + void CreateTextures( + ShaderAPITextureHandle_t *pHandles, + int count, + int width, + int height, + int depth, + ImageFormat dstImageFormat, + int numMipLevels, + int numCopies, + int flags, + const char *pDebugName, + const char *pTextureGroupName ); + ShaderAPITextureHandle_t CreateDepthTexture( ImageFormat renderFormat, int width, int height, const char *pDebugName, bool bTexture ); + void DeleteTexture( ShaderAPITextureHandle_t textureHandle ); + bool IsTexture( ShaderAPITextureHandle_t textureHandle ); + bool IsTextureResident( ShaderAPITextureHandle_t textureHandle ); + + // stuff that isn't to be used from within a shader + void ClearBuffersObeyStencil( bool bClearColor, bool bClearDepth ); + void ClearBuffersObeyStencilEx( bool bClearColor, bool bClearAlpha, bool bClearDepth ); + void PerformFullScreenStencilOperation( void ); + void ReadPixels( int x, int y, int width, int height, unsigned char *data, ImageFormat dstFormat ); + virtual void ReadPixels( Rect_t *pSrcRect, Rect_t *pDstRect, unsigned char *data, ImageFormat dstFormat, int nDstStride ); + + // Selection mode methods + int SelectionMode( bool selectionMode ); + void SelectionBuffer( unsigned int* pBuffer, int size ); + void ClearSelectionNames( ); + void LoadSelectionName( int name ); + void PushSelectionName( int name ); + void PopSelectionName(); + + void FlushHardware(); + void ResetRenderState( bool bFullReset = true ); + + // Can we download textures? + virtual bool CanDownloadTextures() const; + + // Board-independent calls, here to unify how shaders set state + // Implementations should chain back to IShaderUtil->BindTexture(), etc. + + // Use this to begin and end the frame + void BeginFrame(); + void EndFrame(); + + // returns current time + double CurrentTime() const; + + // Get the current camera position in world space. + void GetWorldSpaceCameraPosition( float * pPos ) const; + + void ForceHardwareSync( void ); + + int GetCurrentNumBones( void ) const; + bool IsHWMorphingEnabled( ) const; + int GetCurrentLightCombo( void ) const; + int MapLightComboToPSLightCombo( int nLightCombo ) const; + MaterialFogMode_t GetCurrentFogType( void ) const; + + void RecordString( const char *pStr ); + + void EvictManagedResources(); + + void SetTextureTransformDimension( TextureStage_t textureStage, int dimension, bool projected ); + void DisableTextureTransform( TextureStage_t textureStage ) + { + } + void SetBumpEnvMatrix( TextureStage_t textureStage, float m00, float m01, float m10, float m11 ); + + // Gets the lightmap dimensions + virtual void GetLightmapDimensions( int *w, int *h ); + + virtual void SyncToken( const char *pToken ); + + // Setup standard vertex shader constants (that don't change) + // This needs to be called anytime that overbright changes. + virtual void SetStandardVertexShaderConstants( float fOverbright ) + { + } + + // Scissor Rect + virtual void SetScissorRect( const int nLeft, const int nTop, const int nRight, const int nBottom, const bool bEnableScissor ) {} + + // Reports support for a given CSAA mode + bool SupportsCSAAMode( int nNumSamples, int nQualityLevel ) { return false; } + + // Level of anisotropic filtering + virtual void SetAnisotropicLevel( int nAnisotropyLevel ) + { + } + + void SetDefaultDynamicState() + { + } + virtual void CommitPixelShaderLighting( int pshReg ) + { + } + + virtual void MarkUnusedVertexFields( unsigned int nFlags, int nTexCoordCount, bool *pUnusedTexCoords ) + { + } + + ShaderAPIOcclusionQuery_t CreateOcclusionQueryObject( void ) + { + return INVALID_SHADERAPI_OCCLUSION_QUERY_HANDLE; + } + + void DestroyOcclusionQueryObject( ShaderAPIOcclusionQuery_t handle ) + { + } + + void BeginOcclusionQueryDrawing( ShaderAPIOcclusionQuery_t handle ) + { + } + + void EndOcclusionQueryDrawing( ShaderAPIOcclusionQuery_t handle ) + { + } + + int OcclusionQuery_GetNumPixelsRendered( ShaderAPIOcclusionQuery_t handle, bool bFlush ) + { + return 0; + } + + virtual void AcquireThreadOwnership() {} + virtual void ReleaseThreadOwnership() {} + + virtual bool SupportsBorderColor() const { return false; } + virtual bool SupportsFetch4() const { return false; } + virtual void EnableBuffer2FramesAhead( bool bEnable ) {} + + virtual void SetDepthFeatheringPixelShaderConstant( int iConstant, float fDepthBlendScale ) {} + + void SetPixelShaderFogParams( int reg ) + { + } + + virtual bool InFlashlightMode() const + { + return false; + } + + virtual bool InEditorMode() const + { + return false; + } + + // What fields in the morph do we actually use? + virtual MorphFormat_t ComputeMorphFormat( int numSnapshots, StateSnapshot_t* pIds ) const + { + return 0; + } + + // Gets the bound morph's vertex format; returns 0 if no morph is bound + virtual MorphFormat_t GetBoundMorphFormat() + { + return 0; + } + + void GetStandardTextureDimensions( int *pWidth, int *pHeight, StandardTextureId_t id ); + + // Binds a standard texture + virtual void BindStandardTexture( Sampler_t stage, StandardTextureId_t id ) + { + } + + virtual void BindStandardVertexTexture( VertexTextureSampler_t stage, StandardTextureId_t id ) + { + } + + virtual void SetFlashlightState( const FlashlightState_t &state, const VMatrix &worldToTexture ) + { + } + + virtual void SetFlashlightStateEx( const FlashlightState_t &state, const VMatrix &worldToTexture, ITexture *pFlashlightDepthTexture ) + { + } + + virtual const FlashlightState_t &GetFlashlightState( VMatrix &worldToTexture ) const + { + static FlashlightState_t blah; + return blah; + } + + virtual const FlashlightState_t &GetFlashlightStateEx( VMatrix &worldToTexture, ITexture **pFlashlightDepthTexture ) const + { + static FlashlightState_t blah; + return blah; + } + + virtual void SetModeChangeCallback( ModeChangeCallbackFunc_t func ) + { + } + + + virtual void ClearVertexAndPixelShaderRefCounts() + { + } + + virtual void PurgeUnusedVertexAndPixelShaders() + { + } + + // Binds a vertex texture to a particular texture stage in the vertex pipe + virtual void BindVertexTexture( VertexTextureSampler_t nStage, ShaderAPITextureHandle_t hTexture ) + { + } + + // Sets morph target factors + virtual void SetFlexWeights( int nFirstWeight, int nCount, const MorphWeight_t* pWeights ) + { + } + + // NOTE: Stuff after this is added after shipping HL2. + ITexture *GetRenderTargetEx( int nRenderTargetID ) + { + return NULL; + } + + void SetToneMappingScaleLinear( const Vector &scale ) + { + } + + const Vector &GetToneMappingScaleLinear( void ) const + { + static Vector dummy; + return dummy; + } + + virtual float GetLightMapScaleFactor( void ) const + { + return 1.0; + } + + // For dealing with device lost in cases where SwapBuffers isn't called all the time (Hammer) + virtual void HandleDeviceLost() + { + } + + virtual void EnableLinearColorSpaceFrameBuffer( bool bEnable ) + { + } + + // Lets the shader know about the full-screen texture so it can + virtual void SetFullScreenTextureHandle( ShaderAPITextureHandle_t h ) + { + } + + void SetFloatRenderingParameter(int parm_number, float value) + { + } + + void SetIntRenderingParameter(int parm_number, int value) + { + } + void SetVectorRenderingParameter(int parm_number, Vector const &value) + { + } + + float GetFloatRenderingParameter(int parm_number) const + { + return 0; + } + + int GetIntRenderingParameter(int parm_number) const + { + return 0; + } + + Vector GetVectorRenderingParameter(int parm_number) const + { + return Vector(0,0,0); + } + + // Methods related to stencil + void SetStencilEnable(bool onoff) + { + } + + void SetStencilFailOperation(StencilOperation_t op) + { + } + + void SetStencilZFailOperation(StencilOperation_t op) + { + } + + void SetStencilPassOperation(StencilOperation_t op) + { + } + + void SetStencilCompareFunction(StencilComparisonFunction_t cmpfn) + { + } + + void SetStencilReferenceValue(int ref) + { + } + + void SetStencilTestMask(uint32 msk) + { + } + + void SetStencilWriteMask(uint32 msk) + { + } + + void ClearStencilBufferRectangle( int xmin, int ymin, int xmax, int ymax,int value) + { + } + + virtual void GetDXLevelDefaults(uint &max_dxlevel,uint &recommended_dxlevel) + { + max_dxlevel=recommended_dxlevel=90; + } + + virtual void GetMaxToRender( IMesh *pMesh, bool bMaxUntilFlush, int *pMaxVerts, int *pMaxIndices ) + { + *pMaxVerts = 32768; + *pMaxIndices = 32768; + } + + // Returns the max possible vertices + indices to render in a single draw call + virtual int GetMaxVerticesToRender( IMaterial *pMaterial ) + { + return 32768; + } + + virtual int GetMaxIndicesToRender( ) + { + return 32768; + } + virtual int CompareSnapshots( StateSnapshot_t snapshot0, StateSnapshot_t snapshot1 ) { return 0; } + + virtual void DisableAllLocalLights() {} + + virtual bool SupportsMSAAMode( int nMSAAMode ) { return false; } + + // Hooks for firing PIX events from outside the Material System... + virtual void SetPIXMarker( unsigned long color, const char *szName ) {} + + virtual void ComputeVertexDescription( unsigned char* pBuffer, VertexFormat_t vertexFormat, MeshDesc_t& desc ) const {} + + virtual bool SupportsShadowDepthTextures() { return false; } + + virtual int NeedsShaderSRGBConversion(void) const { return 1; } + + virtual bool SupportsFetch4() { return false; } + + virtual void SetShadowDepthBiasFactors( float fShadowSlopeScaleDepthBias, float fShadowDepthBias ) {} + + virtual void SetDisallowAccess( bool ) {} + virtual void EnableShaderShaderMutex( bool ) {} + virtual void ShaderLock() {} + virtual void ShaderUnlock() {} + virtual void EnableHWMorphing( bool bEnable ) {} + ImageFormat GetNullTextureFormat( void ) { return IMAGE_FORMAT_ABGR8888; } // stub + virtual void PushDeformation( DeformationBase_t const *Deformation ) + { + } + + virtual void PopDeformation( ) + { + } + + virtual int GetNumActiveDeformations() const + { + return 0; + } + + + virtual void ExecuteCommandBuffer( uint8 *pBuf ) + { + } + + void SetStandardTextureHandle(StandardTextureId_t,ShaderAPITextureHandle_t) + { + } + + virtual void SetPSNearAndFarZ( int pshReg ) + { + } + + int GetPackedDeformationInformation( int nMaskOfUnderstoodDeformations, + float *pConstantValuesOut, + int nBufferSize, + int nMaximumDeformations, + int *pNumDefsOut ) const + { + *pNumDefsOut = 0; + return 0; + } + + virtual bool OwnGPUResources( bool bEnable ) + { + return false; + } + +private: + enum + { + TRANSLUCENT = 0x1, + ALPHATESTED = 0x2, + VERTEX_AND_PIXEL_SHADERS = 0x4, + DEPTHWRITE = 0x8, + }; + void EnableAlphaToCoverage() {} ; + void DisableAlphaToCoverage() {} ; + + ImageFormat GetShadowDepthTextureFormat() { return IMAGE_FORMAT_UNKNOWN; }; + + // + // NOTE: Under here are real methods being used by dx10 implementation + // above is stuff I still have to port over. + // +private: + void ClearShaderState( ShaderStateDx10_t* pState ); + void CommitStateChanges( bool bForce = false ); + +private: + CMeshDx10 m_Mesh; + + bool m_bResettingRenderState : 1; + CFunctionCommit m_Commit; + ShaderStateDx10_t m_DesiredState; + ShaderStateDx10_t m_CurrentState; +}; + + +//----------------------------------------------------------------------------- +// Singleton global +//----------------------------------------------------------------------------- +extern CShaderAPIDx10* g_pShaderAPIDx10; + +#endif // SHADERAPIDX10_H + diff --git a/materialsystem/shaderapidx9/shaderapidx10_global.h b/materialsystem/shaderapidx9/shaderapidx10_global.h new file mode 100644 index 0000000..62416b4 --- /dev/null +++ b/materialsystem/shaderapidx9/shaderapidx10_global.h @@ -0,0 +1,19 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//===========================================================================// + +#ifndef SHADERAPIDX10_GLOBAL_H +#define SHADERAPIDX10_GLOBAL_H + +#ifdef _WIN32 +#pragma once +#endif + +#include "shaderapi_global.h" + + +#endif // SHADERAPIDX10_GLOBAL_H diff --git a/materialsystem/shaderapidx9/shaderapidx8.cpp b/materialsystem/shaderapidx9/shaderapidx8.cpp new file mode 100644 index 0000000..ba6d509 --- /dev/null +++ b/materialsystem/shaderapidx9/shaderapidx8.cpp @@ -0,0 +1,14356 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// +// The dx8 implementation of the shader API +//===========================================================================// + +/* +DX9 todo: +-make the transforms in the older shaders match the transforms in lightmappedgeneric +-fix polyoffset for hardware that doesn't support D3DRS_SLOPESCALEDEPTHBIAS and D3DRS_DEPTHBIAS + - code is there, I think matrix offset just needs tweaking +-fix forcehardwaresync - implement texture locking for hardware that doesn't support async query +-get the format for GetAdapterModeCount and EnumAdapterModes from somewhere (shaderapidx8.cpp, GetModeCount, GetModeInfo) +-record frame sync objects (allocframesyncobjects, free framesync objects, ForceHardwareSync) +-Need to fix ENVMAPMASKSCALE, BUMPOFFSET in lightmappedgeneric*.cpp and vertexlitgeneric*.cpp +fix this: + // FIXME: This also depends on the vertex format and whether or not we are static lit in dx9 + #ifndef SHADERAPIDX9 + if (m_DynamicState.m_VertexShader != shader) // garymcthack + #endif // !SHADERAPIDX9 +unrelated to dx9: +mat_fullbright 1 doesn't work properly on alpha materials in testroom_standards +*/ +#define DISABLE_PROTECTED_THINGS +#include "shaderapidx8.h" +#include "shaderapidx8_global.h" +#include "shadershadowdx8.h" +#include "locald3dtypes.h" +#include "utlvector.h" +#include "IHardwareConfigInternal.h" +#include "utlstack.h" +#include "shaderapi/ishaderutil.h" +#include "shaderapi/commandbuffer.h" +#include "shaderapidx8_global.h" +#include "materialsystem/imaterialsystem.h" +#include "materialsystem/itexture.h" +#include "imaterialinternal.h" +#include "imeshdx8.h" +#include "materialsystem/imorph.h" +#include "colorformatdx8.h" +#include "texturedx8.h" +#include "textureheap.h" +#include +#include "interface.h" +#include "utlrbtree.h" +#include "utlsymbol.h" +#include "tier1/strtools.h" +#include "recording.h" +#ifndef _X360 +#include +#endif +#include "vertexshaderdx8.h" +#include "filesystem.h" +#include "mathlib/mathlib.h" +#include "materialsystem/materialsystem_config.h" +#include "worldsize.h" +#include "TransitionTable.h" +#include "tier0/vcrmode.h" +#include "tier0/vprof.h" +#include "tier1/tier1.h" +#include "tier1/utlbuffer.h" +#include "vertexdecl.h" +#include "tier0/icommandline.h" +#include "IShaderSystem.h" +#include "tier1/convar.h" +#include "tier1/KeyValues.h" +#include "Color.h" +#ifdef RECORDING +#include "materialsystem/IShader.h" +#endif +#include "../stdshaders/common_hlsl_cpp_consts.h" // hack hack hack! +#include "KeyValues.h" +#include "bitmap/imageformat.h" +#include "materialsystem/idebugtextureinfo.h" +#include "tier1/utllinkedlist.h" +#include "vtf/vtf.h" +#include "datacache/idatacache.h" +#include "renderparm.h" +#include "tier2/tier2.h" +#include "materialsystem/deformations.h" +#include "bitmap/tgawriter.h" +#include "tier0/icommandline.h" +#include "togl/rendermechanism.h" // provides GLMPRINTF/GLMPRINTSTR / GLMPRINTEXT macros which only activate if GLMDEBUG is nonzero and POSIX is defined. + +#if defined( _X360 ) +#include "xbox/xbox_console.h" +#include "xbox/xbox_win32stubs.h" +#include "xbox/xbox_launch.h" +#endif +#include "tier0/tslist.h" +#ifndef _X360 +#include "wmi.h" +#endif +#include "filesystem/IQueuedLoader.h" +#include "shaderdevicedx8.h" +#include "togl/rendermechanism.h" + +// Define this if you want to use a stubbed d3d. +//#define STUBD3D + +#ifdef STUBD3D +#include "stubd3ddevice.h" +#endif + +#include "winutils.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +#if defined( OSX ) + typedef unsigned int DWORD; + typedef DWORD* LPDWORD; +#endif + +#ifdef _WIN32 +#pragma warning (disable:4189) +#endif + +ConVar mat_texture_limit( "mat_texture_limit", "-1", FCVAR_NEVER_AS_STRING, + "If this value is not -1, the material system will limit the amount of texture memory it uses in a frame." + " Useful for identifying performance cliffs. The value is in kilobytes." ); + +ConVar mat_frame_sync_enable( "mat_frame_sync_enable", "1", FCVAR_CHEAT ); +ConVar mat_frame_sync_force_texture( "mat_frame_sync_force_texture", "0", FCVAR_CHEAT, "Force frame syncing to lock a managed texture." ); + + +#if defined( _X360 ) +ConVar mat_texturecachesize( "mat_texturecachesize", "176" ); +ConVar mat_force_flush_texturecache( "mat_force_flush_texturecache", "0" ); +#endif + +extern ConVar mat_debugalttab; + +#define ALLOW_SMP_ACCESS 0 + +#if ALLOW_SMP_ACCESS +static ConVar mat_use_smp( "mat_use_smp", "0" ); +#endif + +// Convars for driving PIX (not all hooked up yet...JasonM) +static ConVar r_pix_start( "r_pix_start", "0" ); +static ConVar r_pix_recordframes( "r_pix_recordframes", "0" ); + + +#define D3DDeviceWrapper IDirect3DDevice9 + +//----------------------------------------------------------------------------- +// Some important enumerations +//----------------------------------------------------------------------------- +enum +{ + MAX_VERTEX_TEXTURE_COUNT = 4, +}; + + +//----------------------------------------------------------------------------- +// These board states change with high frequency; are not shadowed +//----------------------------------------------------------------------------- +struct TextureStageState_t +{ + D3DTEXTURETRANSFORMFLAGS m_TextureTransformFlags; + float m_BumpEnvMat00; + float m_BumpEnvMat01; + float m_BumpEnvMat10; + float m_BumpEnvMat11; +}; + +struct SamplerState_t +{ + ShaderAPITextureHandle_t m_BoundTexture; + D3DTEXTUREADDRESS m_UTexWrap; + D3DTEXTUREADDRESS m_VTexWrap; + D3DTEXTUREADDRESS m_WTexWrap; + D3DTEXTUREFILTERTYPE m_MagFilter; + D3DTEXTUREFILTERTYPE m_MinFilter; + D3DTEXTUREFILTERTYPE m_MipFilter; + int m_FinestMipmapLevel; + float m_LodBias; + int m_nAnisotropicLevel; + bool m_TextureEnable; + bool m_SRGBReadEnable; +}; + + +//----------------------------------------------------------------------------- +// State related to vertex textures +//----------------------------------------------------------------------------- +struct VertexTextureState_t +{ + ShaderAPITextureHandle_t m_BoundTexture; + D3DTEXTUREADDRESS m_UTexWrap; + D3DTEXTUREADDRESS m_VTexWrap; + D3DTEXTUREFILTERTYPE m_MagFilter; + D3DTEXTUREFILTERTYPE m_MinFilter; + D3DTEXTUREFILTERTYPE m_MipFilter; +}; + + +enum TransformType_t +{ + TRANSFORM_IS_IDENTITY = 0, + TRANSFORM_IS_CAMERA_TO_WORLD, + TRANSFORM_IS_GENERAL, +}; + +enum TransformDirtyBits_t +{ + STATE_CHANGED_VERTEX_SHADER = 0x1, + STATE_CHANGED_FIXED_FUNCTION = 0x2, + STATE_CHANGED = 0x3 +}; + +enum +{ +#if !defined( _X360 ) + MAX_NUM_RENDERSTATES = ( D3DRS_BLENDOPALPHA+1 ), +#else + MAX_NUM_RENDERSTATES = D3DRS_MAX, +#endif +// MORPH_TARGET_FACTOR_COUNT = VERTEX_SHADER_MORPH_TARGET_FACTOR_COUNT * 4, +}; + +struct DynamicState_t +{ + // Constant color + unsigned int m_ConstantColor; + + // Normalize normals? + bool m_NormalizeNormals; + + // Viewport state + D3DVIEWPORT9 m_Viewport; + + // Transform state + D3DXMATRIX m_Transform[NUM_MATRIX_MODES]; + unsigned char m_TransformType[NUM_MATRIX_MODES]; + unsigned char m_TransformChanged[NUM_MATRIX_MODES]; + + // Ambient light color + D3DCOLOR m_Ambient; + D3DLIGHT m_Lights[MAX_NUM_LIGHTS]; + LightDesc_t m_LightDescs[MAX_NUM_LIGHTS]; + bool m_LightEnable[MAX_NUM_LIGHTS]; + Vector4D m_AmbientLightCube[6]; + unsigned char m_LightChanged[MAX_NUM_LIGHTS]; + unsigned char m_LightEnableChanged[MAX_NUM_LIGHTS]; + VertexShaderLightTypes_t m_LightType[MAX_NUM_LIGHTS]; + Vector m_vLightingOrigin; + int m_NumLights; + + // Shade mode + D3DSHADEMODE m_ShadeMode; + + // Clear color + D3DCOLOR m_ClearColor; + + // Fog + D3DCOLOR m_FogColor; + float m_PixelFogColor[4]; + bool m_bFogGammaCorrectionDisabled; + bool m_FogEnable; + MaterialFogMode_t m_SceneFog; + D3DFOGMODE m_FogMode; + float m_FogStart; + float m_FogEnd; + float m_FogZ; + float m_FogMaxDensity; + + float m_HeightClipZ; + MaterialHeightClipMode_t m_HeightClipMode; + + // user clip planes + int m_UserClipPlaneEnabled; + int m_UserClipPlaneChanged; + D3DXPLANE m_UserClipPlaneWorld[MAXUSERCLIPPLANES]; + D3DXPLANE m_UserClipPlaneProj[MAXUSERCLIPPLANES]; + bool m_UserClipLastUpdatedUsingFixedFunction; + + bool m_FastClipEnabled; + bool m_bFastClipPlaneChanged; + D3DXPLANE m_FastClipPlane; + + // Used when overriding the user clip plane + bool m_bUserClipTransformOverride; + D3DXMATRIX m_UserClipTransform; + + // Cull mode + D3DCULL m_DesiredCullMode; + D3DCULL m_CullMode; + bool m_bCullEnabled; + + // Skinning + D3DVERTEXBLENDFLAGS m_VertexBlend; + int m_NumBones; + + // Pixel and vertex shader constants... + Vector4D* m_pVectorVertexShaderConstant; + BOOL* m_pBooleanVertexShaderConstant; + IntVector4D* m_pIntegerVertexShaderConstant; + Vector4D* m_pVectorPixelShaderConstant; + BOOL* m_pBooleanPixelShaderConstant; + IntVector4D* m_pIntegerPixelShaderConstant; + + // Texture stage state + TextureStageState_t m_TextureStage[MAX_TEXTURE_STAGES]; + SamplerState_t m_SamplerState[MAX_SAMPLERS]; + + // Vertex texture stage state + VertexTextureState_t m_VertexTextureState[MAX_VERTEX_TEXTURE_COUNT]; + + DWORD m_RenderState[MAX_NUM_RENDERSTATES]; + + RECT m_ScissorRect; + + IDirect3DVertexDeclaration9 *m_pVertexDecl; + + bool m_bSRGBWritesEnabled; + bool m_bHWMorphingEnabled; + + float m_DestAlphaDepthRange; //Dest alpha writes compress the depth to get better results. This holds the default setting that can be overriden with r_destalpharange + +#if defined( _X360 ) + int m_iVertexShaderGPRAllocation; //only need to track vertex shader + bool m_bBuffer2Frames; +#endif + + DynamicState_t() {} + +private: + DynamicState_t( DynamicState_t const& ); +}; + +//----------------------------------------------------------------------------- +// Method to queue up dirty dynamic state change calls +//----------------------------------------------------------------------------- +typedef void (*StateCommitFunc_t)( IDirect3DDevice9 *pDevice, const DynamicState_t &desiredState, DynamicState_t ¤tState, bool bForce ); +static void CommitSetViewports( IDirect3DDevice9 *pDevice, const DynamicState_t &desiredState, DynamicState_t ¤tState, bool bForce ); + +// NOTE: It's slightly memory inefficient, and definitely not typesafe, +// to put all commit funcs into the same table (vs, ff, per-draw, per-pass), +// but it makes the code a heck of a lot simpler and smaller. +enum CommitFunc_t +{ + COMMIT_FUNC_CommitVertexTextures = 0, + COMMIT_FUNC_CommitFlexWeights, + COMMIT_FUNC_CommitSetScissorRect, + COMMIT_FUNC_CommitSetViewports, + +#if defined( _X360 ) + COMMIT_FUNC_CommitShaderGPRs, +#endif + + COMMIT_FUNC_COUNT, + COMMIT_FUNC_BYTE_COUNT = ( COMMIT_FUNC_COUNT + 0x7 ) >> 3, +}; + +enum CommitFuncType_t +{ + COMMIT_PER_DRAW = 0, + COMMIT_PER_PASS, + + COMMIT_FUNC_TYPE_COUNT, +}; + +enum CommitShaderType_t +{ + COMMIT_FIXED_FUNCTION = 0, + COMMIT_VERTEX_SHADER, + COMMIT_ALWAYS, + + COMMIT_SHADER_TYPE_COUNT, +}; + + +#define ADD_COMMIT_FUNC( _func, _shader, _func_name ) \ + if ( !IsCommitFuncInUse( _func, _shader, COMMIT_FUNC_ ## _func_name ) ) \ + { \ + AddCommitFunc( _func, _shader, _func_name ); \ + MarkCommitFuncInUse( _func, _shader, COMMIT_FUNC_ ## _func_name ); \ + } + +#define ADD_RENDERSTATE_FUNC( _func, _shader, _func_name, _state, _val ) \ + if ( m_bResettingRenderState || (m_DesiredState._state != _val) ) \ + { \ + m_DesiredState._state = _val; \ + ADD_COMMIT_FUNC( _func, _shader, _func_name ) \ + } + +#define ADD_VERTEX_TEXTURE_FUNC( _func, _shader, _func_name, _stage, _state, _val ) \ + Assert( ( int )_stage < MAX_VERTEX_TEXTURE_COUNT ); \ + if ( m_bResettingRenderState || (m_DesiredState.m_VertexTextureState[_stage]._state != _val) ) \ + { \ + m_DesiredState.m_VertexTextureState[_stage]._state = _val; \ + ADD_COMMIT_FUNC( _func, _shader, _func_name ) \ + } + + +//----------------------------------------------------------------------------- +// Check render state support at compile time instead of runtime +//----------------------------------------------------------------------------- +#define SetSupportedRenderState( _state, _val ) \ + { \ + if( _state != D3DRS_NOTSUPPORTED ) \ + { \ + SetRenderState( _state, _val, false ); \ + } \ + } + +#define SetSupportedRenderStateForce( _state, _val ) \ + { \ + if( _state != D3DRS_NOTSUPPORTED ) \ + { \ + SetRenderStateForce( _state, _val ); \ + } \ + } + +//----------------------------------------------------------------------------- +// Allocated textures +//----------------------------------------------------------------------------- +struct Texture_t +{ + Texture_t() + { + m_Flags = 0; + m_Count = 1; + m_CountIndex = 0; + m_nTimesBoundMax = 0; + m_nTimesBoundThisFrame = 0; + m_pTexture = NULL; + m_ppTexture = NULL; + m_ImageFormat = IMAGE_FORMAT_RGBA8888; + m_pTextureGroupCounterGlobal = NULL; + m_pTextureGroupCounterFrame = NULL; + m_FinestMipmapLevel = 0; + m_LodBias = 0.0f; + } + + // FIXME: Compress this info + D3DTEXTUREADDRESS m_UTexWrap; + D3DTEXTUREADDRESS m_VTexWrap; + D3DTEXTUREADDRESS m_WTexWrap; + D3DTEXTUREFILTERTYPE m_MagFilter; + D3DTEXTUREFILTERTYPE m_MinFilter; + D3DTEXTUREFILTERTYPE m_MipFilter; + int m_FinestMipmapLevel; + float m_LodBias; + + unsigned char m_NumLevels; + unsigned char m_SwitchNeeded; // Do we need to advance the current copy? + unsigned char m_NumCopies; // copies are used to optimize procedural textures + unsigned char m_CurrentCopy; // the current copy we're using... + + int m_CreationFlags; + + CUtlSymbol m_DebugName; + CUtlSymbol m_TextureGroupName; + int *m_pTextureGroupCounterGlobal; // Global counter for this texture's group. + int *m_pTextureGroupCounterFrame; // Per-frame global counter for this texture's group. + + // stats stuff + int m_SizeBytes; + int m_SizeTexels; + int m_LastBoundFrame; + int m_nTimesBoundMax; + int m_nTimesBoundThisFrame; + + enum Flags_t + { + IS_ALLOCATED = 0x0001, + IS_DEPTH_STENCIL = 0x0002, + IS_DEPTH_STENCIL_TEXTURE = 0x0004, // depth stencil texture, not surface + IS_RENDERABLE = ( IS_DEPTH_STENCIL | IS_ALLOCATED ), + IS_LOCKABLE = 0x0008, + IS_FINALIZED = 0x0010, // 360: completed async hi-res load + IS_FAILED = 0x0020, // 360: failed during load + CAN_CONVERT_FORMAT = 0x0040, // 360: allow format conversion + IS_LINEAR = 0x0080, // 360: unswizzled linear format + IS_RENDER_TARGET = 0x0100, // 360: marks a render target texture source + IS_RENDER_TARGET_SURFACE = 0x0200, // 360: marks a render target surface target + IS_VERTEX_TEXTURE = 0x0800, + }; + + short m_Width; + short m_Height; + short m_Depth; + unsigned short m_Flags; + + typedef IDirect3DBaseTexture *IDirect3DBaseTexturePtr; + typedef IDirect3DBaseTexture **IDirect3DBaseTexturePtrPtr; + typedef IDirect3DSurface *IDirect3DSurfacePtr; + + IDirect3DBaseTexturePtr GetTexture( void ) + { + Assert( m_NumCopies == 1 ); + Assert( !( m_Flags & IS_DEPTH_STENCIL ) ); + return m_pTexture; + } + IDirect3DBaseTexturePtr GetTexture( int copy ) + { + Assert( m_NumCopies > 1 ); + Assert( !( m_Flags & IS_DEPTH_STENCIL ) ); + return m_ppTexture[copy]; + } + IDirect3DBaseTexturePtrPtr &GetTextureArray( void ) + { + Assert( m_NumCopies > 1 ); + Assert( !( m_Flags & IS_DEPTH_STENCIL ) ); + return m_ppTexture; + } + + IDirect3DSurfacePtr &GetDepthStencilSurface( void ) + { + Assert( m_NumCopies == 1 ); + Assert( (m_Flags & IS_DEPTH_STENCIL) ); + return m_pDepthStencilSurface; + } + + IDirect3DSurfacePtr &GetRenderTargetSurface( bool bSRGB ) + { + Assert( m_NumCopies == 1 ); + Assert( m_Flags & IS_RENDER_TARGET_SURFACE ); + return m_pRenderTargetSurface[bSRGB]; + } + + void SetTexture( IDirect3DBaseTexturePtr pPtr ) + { + m_pTexture = pPtr; + } + void SetTexture( int copy, IDirect3DBaseTexturePtr pPtr ) + { + m_ppTexture[copy] = pPtr; + } + + int GetMemUsage() const + { + return m_SizeBytes; + } + + int GetWidth() const + { + return ( int )m_Width; + } + + int GetHeight() const + { + return ( int )m_Height; + } + + int GetDepth() const + { + return ( int )m_Depth; + } + + int GetLodClamp() const + { + return m_FinestMipmapLevel; + } + + void SetImageFormat( ImageFormat format ) + { + m_ImageFormat = format; + } + ImageFormat GetImageFormat() const + { + return m_ImageFormat; + } + +private: + union + { + IDirect3DBaseTexture *m_pTexture; // used when there's one copy + IDirect3DBaseTexture **m_ppTexture; // used when there are more than one copies + IDirect3DSurface *m_pDepthStencilSurface; // used when there's one depth stencil surface + IDirect3DSurface *m_pRenderTargetSurface[2]; + }; + + ImageFormat m_ImageFormat; + +public: + short m_Count; + short m_CountIndex; + + short GetCount() const + { + return m_Count; + } +}; + +#define MAX_DEFORMATION_PARAMETERS 16 +#define DEFORMATION_STACK_DEPTH 10 + +struct Deformation_t +{ + int m_nDeformationType; + int m_nNumParameters; + float m_flDeformationParameters[MAX_DEFORMATION_PARAMETERS]; +}; + + +//----------------------------------------------------------------------------- +// The DX8 implementation of the shader API +//----------------------------------------------------------------------------- +class CShaderAPIDx8 : public CShaderDeviceDx8, public IShaderAPIDX8, public IDebugTextureInfo +{ + typedef CShaderDeviceDx8 BaseClass; + +public: + // constructor, destructor + CShaderAPIDx8( ); + virtual ~CShaderAPIDx8(); + + // Methods of IShaderAPI +public: + virtual void SetViewports( int nCount, const ShaderViewport_t* pViewports ); + virtual int GetViewports( ShaderViewport_t* pViewports, int nMax ) const; + virtual void ClearBuffers( bool bClearColor, bool bClearDepth, bool bClearStencil, int renderTargetWidth, int renderTargetHeight ); + virtual void ClearColor3ub( unsigned char r, unsigned char g, unsigned char b ); + virtual void ClearColor4ub( unsigned char r, unsigned char g, unsigned char b, unsigned char a ); + virtual void BindVertexShader( VertexShaderHandle_t hVertexShader ); + virtual void BindGeometryShader( GeometryShaderHandle_t hGeometryShader ); + virtual void BindPixelShader( PixelShaderHandle_t hPixelShader ); + virtual void SetRasterState( const ShaderRasterState_t& state ); + virtual void SetFlexWeights( int nFirstWeight, int nCount, const MorphWeight_t* pWeights ); + + // Methods of IShaderDynamicAPI +public: + virtual void GetBackBufferDimensions( int &nWidth, int &nHeight ) const + { + // Chain to the device + BaseClass::GetBackBufferDimensions( nWidth, nHeight ); + } + virtual void MarkUnusedVertexFields( unsigned int nFlags, int nTexCoordCount, bool *pUnusedTexCoords ); + +public: + // Methods of CShaderAPIBase + virtual bool OnDeviceInit(); + virtual void OnDeviceShutdown(); + virtual void ReleaseShaderObjects(); + virtual void RestoreShaderObjects(); + virtual void BeginPIXEvent( unsigned long color, const char *szName ); + virtual void EndPIXEvent(); + virtual void AdvancePIXFrame(); + +public: + // Methods of IShaderAPIDX8 + virtual void QueueResetRenderState(); + + // + // Abandon all hope ye who pass below this line which hasn't been ported. + // + + // Sets the mode... + bool SetMode( void* VD3DHWND, int nAdapter, const ShaderDeviceInfo_t &info ); + + // Change the video mode after it's already been set. + void ChangeVideoMode( const ShaderDeviceInfo_t &info ); + + // Sets the default render state + void SetDefaultState(); + + // Methods to ask about particular state snapshots + virtual bool IsTranslucent( StateSnapshot_t id ) const; + virtual bool IsAlphaTested( StateSnapshot_t id ) const; + virtual bool UsesVertexAndPixelShaders( StateSnapshot_t id ) const; + virtual int CompareSnapshots( StateSnapshot_t snapshot0, StateSnapshot_t snapshot1 ); + + // Computes the vertex format for a particular set of snapshot ids + VertexFormat_t ComputeVertexFormat( int num, StateSnapshot_t* pIds ) const; + VertexFormat_t ComputeVertexUsage( int num, StateSnapshot_t* pIds ) const; + + // What fields in the morph do we actually use? + virtual MorphFormat_t ComputeMorphFormat( int numSnapshots, StateSnapshot_t* pIds ) const; + + // Uses a state snapshot + void UseSnapshot( StateSnapshot_t snapshot ); + + // Color state + void Color3f( float r, float g, float b ); + void Color4f( float r, float g, float b, float a ); + void Color3fv( float const* c ); + void Color4fv( float const* c ); + + void Color3ub( unsigned char r, unsigned char g, unsigned char b ); + void Color3ubv( unsigned char const* pColor ); + void Color4ub( unsigned char r, unsigned char g, unsigned char b, unsigned char a ); + void Color4ubv( unsigned char const* pColor ); + + // Set the number of bone weights + virtual void SetNumBoneWeights( int numBones ); + virtual void EnableHWMorphing( bool bEnable ); + + // Sets the vertex and pixel shaders + virtual void SetVertexShaderIndex( int vshIndex = -1 ); + virtual void SetPixelShaderIndex( int pshIndex = 0 ); + + // Matrix state + void MatrixMode( MaterialMatrixMode_t matrixMode ); + void PushMatrix(); + void PopMatrix(); + void LoadMatrix( float *m ); + void LoadBoneMatrix( int boneIndex, const float *m ); + void MultMatrix( float *m ); + void MultMatrixLocal( float *m ); + void GetMatrix( MaterialMatrixMode_t matrixMode, float *dst ); + void LoadIdentity( void ); + void LoadCameraToWorld( void ); + void Ortho( double left, double top, double right, double bottom, double zNear, double zFar ); + void PerspectiveX( double fovx, double aspect, double zNear, double zFar ); + void PerspectiveOffCenterX( double fovx, double aspect, double zNear, double zFar, double bottom, double top, double left, double right ); + void PickMatrix( int x, int y, int width, int height ); + void Rotate( float angle, float x, float y, float z ); + void Translate( float x, float y, float z ); + void Scale( float x, float y, float z ); + void ScaleXY( float x, float y ); + + // Binds a particular material to render with + void Bind( IMaterial* pMaterial ); + IMaterialInternal* GetBoundMaterial(); + + // Level of anisotropic filtering + virtual void SetAnisotropicLevel( int nAnisotropyLevel ); + + virtual void SyncToken( const char *pToken ); + + // Cull mode + void CullMode( MaterialCullMode_t cullMode ); + + // Force writes only when z matches. . . useful for stenciling things out + // by rendering the desired Z values ahead of time. + void ForceDepthFuncEquals( bool bEnable ); + + // Turns off Z buffering + void OverrideDepthEnable( bool bEnable, bool bDepthEnable ); + + void OverrideAlphaWriteEnable( bool bOverrideEnable, bool bAlphaWriteEnable ); + void OverrideColorWriteEnable( bool bOverrideEnable, bool bColorWriteEnable ); + + void SetHeightClipZ( float z ); + void SetHeightClipMode( enum MaterialHeightClipMode_t heightClipMode ); + + void SetClipPlane( int index, const float *pPlane ); + void EnableClipPlane( int index, bool bEnable ); + + void SetFastClipPlane(const float *pPlane); + void EnableFastClip(bool bEnable); + + // The shade mode + void ShadeMode( ShaderShadeMode_t mode ); + + // Vertex blend state + void SetVertexBlendState( int numBones ); + + // Gets the dynamic mesh + IMesh* GetDynamicMesh( IMaterial* pMaterial, int nHWSkinBoneCount, bool buffered, + IMesh* pVertexOverride, IMesh* pIndexOverride ); + IMesh* GetDynamicMeshEx( IMaterial* pMaterial, VertexFormat_t vertexFormat, int nHWSkinBoneCount, + bool bBuffered, IMesh* pVertexOverride, IMesh* pIndexOverride ); + IMesh *GetFlexMesh(); + + // Returns the number of vertices we can render using the dynamic mesh + virtual void GetMaxToRender( IMesh *pMesh, bool bMaxUntilFlush, int *pMaxVerts, int *pMaxIndices ); + virtual int GetMaxVerticesToRender( IMaterial *pMaterial ); + virtual int GetMaxIndicesToRender( ); + + // Draws the mesh + void DrawMesh( CMeshBase* mesh ); + + // modifies the vertex data when necessary + void ModifyVertexData( ); + + // Draws + void BeginPass( StateSnapshot_t snapshot ); + void RenderPass( int nPass, int nPassCount ); + + // We use smaller dynamic VBs during level transitions, to free up memory + virtual int GetCurrentDynamicVBSize( void ); + virtual void DestroyVertexBuffers( bool bExitingLevel = false ); + + void SetVertexDecl( VertexFormat_t vertexFormat, bool bHasColorMesh, bool bUsingFlex, bool bUsingMorph ); + + // Sets the constant register for vertex and pixel shaders + FORCEINLINE void SetVertexShaderConstantInternal( int var, float const* pVec, int numVecs = 1, bool bForce = false ); + + void SetVertexShaderConstant( int var, float const* pVec, int numVecs = 1, bool bForce = false ); + void SetBooleanVertexShaderConstant( int var, BOOL const* pVec, int numBools = 1, bool bForce = false ); + void SetIntegerVertexShaderConstant( int var, int const* pVec, int numIntVecs = 1, bool bForce = false ); + + void SetPixelShaderConstant( int var, float const* pVec, int numVecs = 1, bool bForce = false ); + FORCEINLINE void SetPixelShaderConstantInternal( int var, float const* pValues, int nNumConsts, bool bForce ); + + void SetBooleanPixelShaderConstant( int var, BOOL const* pVec, int numBools = 1, bool bForce = false ); + void SetIntegerPixelShaderConstant( int var, int const* pVec, int numIntVecs = 1, bool bForce = false ); + + void InvalidateDelayedShaderConstants( void ); + + // Returns the nearest supported format + ImageFormat GetNearestSupportedFormat( ImageFormat fmt, bool bFilteringRequired = true ) const; + ImageFormat GetNearestRenderTargetFormat( ImageFormat format ) const; + virtual bool DoRenderTargetsNeedSeparateDepthBuffer() const; + + // stuff that shouldn't be used from within a shader + void ModifyTexture( ShaderAPITextureHandle_t textureHandle ); + void BindTexture( Sampler_t sampler, ShaderAPITextureHandle_t textureHandle ); + virtual void BindVertexTexture( VertexTextureSampler_t nStage, ShaderAPITextureHandle_t textureHandle ); + void DeleteTexture( ShaderAPITextureHandle_t textureHandle ); + + void WriteTextureToFile( ShaderAPITextureHandle_t hTexture, const char *szFileName ); + + bool IsTexture( ShaderAPITextureHandle_t textureHandle ); + bool IsTextureResident( ShaderAPITextureHandle_t textureHandle ); + FORCEINLINE bool TextureIsAllocated( ShaderAPITextureHandle_t hTexture ) + { + return m_Textures.IsValidIndex( hTexture ) && ( GetTexture( hTexture ).m_Flags & Texture_t::IS_ALLOCATED ); + } + FORCEINLINE void AssertValidTextureHandle( ShaderAPITextureHandle_t textureHandle ) + { +#ifdef _DEBUG + Assert( TextureIsAllocated( textureHandle ) ); +#endif + } + + // Lets the shader know about the full-screen texture so it can + virtual void SetFullScreenTextureHandle( ShaderAPITextureHandle_t h ); + + virtual void SetLinearToGammaConversionTextures( ShaderAPITextureHandle_t hSRGBWriteEnabledTexture, ShaderAPITextureHandle_t hIdentityTexture ); + + // Set the render target to a texID. + // Set to SHADER_RENDERTARGET_BACKBUFFER if you want to use the regular framebuffer. + void SetRenderTarget( ShaderAPITextureHandle_t colorTextureHandle = SHADER_RENDERTARGET_BACKBUFFER, + ShaderAPITextureHandle_t depthTextureHandle = SHADER_RENDERTARGET_DEPTHBUFFER ); + // Set the render target to a texID. + // Set to SHADER_RENDERTARGET_BACKBUFFER if you want to use the regular framebuffer. + void SetRenderTargetEx( int nRenderTargetID, ShaderAPITextureHandle_t colorTextureHandle = SHADER_RENDERTARGET_BACKBUFFER, + ShaderAPITextureHandle_t depthTextureHandle = SHADER_RENDERTARGET_DEPTHBUFFER ); + + // These are bound to the texture, not the texture environment + void TexMinFilter( ShaderTexFilterMode_t texFilterMode ); + void TexMagFilter( ShaderTexFilterMode_t texFilterMode ); + void TexWrap( ShaderTexCoordComponent_t coord, ShaderTexWrapMode_t wrapMode ); + void TexSetPriority( int priority ); + void TexLodClamp( int finest ); + void TexLodBias( float bias ); + + ShaderAPITextureHandle_t CreateTextureHandle( void ); + void CreateTextureHandles( ShaderAPITextureHandle_t *handles, int count ); + + ShaderAPITextureHandle_t CreateTexture( + int width, + int height, + int depth, + ImageFormat dstImageFormat, + int numMipLevels, + int numCopies, + int creationFlags, + const char *pDebugName, + const char *pTextureGroupName ); + + // Create a multi-frame texture (equivalent to calling "CreateTexture" multiple times, but more efficient) + void CreateTextures( + ShaderAPITextureHandle_t *pHandles, + int count, + int width, + int height, + int depth, + ImageFormat dstImageFormat, + int numMipLevels, + int numCopies, + int flags, + const char *pDebugName, + const char *pTextureGroupName ); + + ShaderAPITextureHandle_t CreateDepthTexture( + ImageFormat renderTargetFormat, + int width, + int height, + const char *pDebugName, + bool bTexture ); + + void TexImage2D( + int level, + int cubeFaceID, + ImageFormat dstFormat, + int zOffset, + int width, + int height, + ImageFormat srcFormat, + bool bSrcIsTiled, + void *imageData ); + + void TexSubImage2D( + int level, + int cubeFaceID, + int xOffset, + int yOffset, + int zOffset, + int width, + int height, + ImageFormat srcFormat, + int srcStride, + bool bSrcIsTiled, + void *imageData ); + + void TexImageFromVTF( IVTFTexture *pVTF, int iVTFFrame ); + + bool TexLock( int level, int cubeFaceID, int xOffset, int yOffset, int width, int height, CPixelWriter& writer ); + void TexUnlock( ); + + // stuff that isn't to be used from within a shader + // what's the best way to hide this? subclassing? + virtual void ClearBuffersObeyStencil( bool bClearColor, bool bClearDepth ); + virtual void ClearBuffersObeyStencilEx( bool bClearColor, bool bClearAlpha, bool bClearDepth ); + virtual void PerformFullScreenStencilOperation( void ); + void ReadPixels( int x, int y, int width, int height, unsigned char *data, ImageFormat dstFormat ); + virtual void ReadPixels( Rect_t *pSrcRect, Rect_t *pDstRect, unsigned char *data, ImageFormat dstFormat, int nDstStride ); + + // Gets the current buffered state... (debug only) + void GetBufferedState( BufferedState_t& state ); + + // Buffered primitives + void FlushBufferedPrimitives(); + void FlushBufferedPrimitivesInternal( ); + + // Make sure we finish drawing everything that has been requested + void FlushHardware(); + + // Use this to begin and end the frame + void BeginFrame(); + void EndFrame(); + + // Used to clear the transition table when we know it's become invalid. + void ClearSnapshots(); + + // Backward compat + virtual int GetActualTextureStageCount() const; + virtual int GetActualSamplerCount() const; + virtual int StencilBufferBits() const; + virtual bool IsAAEnabled() const; // Is antialiasing being used? + virtual bool OnAdapterSet( ); + bool m_bAdapterSet; + + void UpdateFastClipUserClipPlane( void ); + bool ReadPixelsFromFrontBuffer() const; + + // returns the current time in seconds.... + double CurrentTime() const; + + // Get the current camera position in world space. + void GetWorldSpaceCameraPosition( float* pPos ) const; + + // Fog methods + void FogMode( MaterialFogMode_t fogMode ); + void FogStart( float fStart ); + void FogEnd( float fEnd ); + void FogMaxDensity( float flMaxDensity ); + void SetFogZ( float fogZ ); + void GetFogDistances( float *fStart, float *fEnd, float *fFogZ ); + + void SceneFogMode( MaterialFogMode_t fogMode ); + MaterialFogMode_t GetSceneFogMode( ); + MaterialFogMode_t GetPixelFogMode( ); + int GetPixelFogCombo( );//0 is either range fog, or no fog simulated with rigged range fog values. 1 is height fog + bool ShouldUsePixelFogForMode( MaterialFogMode_t fogMode ); + void SceneFogColor3ub( unsigned char r, unsigned char g, unsigned char b ); + void GetSceneFogColor( unsigned char *rgb ); + void GetSceneFogColor( unsigned char *r, unsigned char *g, unsigned char *b ); + + // Selection mode methods + int SelectionMode( bool selectionMode ); + void SelectionBuffer( unsigned int* pBuffer, int size ); + void ClearSelectionNames( ); + void LoadSelectionName( int name ); + void PushSelectionName( int name ); + void PopSelectionName(); + bool IsInSelectionMode() const; + void RegisterSelectionHit( float minz, float maxz ); + void WriteHitRecord(); + + // Binds a standard texture + virtual void BindStandardTexture( Sampler_t sampler, StandardTextureId_t id ); + virtual void BindStandardVertexTexture( VertexTextureSampler_t sampler, StandardTextureId_t id ); + virtual void GetStandardTextureDimensions( int *pWidth, int *pHeight, StandardTextureId_t id ); + + // Gets the lightmap dimensions + virtual void GetLightmapDimensions( int *w, int *h ); + + // Use this to get the mesh builder that allows us to modify vertex data + CMeshBuilder* GetVertexModifyBuilder(); + + virtual bool InFlashlightMode() const; + virtual bool InEditorMode() const; + + // Gets the bound morph's vertex format; returns 0 if no morph is bound + virtual MorphFormat_t GetBoundMorphFormat(); + + // Helper to get at the texture state stage + TextureStageState_t& TextureStage( int stage ) { return m_DynamicState.m_TextureStage[stage]; } + const TextureStageState_t& TextureStage( int stage ) const { return m_DynamicState.m_TextureStage[stage]; } + SamplerState_t& SamplerState( int nSampler ) { return m_DynamicState.m_SamplerState[nSampler]; } + const SamplerState_t& SamplerState( int nSampler ) const { return m_DynamicState.m_SamplerState[nSampler]; } + + void SetAmbientLight( float r, float g, float b ); + void SetLight( int lightNum, const LightDesc_t& desc ); + void SetLightingOrigin( Vector vLightingOrigin ); + void DisableAllLocalLights(); + void SetAmbientLightCube( Vector4D colors[6] ); + float GetAmbientLightCubeLuminance( void ); + + int GetMaxLights( void ) const; + const LightDesc_t& GetLight( int lightNum ) const; + + void SetVertexShaderStateAmbientLightCube(); + void SetPixelShaderStateAmbientLightCube( int pshReg, bool bForceToBlack = false ); + + void CopyRenderTargetToTexture( ShaderAPITextureHandle_t textureHandle ); + void CopyRenderTargetToTextureEx( ShaderAPITextureHandle_t textureHandle, int nRenderTargetID, Rect_t *pSrcRect = NULL, Rect_t *pDstRect = NULL ); + void CopyTextureToRenderTargetEx( int nRenderTargetID, ShaderAPITextureHandle_t textureHandle, Rect_t *pSrcRect = NULL, Rect_t *pDstRect = NULL ); + void CopyRenderTargetToScratchTexture( ShaderAPITextureHandle_t srcHandle, ShaderAPITextureHandle_t dstHandle, Rect_t *pSrcRect = NULL, Rect_t *pDstRect = NULL ); + + virtual void LockRect( void** pOutBits, int* pOutPitch, ShaderAPITextureHandle_t texHandle, int mipmap, int x, int y, int w, int h, bool bWrite, bool bRead ); + virtual void UnlockRect( ShaderAPITextureHandle_t texHandle, int mipmap ); + + virtual void CopyTextureToTexture( ShaderAPITextureHandle_t srcTex, ShaderAPITextureHandle_t dstTex ); + + // Returns the cull mode (for fill rate computation) + D3DCULL GetCullMode() const; + void SetCullModeState( bool bEnable, D3DCULL nDesiredCullMode ); + void ApplyCullEnable( bool bEnable ); + + // Alpha to coverage + void ApplyAlphaToCoverage( bool bEnable ); + +#if defined( _X360 ) + void ApplySRGBReadState( int iTextureStage, bool bSRGBReadEnabled ); +#endif + + // Applies Z Bias + void ApplyZBias( const ShadowState_t& shaderState ); + + // Applies texture enable + void ApplyTextureEnable( const ShadowState_t& state, int stage ); + + void ApplyFogMode( ShaderFogMode_t fogMode, bool bSRGBWritesEnabled, bool bDisableFogGammaCorrection ); + void UpdatePixelFogColorConstant( void ); + + void EnabledSRGBWrite( bool bEnabled ); + + // Gamma<->Linear conversions according to the video hardware we're running on + float GammaToLinear_HardwareSpecific( float fGamma ) const; + float LinearToGamma_HardwareSpecific( float fLinear ) const; + + // Applies alpha blending + void ApplyAlphaBlend( bool bEnable, D3DBLEND srcBlend, D3DBLEND destBlend ); + + // Applies alpha texture op + void ApplyColorTextureStage( int stage, D3DTEXTUREOP op, int arg1, int arg2 ); + void ApplyAlphaTextureStage( int stage, D3DTEXTUREOP op, int arg1, int arg2 ); + + // Sets texture stage stage + render stage state + void SetSamplerState( int stage, D3DSAMPLERSTATETYPE state, DWORD val ); + void SetTextureStageState( int stage, D3DTEXTURESTAGESTATETYPE state, DWORD val); + void SetRenderStateForce( D3DRENDERSTATETYPE state, DWORD val ); + void SetRenderState( D3DRENDERSTATETYPE state, DWORD val, + bool bFlushBufferedPrimitivesIfChanged = false); + + // Scissor Rect + void SetScissorRect( const int nLeft, const int nTop, const int nRight, const int nBottom, const bool bEnableScissor ); + // Can we download textures? + virtual bool CanDownloadTextures() const; + + void ForceHardwareSync_WithManagedTexture(); + void ForceHardwareSync( void ); + void UpdateFrameSyncQuery( int queryIndex, bool bIssue ); + + void EvictManagedResources(); + + virtual void EvictManagedResourcesInternal(); + + // Gets at a particular transform + inline D3DXMATRIX& GetTransform( int i ) + { + return *m_pMatrixStack[i]->GetTop(); + } + + int GetCurrentNumBones( void ) const; + bool IsHWMorphingEnabled( ) const; + int GetCurrentLightCombo( void ) const; // Used for DX8 only + void GetDX9LightState( LightState_t *state ) const; // Used for DX9 only + + MaterialFogMode_t GetCurrentFogType( void ) const; + + void RecordString( const char *pStr ); + + virtual bool IsRenderingMesh() const { return m_pRenderMesh != 0; } + + void SetTextureTransformDimension( TextureStage_t textureStage, int dimension, bool projected ); + void DisableTextureTransform( TextureStage_t textureMatrix ); + void SetBumpEnvMatrix( TextureStage_t textureStage, float m00, float m01, float m10, float m11 ); + + int GetCurrentFrameCounter( void ) const + { + return m_CurrentFrame; + } + + // Workaround hack for visualization of selection mode + virtual void SetupSelectionModeVisualizationState(); + + // Allocate and delete query objects. + virtual ShaderAPIOcclusionQuery_t CreateOcclusionQueryObject( void ); + virtual void DestroyOcclusionQueryObject( ShaderAPIOcclusionQuery_t h ); + + // Bracket drawing with begin and end so that we can get counts next frame. + virtual void BeginOcclusionQueryDrawing( ShaderAPIOcclusionQuery_t h ); + virtual void EndOcclusionQueryDrawing( ShaderAPIOcclusionQuery_t h ); + + // Get the number of pixels rendered between begin and end on an earlier frame. + // Calling this in the same frame is a huge perf hit! + virtual int OcclusionQuery_GetNumPixelsRendered( ShaderAPIOcclusionQuery_t h, bool bFlush ); + + void SetFlashlightState( const FlashlightState_t &state, const VMatrix &worldToTexture ); + void SetFlashlightStateEx( const FlashlightState_t &state, const VMatrix &worldToTexture, ITexture *pFlashlightDepthTexture ); + const FlashlightState_t &GetFlashlightState( VMatrix &worldToTexture ) const; + const FlashlightState_t &GetFlashlightStateEx( VMatrix &worldToTexture, ITexture **pFlashlightDepthTexture ) const; + + // Gets at the shadow state for a particular state snapshot + virtual bool IsDepthWriteEnabled( StateSnapshot_t id ) const; + +// IDebugTextureInfo implementation. + + virtual bool IsDebugTextureListFresh( int numFramesAllowed = 1 ); + virtual void EnableDebugTextureList( bool bEnable ); + virtual bool SetDebugTextureRendering( bool bEnable ); + virtual void EnableGetAllTextures( bool bEnable ); + virtual KeyValues* GetDebugTextureList(); + virtual int GetTextureMemoryUsed( TextureMemoryType eTextureMemory ); + + virtual void ClearVertexAndPixelShaderRefCounts(); + virtual void PurgeUnusedVertexAndPixelShaders(); + + // Called when the dx support level has changed + virtual void DXSupportLevelChanged(); + + // User clip plane override + virtual void EnableUserClipTransformOverride( bool bEnable ); + virtual void UserClipTransform( const VMatrix &worldToProjection ); + + bool UsingSoftwareVertexProcessing() const; + + // Mark all user clip planes as being dirty + void MarkAllUserClipPlanesDirty(); + + // Converts a D3DXMatrix to a VMatrix and back + void D3DXMatrixToVMatrix( const D3DXMATRIX &in, VMatrix &out ); + void VMatrixToD3DXMatrix( const VMatrix &in, D3DXMATRIX &out ); + + ITexture *GetRenderTargetEx( int nRenderTargetID ); + + virtual void SetToneMappingScaleLinear( const Vector &scale ); + virtual const Vector &GetToneMappingScaleLinear( void ) const; + float GetLightMapScaleFactor( void ) const; + + void SetFloatRenderingParameter(int parm_number, float value); + + void SetIntRenderingParameter(int parm_number, int value); + void SetVectorRenderingParameter(int parm_number, Vector const &value); + + float GetFloatRenderingParameter(int parm_number) const; + + int GetIntRenderingParameter(int parm_number) const; + + Vector GetVectorRenderingParameter(int parm_number) const; + + // For dealing with device lost in cases where Present isn't called all the time (Hammer) + virtual void HandleDeviceLost(); + + virtual void EnableLinearColorSpaceFrameBuffer( bool bEnable ); + + virtual void SetPSNearAndFarZ( int pshReg ); + + // stencil methods + void SetStencilEnable(bool onoff); + void SetStencilFailOperation(StencilOperation_t op); + void SetStencilZFailOperation(StencilOperation_t op); + void SetStencilPassOperation(StencilOperation_t op); + void SetStencilCompareFunction(StencilComparisonFunction_t cmpfn); + void SetStencilReferenceValue(int ref); + void SetStencilTestMask(uint32 msk); + void SetStencilWriteMask(uint32 msk); + void ClearStencilBufferRectangle(int xmin, int ymin, int xmax, int ymax,int value); + + virtual void GetDXLevelDefaults(uint &max_dxlevel,uint &recommended_dxlevel); + +#if defined( _X360 ) + HXUIFONT OpenTrueTypeFont( const char *pFontname, int tall, int style ); + void CloseTrueTypeFont( HXUIFONT hFont ); + bool GetTrueTypeFontMetrics( HXUIFONT hFont, XUIFontMetrics *pFontMetrics, XUICharMetrics charMetrics[256] ); + // Render a sequence of characters and extract the data into a buffer + // For each character, provide the width+height of the font texture subrect, + // an offset to apply when rendering the glyph, and an offset into a buffer to receive the RGBA data + bool GetTrueTypeGlyphs( HXUIFONT hFont, int numChars, wchar_t *pWch, int *pOffsetX, int *pOffsetY, int *pWidth, int *pHeight, unsigned char *pRGBA, int *pRGBAOffset ); + ShaderAPITextureHandle_t CreateRenderTargetSurface( int width, int height, ImageFormat format, const char *pDebugName, const char *pTextureGroupName ); + void PersistDisplay(); + bool PostQueuedTexture( const void *pData, int nSize, ShaderAPITextureHandle_t *pHandles, int nHandles, int nWidth, int nHeight, int nDepth, int nMips, int *pRefCount ); + void *GetD3DDevice(); + + void PushVertexShaderGPRAllocation( int iVertexShaderCount = 64 ); + void PopVertexShaderGPRAllocation( void ); + + void EnableVSync_360( bool bEnable ); +#endif + + virtual bool OwnGPUResources( bool bEnable ); + +// ------------ New Vertex/Index Buffer interface ---------------------------- + void BindVertexBuffer( int streamID, IVertexBuffer *pVertexBuffer, int nOffsetInBytes, int nFirstVertex, int nVertexCount, VertexFormat_t fmt, int nRepetitions = 1 ); + void BindIndexBuffer( IIndexBuffer *pIndexBuffer, int nOffsetInBytes ); + void Draw( MaterialPrimitiveType_t primitiveType, int nFirstIndex, int nIndexCount ); + + // Draw the mesh with the currently bound vertex and index buffers. + void DrawWithVertexAndIndexBuffers( void ); +// ------------ End ---------------------------- + + // deformations + virtual void PushDeformation( const DeformationBase_t *pDeformation ); + virtual void PopDeformation( ); + virtual int GetNumActiveDeformations( ) const ; + + + // for shaders to set vertex shader constants. returns a packed state which can be used to set the dynamic combo + virtual int GetPackedDeformationInformation( int nMaskOfUnderstoodDeformations, + float *pConstantValuesOut, + int nBufferSize, + int nMaximumDeformations, + int *pNumDefsOut ) const ; + + inline Texture_t &GetTexture( ShaderAPITextureHandle_t hTexture ) + { + return m_Textures[hTexture]; + } + + // Gets the texture + IDirect3DBaseTexture* GetD3DTexture( ShaderAPITextureHandle_t hTexture ); + + + virtual bool ShouldWriteDepthToDestAlpha( void ) const; + + virtual void AcquireThreadOwnership(); + virtual void ReleaseThreadOwnership(); +private: + enum + { + SMALL_BACK_BUFFER_SURFACE_WIDTH = 256, + SMALL_BACK_BUFFER_SURFACE_HEIGHT = 256, + }; + + bool m_bEnableDebugTextureList; + bool m_bDebugGetAllTextures; + bool m_bDebugTexturesRendering; + KeyValues *m_pDebugTextureList; + int m_nTextureMemoryUsedLastFrame, m_nTextureMemoryUsedTotal; + int m_nTextureMemoryUsedPicMip1, m_nTextureMemoryUsedPicMip2; + int m_nDebugDataExportFrame; + + FlashlightState_t m_FlashlightState; + VMatrix m_FlashlightWorldToTexture; + ITexture *m_pFlashlightDepthTexture; + + CShaderAPIDx8( CShaderAPIDx8 const& ); + + enum + { + INVALID_TRANSITION_OP = 0xFFFF + }; + + // State transition table for the device is as follows: + + // Other app init causes transition from OK to OtherAppInit, during transition we must release resources + // !Other app init causes transition from OtherAppInit to OK, during transition we must restore resources + // Minimized or device lost or device not reset causes transition from OK to LOST_DEVICE, during transition we must release resources + // Minimized or device lost or device not reset causes transition from OtherAppInit to LOST_DEVICE + + // !minimized AND !device lost causes transition from LOST_DEVICE to NEEDS_RESET + // minimized or device lost causes transition from NEEDS_RESET to LOST_DEVICE + + // Successful TryDeviceReset and !Other app init causes transition from NEEDS_RESET to OK, during transition we must restore resources + // Successful TryDeviceReset and Other app init causes transition from NEEDS_RESET to OtherAppInit + + void ExportTextureList(); + void AddBufferToTextureList( const char *pName, D3DSURFACE_DESC &desc ); + + void SetupTextureGroup( ShaderAPITextureHandle_t hTexture, const char *pTextureGroupName ); + + // Creates the matrix stack + void CreateMatrixStacks(); + + // Initializes the render state + void InitRenderState( ); + + // Resets all dx renderstates to dx default so that our shadows are correct. + void ResetDXRenderState( ); + + // Resets the render state + void ResetRenderState( bool bFullReset = true ); + + // Setup standard vertex shader constants (that don't change) + void SetStandardVertexShaderConstants( float fOverbright ); + + // Initializes vertex and pixel shaders + void InitVertexAndPixelShaders(); + + // Discards the vertex and index buffers + void DiscardVertexBuffers(); + + // Computes the fill rate + void ComputeFillRate(); + + // Takes a snapshot + virtual StateSnapshot_t TakeSnapshot( ); + + // Converts the clear color to be appropriate for HDR + D3DCOLOR GetActualClearColor( D3DCOLOR clearColor ); + + // We lost the device + void OnDeviceLost(); + + // Gets the matrix stack from the matrix mode + int GetMatrixStack( MaterialMatrixMode_t mode ) const; + + // Flushes the matrix state, returns false if we don't need to + // do any more work + bool MatrixIsChanging( TransformType_t transform = TRANSFORM_IS_GENERAL ); + + // Updates the matrix transform state + void UpdateMatrixTransform( TransformType_t transform = TRANSFORM_IS_GENERAL ); + + // Sets the vertex shader modelView state.. + // NOTE: GetProjectionMatrix should only be called from the Commit functions! + const D3DXMATRIX &GetProjectionMatrix( void ); + void SetVertexShaderViewProj(); + void SetVertexShaderModelViewProjAndModelView(); + + void SetPixelShaderFogParams( int reg ); + void SetPixelShaderFogParams( int reg, ShaderFogMode_t fogMode ); + + FORCEINLINE void UpdateVertexShaderFogParams( void ) + { + if ( g_pHardwareConfig->Caps().m_SupportsPixelShaders ) + { + float ooFogRange = 1.0f; + + float fStart = m_VertexShaderFogParams[0]; + float fEnd = m_VertexShaderFogParams[1]; + + // Check for divide by zero + if ( fEnd != fStart ) + { + ooFogRange = 1.0f / ( fEnd - fStart ); + } + + float fogParams[4]; + fogParams[0] = ooFogRange * fEnd; + fogParams[1] = 1.0f; + fogParams[2] = 1.0f - clamp( m_flFogMaxDensity, 0.0f, 1.0f ); // Max fog density + + fogParams[3] = ooFogRange; + + float vertexShaderCameraPos[4]; + vertexShaderCameraPos[0] = m_WorldSpaceCameraPositon[0]; + vertexShaderCameraPos[1] = m_WorldSpaceCameraPositon[1]; + vertexShaderCameraPos[2] = m_WorldSpaceCameraPositon[2]; + vertexShaderCameraPos[3] = m_DynamicState.m_FogZ; // waterheight + + // cFogEndOverFogRange, cFogOne, unused, cOOFogRange + SetVertexShaderConstant( VERTEX_SHADER_FOG_PARAMS, fogParams, 1 ); + + // eyepos.x eyepos.y eyepos.z cWaterZ + SetVertexShaderConstant( VERTEX_SHADER_CAMERA_POS, vertexShaderCameraPos ); + } + } + + // Compute stats info for a texture + void ComputeStatsInfo( ShaderAPITextureHandle_t hTexture, bool isCubeMap, bool isVolumeTexture ); + + // For procedural textures + void AdvanceCurrentCopy( ShaderAPITextureHandle_t hTexture ); + + // Deletes a D3D texture + void DeleteD3DTexture( ShaderAPITextureHandle_t hTexture ); + + // Unbinds a texture + void UnbindTexture( ShaderAPITextureHandle_t hTexture ); + + // Releases all D3D textures + void ReleaseAllTextures(); + + // Deletes all textures + void DeleteAllTextures(); + + // Gets the currently modified texture handle + ShaderAPITextureHandle_t GetModifyTextureHandle() const; + + // Gets the bind id + ShaderAPITextureHandle_t GetBoundTextureBindId( Sampler_t sampler ) const; + + // If mat_texture_limit is enabled, then this tells us if binding the specified texture would + // take us over the limit. + bool WouldBeOverTextureLimit( ShaderAPITextureHandle_t hTexture ); + + // Sets the texture state + void SetTextureState( Sampler_t sampler, ShaderAPITextureHandle_t hTexture, bool force = false ); + + // Grab/release the internal render targets such as the back buffer and the save game thumbnail + void AcquireInternalRenderTargets(); + void ReleaseInternalRenderTargets(); + + // create/release linear->gamma table texture lookups. Only used by hardware supporting pixel shader 2b and up + void AcquireLinearToGammaTableTextures(); + void ReleaseLinearToGammaTableTextures(); + + // Gets the texture being modified + IDirect3DBaseTexture* GetModifyTexture(); + void SetModifyTexture( IDirect3DBaseTexture *pTex ); + + // returns true if we're using texture coordinates at a given stage + bool IsUsingTextureCoordinates( int stage, int flags ) const; + + // Returns true if the board thinks we're generating spheremap coordinates + bool IsSpheremapRenderStateActive( int stage ) const; + + // Returns true if we're modulating constant color into the vertex color + bool IsModulatingVertexColor() const; + + // Recomputes ambient light cube + void RecomputeAmbientLightCube( ); + + // Debugging spew + void SpewBoardState(); + + // Compute and save the world space camera position. + void CacheWorldSpaceCameraPosition(); + + // Compute and save the projection atrix with polyoffset built in if we need it. + void CachePolyOffsetProjectionMatrix(); + + // Vertex shader helper functions + int FindVertexShader( VertexFormat_t fmt, char const* pFileName ) const; + int FindPixelShader( char const* pFileName ) const; + + // Returns copies of the front and back buffers + IDirect3DSurface* GetFrontBufferImage( ImageFormat& format ); + IDirect3DSurface* GetBackBufferImage( Rect_t *pSrcRect, Rect_t *pDstRect, ImageFormat& format ); + IDirect3DSurface* GetBackBufferImageHDR( Rect_t *pSrcRect, Rect_t *pDstRect, ImageFormat& format ); + + // Copy bits from a host-memory surface + void CopyBitsFromHostSurface( IDirect3DSurface* pSurfaceBits, + const Rect_t &dstRect, unsigned char *pData, ImageFormat srcFormat, ImageFormat dstFormat, int nDstStride ); + + FORCEINLINE void SetTransform( D3DTRANSFORMSTATETYPE State, CONST D3DXMATRIX *pMatrix ) + { +#if !defined( _X360 ) + Dx9Device()->SetTransform( State, pMatrix ); +#endif + } + + FORCEINLINE void SetLight( DWORD Index, CONST D3DLIGHT9 *pLight ) + { +#if !defined( _X360 ) + Dx9Device()->SetLight( Index, pLight ); +#endif + } + + FORCEINLINE void LightEnable( DWORD LightIndex, bool bEnable ) + { +#if !defined( _X360 ) + Dx9Device()->LightEnable( LightIndex, bEnable ); +#endif + } + + + void ExecuteCommandBuffer( uint8 *pCmdBuffer ); + void SetStandardTextureHandle( StandardTextureId_t nId, ShaderAPITextureHandle_t ); + + // Methods related to queuing functions to be called per-(pMesh->Draw call) or per-pass + void ClearAllCommitFuncs( CommitFuncType_t func, CommitShaderType_t shader ); + void CallCommitFuncs( CommitFuncType_t func, CommitShaderType_t shader, bool bForce ); + bool IsCommitFuncInUse( CommitFuncType_t func, CommitShaderType_t shader, int nFunc ) const; + void MarkCommitFuncInUse( CommitFuncType_t func, CommitShaderType_t shader, int nFunc ); + void AddCommitFunc( CommitFuncType_t func, CommitShaderType_t shader, StateCommitFunc_t f ); + void CallCommitFuncs( CommitFuncType_t func, bool bUsingFixedFunction, bool bForce = false ); + + // Commits transforms and lighting + void CommitStateChanges(); + + // Commits transforms that have to be dealt with on a per pass basis (ie. projection matrix for polyoffset) + void CommitPerPassStateChanges( StateSnapshot_t id ); + + // Need to handle fog mode on a per-pass basis + void CommitPerPassFogMode( bool bUsingVertexAndPixelShaders ); + + void CommitPerPassXboxFixups(); + + // Commits user clip planes + void CommitUserClipPlanes( bool bUsingFixedFunction ); + + // Gets the user clip transform (world->view) + D3DXMATRIX & GetUserClipTransform( ); + + // transform commit + bool VertexShaderTransformChanged( int i ); + bool FixedFunctionTransformChanged( int i ); + + void UpdateVertexShaderMatrix( int iMatrix ); + void SetVertexShaderStateSkinningMatrices(); + void CommitVertexShaderTransforms(); + void CommitPerPassVertexShaderTransforms(); + + void UpdateFixedFunctionMatrix( int iMatrix ); + void SetFixedFunctionStateSkinningMatrices(); + void CommitFixedFunctionTransforms(); + void CommitPerPassFixedFunctionTransforms(); + + // Recomputes the fast-clip plane matrices based on the current fast-clip plane + void CommitFastClipPlane( ); + + // Computes a matrix which includes the poly offset given an initial projection matrix + void ComputePolyOffsetMatrix( const D3DXMATRIX& matProjection, D3DXMATRIX &matProjectionOffset ); + + void SetSkinningMatrices(); + + // lighting commit + bool VertexShaderLightingChanged( int i ); + bool VertexShaderLightingEnableChanged( int i ); + bool FixedFunctionLightingChanged( int i ); + bool FixedFunctionLightingEnableChanged( int i ); + VertexShaderLightTypes_t ComputeLightType( int i ) const; + void SortLights( int* index ); + void CommitVertexShaderLighting(); + void CommitPixelShaderLighting( int pshReg ); + void CommitFixedFunctionLighting(); + + // Gets the surface associated with a texture (refcount of surface is increased) + IDirect3DSurface* GetTextureSurface( ShaderAPITextureHandle_t textureHandle ); + IDirect3DSurface* GetDepthTextureSurface( ShaderAPITextureHandle_t textureHandle ); + + // + // Methods related to hardware config + // + void SetDefaultConfigValuesForDxLevel( int dxLevelFromCaps, ShaderDeviceInfo_t &info, unsigned int nFlagsUsed ); + + // Determines hardware capabilities + bool DetermineHardwareCaps( ); + + // Alpha To Coverage entrypoints and states - much of this involves vendor-dependent paths and states... + bool CheckVendorDependentAlphaToCoverage(); + void EnableAlphaToCoverage(); + void DisableAlphaToCoverage(); + + // Vendor-dependent shadow mapping detection + void CheckVendorDependentShadowMappingSupport( bool &bSupportsShadowDepthTextures, bool &bSupportsFetch4 ); + + // Override caps based on a requested dx level + void OverrideCaps( int nForcedDXLevel ); + + // Reports support for a given MSAA mode + bool SupportsMSAAMode( int nMSAAMode ); + + // Reports support for a given CSAA mode + bool SupportsCSAAMode( int nNumSamples, int nQualityLevel ); + + // Gamma correction of fog color, or not... + D3DCOLOR ComputeGammaCorrectedFogColor( unsigned char r, unsigned char g, unsigned char b, bool bSRGBWritesEnabled ); + + void SetDefaultMaterial(); + + bool RestorePersistedDisplay( bool bUseFrontBuffer ); + + void ClearStdTextureHandles( void ); + + // debug logging + void PrintfVA( char *fmt, va_list vargs ); + void Printf( const char *fmt, ... ); + float Knob( char *knobname, float *setvalue = NULL ); + + // "normal" back buffer and depth buffer. Need to keep this around so that we + // know what to set the render target to when we are done rendering to a texture. + IDirect3DSurface *m_pBackBufferSurface; + IDirect3DSurface *m_pBackBufferSurfaceSRGB; + IDirect3DSurface *m_pZBufferSurface; + + // Optimization for screenshots + IDirect3DSurface *m_pSmallBackBufferFP16TempSurface; + + ShaderAPITextureHandle_t m_hFullScreenTexture; + + ShaderAPITextureHandle_t m_hLinearToGammaTableTexture; + ShaderAPITextureHandle_t m_hLinearToGammaTableIdentityTexture; + + // + // State needed at the time of rendering (after snapshots have been applied) + // + + // Interface for the D3DXMatrixStack + ID3DXMatrixStack* m_pMatrixStack[NUM_MATRIX_MODES]; + matrix3x4_t m_boneMatrix[NUM_MODEL_TRANSFORMS]; + int m_maxBoneLoaded; + + // Current matrix mode + D3DTRANSFORMSTATETYPE m_MatrixMode; + int m_CurrStack; + + // The current camera position in world space. + Vector4D m_WorldSpaceCameraPositon; + + // The current projection matrix with polyoffset baked into it. + D3DXMATRIX m_CachedPolyOffsetProjectionMatrix; + D3DXMATRIX m_CachedFastClipProjectionMatrix; + D3DXMATRIX m_CachedFastClipPolyOffsetProjectionMatrix; + + // The texture stage state that changes frequently + DynamicState_t m_DynamicState; + + // The *desired* dynamic state. Most dynamic state is committed into actual hardware state + // at either per-pass or per-material time. This can also be used to force the hardware + // to match the desired state after returning from alt-tab. + DynamicState_t m_DesiredState; + + // A list of state commit functions to run as per-draw call commit time + unsigned char m_pCommitFlags[COMMIT_FUNC_TYPE_COUNT][COMMIT_SHADER_TYPE_COUNT][ COMMIT_FUNC_BYTE_COUNT ]; + CUtlVector< StateCommitFunc_t > m_CommitFuncs[COMMIT_FUNC_TYPE_COUNT][COMMIT_SHADER_TYPE_COUNT]; + + // Render data + CMeshBase *m_pRenderMesh; + int m_nDynamicVBSize; + IMaterialInternal *m_pMaterial; + + // Shadow depth bias states + float m_fShadowSlopeScaleDepthBias; + float m_fShadowDepthBias; + + bool m_bReadPixelsEnabled; + + // Render-to-texture stuff... + bool m_UsingTextureRenderTarget; + + int m_ViewportMaxWidth; + int m_ViewportMaxHeight; + + ShaderAPITextureHandle_t m_hCachedRenderTarget; + bool m_bUsingSRGBRenderTarget; + + // Ambient cube map ok? + int m_CachedAmbientLightCube; + + // The current frame + int m_CurrentFrame; + + // The texture we're currently modifying + ShaderAPITextureHandle_t m_ModifyTextureHandle; + char m_ModifyTextureLockedLevel; + unsigned char m_ModifyTextureLockedFace; + + // Stores all textures + CUtlFixedLinkedList< Texture_t > m_Textures; + + // Mesh builder used to modify vertex data + CMeshBuilder m_ModifyBuilder; + + float m_VertexShaderFogParams[2]; + float m_flFogMaxDensity; + + // Shadow state transition table + CTransitionTable m_TransitionTable; + StateSnapshot_t m_nCurrentSnapshot; + + // Depth test override... + bool m_bOverrideMaterialIgnoreZ; + bool m_bIgnoreZValue; + + // Are we in the middle of resetting the render state? + bool m_bResettingRenderState; + + // Can we buffer 2 frames ahead? + bool m_bBuffer2FramesAhead; + + // Selection name stack + CUtlStack< int > m_SelectionNames; + bool m_InSelectionMode; + unsigned int* m_pSelectionBufferEnd; + unsigned int* m_pSelectionBuffer; + unsigned int* m_pCurrSelectionRecord; + float m_SelectionMinZ; + float m_SelectionMaxZ; + int m_NumHits; + + // fog + unsigned char m_SceneFogColor[3]; + MaterialFogMode_t m_SceneFogMode; + + // Tone Mapping state ( w is gamma scale ) + Vector4D m_ToneMappingScale; + + Deformation_t m_DeformationStack[DEFORMATION_STACK_DEPTH]; + + Deformation_t *m_pDeformationStackPtr; + + void WriteShaderConstantsToGPU(); + + // rendering parameter storage + int IntRenderingParameters[MAX_INT_RENDER_PARMS]; + float FloatRenderingParameters[MAX_FLOAT_RENDER_PARMS]; + Vector VectorRenderingParameters[MAX_VECTOR_RENDER_PARMS]; + + ShaderAPITextureHandle_t m_StdTextureHandles[TEXTURE_MAX_STD_TEXTURES]; + + // PIX instrumentation utlities...enable these with PIX_INSTRUMENTATION + void StartPIXInstrumentation(); + void EndPIXInstrumentation(); + void SetPIXMarker( unsigned long color, const char *szName ); + void IncrementPIXError(); + bool PIXError(); + int m_nPIXErrorCount; + int m_nPixFrame; + bool m_bPixCapturing; + + void ComputeVertexDescription( unsigned char* pBuffer, VertexFormat_t vertexFormat, MeshDesc_t& desc ) const + { + return MeshMgr()->ComputeVertexDescription( pBuffer, vertexFormat, desc ); + } + + // Reports support for shadow depth texturing + bool SupportsShadowDepthTextures( void ); + bool SupportsFetch4( void ); + + void SetShadowDepthBiasFactors( float fShadowSlopeScaleDepthBias, float fShadowDepthBias ); + + // Vendor-dependent depth stencil texture format + ImageFormat GetShadowDepthTextureFormat( void ); + + bool SupportsNormalMapCompression( void ) const; + bool SupportsBorderColor( void ) const; + bool SupportsFetch4( void ) const; + + void EnableBuffer2FramesAhead( bool bEnable ); + + void SetDepthFeatheringPixelShaderConstant( int iConstant, float fDepthBlendScale ); + + void SetDisallowAccess( bool b ) + { + g_bShaderAccessDisallowed = b; + } + + void EnableShaderShaderMutex( bool b ) + { + Assert( g_ShaderMutex.GetOwnerId() == 0 ); + g_bUseShaderMutex = b; + } + + void ShaderLock() + { + g_ShaderMutex.Lock(); + } + + void ShaderUnlock() + { + g_ShaderMutex.Unlock(); + } + + // Vendor-dependent slim texture format + ImageFormat GetNullTextureFormat( void ); + + //The idea behind a delayed constant is this. + // Some shaders set constants based on rendering states, and some rendering states aren't updated until after a shader's already called Draw(). + // So, for some functions that are state based, we save the constant we set and if the state changes between when it's set in the shader setup code + // and when the shader is drawn, we update that constant. + struct DelayedConstants_t + { + int iPixelShaderFogParams; + + void Invalidate( void ) + { + iPixelShaderFogParams = -1; + } + DelayedConstants_t( void ) { this->Invalidate(); } + }; + DelayedConstants_t m_DelayedShaderConstants; + + bool SetRenderTargetInternalXbox( ShaderAPITextureHandle_t hTexture, bool bForce = false ); + +#if defined( _X360 ) + CUtlStack m_VertexShaderGPRAllocationStack; +#endif + + int m_MaxVectorVertexShaderConstant; + int m_MaxBooleanVertexShaderConstant; + int m_MaxIntegerVertexShaderConstant; + int m_MaxVectorPixelShaderConstant; + int m_MaxBooleanPixelShaderConstant; + int m_MaxIntegerPixelShaderConstant; + + bool m_bGPUOwned; + bool m_bResetRenderStateNeeded; + +#ifdef ENABLE_NULLREF_DEVICE_SUPPORT + bool m_NullDevice; +#endif +}; + + +//----------------------------------------------------------------------------- +// Class Factory +//----------------------------------------------------------------------------- +static CShaderAPIDx8 g_ShaderAPIDX8; +IShaderAPIDX8 *g_pShaderAPIDX8 = &g_ShaderAPIDX8; +CShaderDeviceDx8* g_pShaderDeviceDx8 = &g_ShaderAPIDX8; + +// FIXME: Remove IShaderAPI + IShaderDevice; they change after SetMode +EXPOSE_SINGLE_INTERFACE_GLOBALVAR( CShaderAPIDx8, IShaderAPI, + SHADERAPI_INTERFACE_VERSION, g_ShaderAPIDX8 ) + +EXPOSE_SINGLE_INTERFACE_GLOBALVAR( CShaderAPIDx8, IShaderDevice, + SHADER_DEVICE_INTERFACE_VERSION, g_ShaderAPIDX8 ) + +EXPOSE_SINGLE_INTERFACE_GLOBALVAR( CShaderAPIDx8, IDebugTextureInfo, + DEBUG_TEXTURE_INFO_VERSION, g_ShaderAPIDX8 ) + +//----------------------------------------------------------------------------- +// Accessors for major interfaces +//----------------------------------------------------------------------------- + +// Pix wants a max of 32 characters +// We'll give it the right-most substrings separated by slashes +static char s_pPIXMaterialName[32]; +void PIXifyName( char *pDst, int destSize, const char *pSrc ) +{ + char *pSrcWalk = (char *)pSrc; + + while ( V_strlen( pSrcWalk ) > 31 ) // While we still have too many characters + { + char *pTok = strpbrk( pSrcWalk, "/\\" ); // Find next token + + if ( pTok ) + pSrcWalk = pTok + 1; + else + break; + } + + V_strncpy( pDst, pSrcWalk, min( 32, destSize ) ); +} + +static int AdjustUpdateRange( float const* pVec, void const *pOut, int numVecs, int* pSkip ) +{ + int skip = 0; + uint32* pSrc = (uint32*)pVec; + uint32* pDst = (uint32*)pOut; + while( numVecs && !( ( pSrc[0] ^ pDst[0] ) | ( pSrc[1] ^ pDst[1] ) | ( pSrc[2] ^ pDst[2] ) | ( pSrc[3] ^ pDst[3] ) ) ) + { + pSrc += 4; + pDst += 4; + numVecs--; + skip++; + } + *pSkip = skip; + if ( !numVecs ) + return 0; + + uint32* pSrcLast = pSrc + numVecs * 4 - 4; + uint32* pDstLast = pDst + numVecs * 4 - 4; + while( numVecs > 1 && !( ( pSrcLast[0] ^ pDstLast[0] ) | ( pSrcLast[1] ^ pDstLast[1] ) | ( pSrcLast[2] ^ pDstLast[2] ) | ( pSrcLast[3] ^ pDstLast[3] ) ) ) + { + pSrcLast -= 4; + pDstLast -= 4; + numVecs--; + } + + return numVecs; +} + +//----------------------------------------------------------------------------- +// Constructor, destructor +//----------------------------------------------------------------------------- +CShaderAPIDx8::CShaderAPIDx8() : + m_Textures( 32 ), + m_CurrStack( -1 ), + m_ModifyTextureHandle( INVALID_SHADERAPI_TEXTURE_HANDLE ), + m_pRenderMesh( 0 ), + m_nDynamicVBSize( DYNAMIC_VERTEX_BUFFER_MEMORY ), + m_pMaterial( NULL ), + m_CurrentFrame( 0 ), + m_CachedAmbientLightCube( STATE_CHANGED ), + m_InSelectionMode( false ), + m_SelectionMinZ( FLT_MAX ), + m_SelectionMaxZ( FLT_MIN ), + m_pSelectionBuffer( 0 ), + m_pSelectionBufferEnd( 0 ), + m_bResetRenderStateNeeded( false ), + m_ModifyTextureLockedLevel( -1 ), + m_nPixFrame(0), + m_bPixCapturing(false), + m_nPIXErrorCount(0), + m_pBackBufferSurface( 0 ), + m_pBackBufferSurfaceSRGB( 0 ), + m_pZBufferSurface( 0 ), + m_bResettingRenderState( false ), + m_bReadPixelsEnabled( false ), + m_ToneMappingScale( 1.0f, 1.0f, 1.0f, 1.0f ), + m_hFullScreenTexture( INVALID_SHADERAPI_TEXTURE_HANDLE ), + m_hLinearToGammaTableTexture( INVALID_SHADERAPI_TEXTURE_HANDLE ), + m_hLinearToGammaTableIdentityTexture( INVALID_SHADERAPI_TEXTURE_HANDLE ), + m_fShadowSlopeScaleDepthBias( 16.0f ), + m_fShadowDepthBias( 0.00008f ), + m_hCachedRenderTarget( INVALID_SHADERAPI_TEXTURE_HANDLE ), + m_bUsingSRGBRenderTarget( false ) +{ + // FIXME: Remove! Backward compat + m_bAdapterSet = false; + m_bBuffer2FramesAhead = false; + m_bReadPixelsEnabled = true; + + memset( m_pMatrixStack, 0, sizeof(ID3DXMatrixStack*) * NUM_MATRIX_MODES ); + memset( &m_DynamicState, 0, sizeof(m_DynamicState) ); + //m_DynamicState.m_HeightClipMode = MATERIAL_HEIGHTCLIPMODE_DISABLE; + m_nWindowHeight = m_nWindowWidth = 0; + m_maxBoneLoaded = 0; + + m_bEnableDebugTextureList = 0; + m_bDebugTexturesRendering = 0; + m_pDebugTextureList = NULL; + m_nTextureMemoryUsedLastFrame = 0; + m_nTextureMemoryUsedTotal = 0; + m_nTextureMemoryUsedPicMip1 = 0; + m_nTextureMemoryUsedPicMip2 = 0; + m_nDebugDataExportFrame = 0; + + m_SceneFogColor[0] = 0; + m_SceneFogColor[1] = 0; + m_SceneFogColor[2] = 0; + m_SceneFogMode = MATERIAL_FOG_NONE; + + // We haven't yet detected whether we support CreateQuery or not yet. + memset(IntRenderingParameters,0,sizeof(IntRenderingParameters)); + memset(FloatRenderingParameters,0,sizeof(FloatRenderingParameters)); + memset(VectorRenderingParameters,0,sizeof(VectorRenderingParameters)); + + m_pDeformationStackPtr = m_DeformationStack + DEFORMATION_STACK_DEPTH; + + m_bGPUOwned = false; + m_MaxVectorVertexShaderConstant = 0; + m_MaxBooleanVertexShaderConstant = 0; + m_MaxIntegerVertexShaderConstant = 0; + m_MaxVectorPixelShaderConstant = 0; + m_MaxBooleanPixelShaderConstant = 0; + m_MaxIntegerPixelShaderConstant = 0; + + ClearStdTextureHandles(); + + //Debugger(); +#ifdef ENABLE_NULLREF_DEVICE_SUPPORT + m_NullDevice = !!CommandLine()->FindParm( "-nulldevice" ); +#endif +} + +CShaderAPIDx8::~CShaderAPIDx8() +{ + if ( m_DynamicState.m_pVectorVertexShaderConstant ) + { + delete[] m_DynamicState.m_pVectorVertexShaderConstant; + m_DynamicState.m_pVectorVertexShaderConstant = NULL; + } + + if ( m_DynamicState.m_pBooleanVertexShaderConstant ) + { + delete[] m_DynamicState.m_pBooleanVertexShaderConstant; + m_DynamicState.m_pBooleanVertexShaderConstant = NULL; + } + + if ( m_DynamicState.m_pIntegerVertexShaderConstant ) + { + delete[] m_DynamicState.m_pIntegerVertexShaderConstant; + m_DynamicState.m_pIntegerVertexShaderConstant = NULL; + } + + if ( m_DynamicState.m_pVectorPixelShaderConstant ) + { + delete[] m_DynamicState.m_pVectorPixelShaderConstant; + m_DynamicState.m_pVectorPixelShaderConstant = NULL; + } + + if ( m_DynamicState.m_pBooleanPixelShaderConstant ) + { + delete[] m_DynamicState.m_pBooleanPixelShaderConstant; + m_DynamicState.m_pBooleanPixelShaderConstant = NULL; + } + + if ( m_DynamicState.m_pIntegerPixelShaderConstant ) + { + delete[] m_DynamicState.m_pIntegerPixelShaderConstant; + m_DynamicState.m_pIntegerPixelShaderConstant = NULL; + } + + if ( m_pDebugTextureList ) + { + m_pDebugTextureList->deleteThis(); + m_pDebugTextureList = NULL; + } +} + + +void CShaderAPIDx8::ClearStdTextureHandles( void ) +{ + for(int i = 0 ; i < ARRAYSIZE( m_StdTextureHandles ) ; i++ ) + m_StdTextureHandles[i] = INVALID_SHADERAPI_TEXTURE_HANDLE; +} + + +//----------------------------------------------------------------------------- +// FIXME: Remove! Backward compat. +//----------------------------------------------------------------------------- +bool CShaderAPIDx8::OnAdapterSet() +{ + if ( !DetermineHardwareCaps( ) ) + return false; + + // Modify the caps based on requested DXlevels + int nForcedDXLevel = CommandLine()->ParmValue( "-dxlevel", 0 ); + + if ( nForcedDXLevel > 0 ) + { + nForcedDXLevel = MAX( nForcedDXLevel, ABSOLUTE_MINIMUM_DXLEVEL ); + } + + + // FIXME: Check g_pHardwareConfig->ActualCaps() for a preferred DX level + OverrideCaps( nForcedDXLevel ); + + m_bAdapterSet = true; + return true; +} + + +void CShaderAPIDx8::GetDXLevelDefaults(uint &max_dxlevel,uint &recommended_dxlevel) +{ + max_dxlevel=g_pHardwareConfig->ActualCaps().m_nMaxDXSupportLevel; + recommended_dxlevel=g_pHardwareConfig->ActualCaps().m_nDXSupportLevel; +} + +//----------------------------------------------------------------------------- +// Can we download textures? +//----------------------------------------------------------------------------- +bool CShaderAPIDx8::CanDownloadTextures() const +{ + if ( IsDeactivated() ) + return false; + + return IsActive(); +} + + +//----------------------------------------------------------------------------- +// Grab the render targets +//----------------------------------------------------------------------------- +void CShaderAPIDx8::AcquireInternalRenderTargets() +{ + GLMPRINTF(( ">-A- CShaderAPIDx8::AcquireInternalRenderTargets... ")); + if ( mat_debugalttab.GetBool() ) + { + Warning( "mat_debugalttab: CShaderAPIDx8::AcquireInternalRenderTargets\n" ); + } + + if ( !m_pBackBufferSurface ) + { + Dx9Device()->GetRenderTarget( 0, &m_pBackBufferSurface ); +#ifdef ENABLE_NULLREF_DEVICE_SUPPORT + if( !m_NullDevice ) +#endif + { + Assert( m_pBackBufferSurface ); + } + } + +#if defined( _X360 ) + if ( !m_pBackBufferSurfaceSRGB ) + { + // create a SRGB back buffer clone + int backWidth, backHeight; + ShaderAPI()->GetBackBufferDimensions( backWidth, backHeight ); + D3DFORMAT backBufferFormat = ImageLoader::ImageFormatToD3DFormat( g_pShaderDevice->GetBackBufferFormat() ); + m_pBackBufferSurfaceSRGB = g_TextureHeap.AllocRenderTargetSurface( backWidth, backHeight, (D3DFORMAT)MAKESRGBFMT( backBufferFormat ), true, 0 ); + } +#endif + +#ifdef ENABLE_NULLREF_DEVICE_SUPPORT + if ( !m_pZBufferSurface && !m_NullDevice ) +#else + if ( !m_pZBufferSurface ) +#endif + { + Dx9Device()->GetDepthStencilSurface( &m_pZBufferSurface ); + Assert( m_pZBufferSurface ); + } + GLMPRINTF(( "<-A- CShaderAPIDx8::AcquireInternalRenderTargets...complete ")); +} + + +//----------------------------------------------------------------------------- +// Release the render targets +//----------------------------------------------------------------------------- +void CShaderAPIDx8::ReleaseInternalRenderTargets( ) +{ + GLMPRINTF(( ">-A- CShaderAPIDx8::ReleaseInternalRenderTargets... ")); + if( mat_debugalttab.GetBool() ) + { + Warning( "mat_debugalttab: CShaderAPIDx8::ReleaseInternalRenderTargets\n" ); + } + + // Note: This function does not release renderable textures created elsewhere + // Those should be released separately via the texure manager + if ( m_pBackBufferSurface ) + { +#if POSIX + // dxabstract's AddRef/Release have optional args to help track usage + int nRetVal = m_pBackBufferSurface->Release( 0, "-B CShaderAPIDx8::ReleaseInternalRenderTargets public release color buffer"); +#else + int nRetVal = m_pBackBufferSurface->Release(); +#endif + //Assert( nRetVal == 0 ); + m_pBackBufferSurface = NULL; + } + + if ( m_pZBufferSurface ) + { +#if POSIX + // dxabstract's AddRef/Release have optional args to help track usage + int nRetVal = m_pZBufferSurface->Release( 0, "-B CShaderAPIDx8::ReleaseInternalRenderTargets public release zbuffer"); +#else + int nRetVal = m_pZBufferSurface->Release(); +#endif + + //Assert( nRetVal == 0 ); //FIXME not sure why we're seeing a refcount of 3 here + m_pZBufferSurface = NULL; + } + GLMPRINTF(( "<-A- CShaderAPIDx8::ReleaseInternalRenderTargets... complete")); +} + +//----------------------------------------------------------------------------- +// During init, places the persisted texture back into the back buffer. +// The initial 360 fixup present will then not flash. This routine has to be +// self contained, no other shader api systems are viable during init. +//----------------------------------------------------------------------------- +bool CShaderAPIDx8::RestorePersistedDisplay( bool bUseFrontBuffer ) +{ +#if defined( _X360 ) + if ( !( XboxLaunch()->GetLaunchFlags() & LF_INTERNALLAUNCH ) ) + { + // there is no persisted screen + return false; + } + + OwnGPUResources( false ); + + const char *strVertexShaderProgram = + " float4x4 matWVP : register(c0);" + " struct VS_IN" + " {" + " float4 ObjPos : POSITION;" + " float2 TexCoord : TEXCOORD;" + " };" + " struct VS_OUT" + " {" + " float4 ProjPos : POSITION;" + " float2 TexCoord : TEXCOORD;" + " };" + " VS_OUT main( VS_IN In )" + " {" + " VS_OUT Out; " + " Out.ProjPos = mul( matWVP, In.ObjPos );" + " Out.TexCoord = In.TexCoord;" + " return Out;" + " }"; + + const char *strPixelShaderProgram = + " struct PS_IN" + " {" + " float2 TexCoord : TEXCOORD;" + " };" + " sampler detail;" + " float4 main( PS_IN In ) : COLOR" + " {" + " return tex2D( detail, In.TexCoord );" + " }"; + + D3DVERTEXELEMENT9 VertexElements[3] = + { + { 0, 0, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0 }, + { 0, 12, D3DDECLTYPE_FLOAT2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 0 }, + D3DDECL_END() + }; + + + IDirect3DTexture *pTexture; + if ( bUseFrontBuffer ) + { + Dx9Device()->GetFrontBuffer( &pTexture ); + } + else + { + // 360 holds a persistent image across restarts + Dx9Device()->GetPersistedTexture( &pTexture ); + } + + ID3DXBuffer *pErrorMsg = NULL; + ID3DXBuffer *pShaderCode = NULL; + + HRESULT hr = D3DXCompileShader( strVertexShaderProgram, (UINT)strlen( strVertexShaderProgram ), NULL, NULL, "main", "vs_2_0", 0, &pShaderCode, &pErrorMsg, NULL ); + if ( FAILED( hr ) ) + { + return false; + } + IDirect3DVertexShader9 *pVertexShader; + Dx9Device()->CreateVertexShader( (DWORD*)pShaderCode->GetBufferPointer(), &pVertexShader ); + pShaderCode->Release(); + + pErrorMsg = NULL; + pShaderCode = NULL; + hr = D3DXCompileShader( strPixelShaderProgram, (UINT)strlen( strPixelShaderProgram ), NULL, NULL, "main", "ps_2_0", 0, &pShaderCode, &pErrorMsg, NULL ); + if ( FAILED(hr) ) + { + return false; + } + IDirect3DPixelShader9 *pPixelShader; + Dx9Device()->CreatePixelShader( (DWORD*)pShaderCode->GetBufferPointer(), &pPixelShader ); + pShaderCode->Release(); + + int w, h; + GetBackBufferDimensions( w, h ); + + // Create a vertex declaration from the element descriptions. + IDirect3DVertexDeclaration9 *pVertexDecl; + Dx9Device()->CreateVertexDeclaration( VertexElements, &pVertexDecl ); + XMMATRIX matWVP = XMMatrixOrthographicOffCenterLH( 0, (FLOAT)w, (FLOAT)h, 0, 0, 1 ); + + ConVarRef mat_monitorgamma( "mat_monitorgamma" ); + ConVarRef mat_monitorgamma_tv_range_min( "mat_monitorgamma_tv_range_min" ); + ConVarRef mat_monitorgamma_tv_range_max( "mat_monitorgamma_tv_range_max" ); + ConVarRef mat_monitorgamma_tv_exp( "mat_monitorgamma_tv_exp" ); + ConVarRef mat_monitorgamma_tv_enabled( "mat_monitorgamma_tv_enabled" ); + g_pShaderDeviceDx8->SetHardwareGammaRamp( mat_monitorgamma.GetFloat(), mat_monitorgamma_tv_range_min.GetFloat(), mat_monitorgamma_tv_range_max.GetFloat(), + mat_monitorgamma_tv_exp.GetFloat(), mat_monitorgamma_tv_enabled.GetBool() ); + + // Structure to hold vertex data. + struct COLORVERTEX + { + FLOAT Position[3]; + float TexCoord[2]; + }; + COLORVERTEX Vertices[4]; + + Vertices[0].Position[0] = 0; + Vertices[0].Position[1] = 0; + Vertices[0].Position[2] = 0; + Vertices[0].TexCoord[0] = 0; + Vertices[0].TexCoord[1] = 0; + + Vertices[1].Position[0] = w-1; + Vertices[1].Position[1] = 0; + Vertices[1].Position[2] = 0; + Vertices[1].TexCoord[0] = 1; + Vertices[1].TexCoord[1] = 0; + + Vertices[2].Position[0] = w-1; + Vertices[2].Position[1] = h-1; + Vertices[2].Position[2] = 0; + Vertices[2].TexCoord[0] = 1; + Vertices[2].TexCoord[1] = 1; + + Vertices[3].Position[0] = 0; + Vertices[3].Position[1] = h-1; + Vertices[3].Position[2] = 0; + Vertices[3].TexCoord[0] = 0; + Vertices[3].TexCoord[1] = 1; + + Dx9Device()->SetTexture( 0, pTexture ); + Dx9Device()->SetVertexShader( pVertexShader ); + Dx9Device()->SetPixelShader( pPixelShader ); + Dx9Device()->SetVertexShaderConstantF( 0, (FLOAT*)&matWVP, 4 ); + Dx9Device()->SetVertexDeclaration( pVertexDecl ); + Dx9Device()->DrawPrimitiveUP( D3DPT_QUADLIST, 1, Vertices, sizeof( COLORVERTEX ) ); + + Dx9Device()->SetVertexShader( NULL ); + Dx9Device()->SetPixelShader( NULL ); + Dx9Device()->SetTexture( 0, NULL ); + Dx9Device()->SetVertexDeclaration( NULL ); + + pVertexShader->Release(); + pPixelShader->Release(); + pVertexDecl->Release(); + pTexture->Release(); + + OwnGPUResources( true ); + + return true; +#else + return false; +#endif +} + +//----------------------------------------------------------------------------- +// Initialize, shutdown the Device.... +//----------------------------------------------------------------------------- +bool CShaderAPIDx8::OnDeviceInit() +{ + AcquireInternalRenderTargets(); + + g_pHardwareConfig->CapsForEdit().m_TextureMemorySize = g_pShaderDeviceMgrDx8->GetVidMemBytes( m_nAdapter ); + + CreateMatrixStacks(); + + // Hide the cursor + RECORD_COMMAND( DX8_SHOW_CURSOR, 1 ); + RECORD_INT( false ); + +#if !defined( _X360 ) + Dx9Device()->ShowCursor( false ); +#endif + + // Initialize the shader manager + ShaderManager()->Init(); + + // Initialize the shader shadow + ShaderShadow()->Init(); + + // Initialize the mesh manager + MeshMgr()->Init(); + + bool bToolsMode = IsWindows() && ( CommandLine()->CheckParm( "-tools" ) != NULL ); + + // Use fat vertices when running in tools + MeshMgr()->UseFatVertices( bToolsMode ); + + // Initialize the transition table. + m_TransitionTable.Init(); + + // Initialize the render state + InitRenderState(); + + // Clear the z and color buffers + ClearBuffers( true, true, true, -1, -1 ); + + AllocFrameSyncObjects(); + AllocNonInteractiveRefreshObjects(); + + RECORD_COMMAND( DX8_BEGIN_SCENE, 0 ); + + // Apply mandatory initialization HW fixups, GPU state will be left as expected + if ( IsX360() ) + { + // place the possible persisted display into the back buffer, ready for present() + RestorePersistedDisplay( false ); + + // 360 MUST perform an initial swap to stabilize the state + // this ensures any states (e.g. gamma) are respected + // without this, the 360 resets to internal default state on the first swap + OwnGPUResources( false ); + Dx9Device()->Present( 0, 0, 0, 0 ); + + // present corrupts the GPU state and back buffer (according to docs) + // re-clear the back buffer in order to re-establish the expected contents + ResetRenderState( false ); + ClearBuffers( true, true, true, -1, -1 ); + + // place the front buffer image in the back buffer, later systems will detect and grab + // other systems will detect and grab + RestorePersistedDisplay( true ); + } + + Dx9Device()->BeginScene(); + + return true; +} + +void CShaderAPIDx8::OnDeviceShutdown() +{ + if ( !IsPC() || !IsActive() ) + return; + + // Deallocate all textures + DeleteAllTextures(); + + // Release render targets + ReleaseInternalRenderTargets(); + + // Free objects that are used for frame syncing. + FreeFrameSyncObjects(); + FreeNonInteractiveRefreshObjects(); + + for (int i = 0; i < NUM_MATRIX_MODES; ++i) + { + if (m_pMatrixStack[i]) + { + int ref = m_pMatrixStack[i]->Release(); + Assert( ref == 0 ); + } + } + + // Shutdown the transition table. + m_TransitionTable.Shutdown(); + + MeshMgr()->Shutdown(); + + ShaderManager()->Shutdown(); + + ReleaseAllVertexDecl( ); +} + + +//----------------------------------------------------------------------------- +// Sets the mode... +//----------------------------------------------------------------------------- +bool CShaderAPIDx8::SetMode( void* VD3DHWND, int nAdapter, const ShaderDeviceInfo_t &info ) +{ + // + // FIXME: Note that this entire function is backward compat and will go soon + // + + bool bRestoreNeeded = false; + + if ( IsActive() ) + { + ReleaseResources(); + OnDeviceShutdown(); + ShutdownDevice(); + bRestoreNeeded = true; + } + + LOCK_SHADERAPI(); + Assert( D3D() ); + Assert( nAdapter < g_pShaderDeviceMgr->GetAdapterCount() ); + + const HardwareCaps_t& actualCaps = g_pShaderDeviceMgr->GetHardwareCaps( nAdapter ); + + ShaderDeviceInfo_t actualInfo = info; + int nDXLevel = actualInfo.m_nDXLevel ? actualInfo.m_nDXLevel : actualCaps.m_nDXSupportLevel; + + static bool bSetModeOnce = false; + if ( !bSetModeOnce ) + { + nDXLevel = MAX( ABSOLUTE_MINIMUM_DXLEVEL, CommandLine()->ParmValue( "-dxlevel", nDXLevel ) ); + bSetModeOnce = true; + } + if ( nDXLevel > actualCaps.m_nMaxDXSupportLevel ) + { + nDXLevel = actualCaps.m_nMaxDXSupportLevel; + } + actualInfo.m_nDXLevel = g_pShaderDeviceMgr->GetClosestActualDXLevel( nDXLevel ); + + if ( !g_pShaderDeviceMgrDx8->ValidateMode( nAdapter, actualInfo ) ) + return false; + + g_pShaderAPI = this; + g_pShaderDevice = this; + g_pShaderShadow = ShaderShadow(); + bool bOk = InitDevice( VD3DHWND, nAdapter, actualInfo ); + if ( !bOk ) + return false; + + if ( !OnDeviceInit() ) + return false; + + if ( bRestoreNeeded && IsPC() ) + { + ReacquireResources(); + } + + return true; +} + + +//----------------------------------------------------------------------------- +// Creates the matrix stack +//----------------------------------------------------------------------------- +void CShaderAPIDx8::CreateMatrixStacks() +{ + MEM_ALLOC_D3D_CREDIT(); + + for (int i = 0; i < NUM_MATRIX_MODES; ++i) + { + HRESULT hr = D3DXCreateMatrixStack( 0, &m_pMatrixStack[i] ); + Assert( hr == D3D_OK ); + } +} + + +//----------------------------------------------------------------------------- +// Vendor-dependent code to turn on alpha to coverage +//----------------------------------------------------------------------------- +void CShaderAPIDx8::EnableAlphaToCoverage( void ) +{ + if( !g_pHardwareConfig->ActualCaps().m_bSupportsAlphaToCoverage || !IsAAEnabled() ) + return; + + D3DRENDERSTATETYPE renderState = (D3DRENDERSTATETYPE)g_pHardwareConfig->Caps().m_AlphaToCoverageState; + SetRenderState( renderState, g_pHardwareConfig->Caps().m_AlphaToCoverageEnableValue ); // Vendor dependent state +} + +//----------------------------------------------------------------------------- +// Vendor-dependent code to turn off alpha to coverage +//----------------------------------------------------------------------------- +void CShaderAPIDx8::DisableAlphaToCoverage() +{ + if( !g_pHardwareConfig->ActualCaps().m_bSupportsAlphaToCoverage || !IsAAEnabled() ) + return; + + D3DRENDERSTATETYPE renderState = (D3DRENDERSTATETYPE)g_pHardwareConfig->Caps().m_AlphaToCoverageState; + SetRenderState( renderState, g_pHardwareConfig->Caps().m_AlphaToCoverageDisableValue ); // Vendor dependent state +} + +//----------------------------------------------------------------------------- +// Determine capabilities +//----------------------------------------------------------------------------- +bool CShaderAPIDx8::DetermineHardwareCaps( ) +{ + HardwareCaps_t& actualCaps = g_pHardwareConfig->ActualCapsForEdit(); + if ( !g_pShaderDeviceMgrDx8->ComputeCapsFromD3D( &actualCaps, m_DisplayAdapter ) ) + return false; + + // See if the file tells us otherwise + g_pShaderDeviceMgrDx8->ReadDXSupportLevels( actualCaps ); + + // Read dxsupport.cfg which has config overrides for particular cards. + g_pShaderDeviceMgrDx8->ReadHardwareCaps( actualCaps, actualCaps.m_nMaxDXSupportLevel ); + + // What's in "-shader" overrides dxsupport.cfg + const char *pShaderParam = CommandLine()->ParmValue( "-shader" ); + if ( pShaderParam ) + { + Q_strncpy( actualCaps.m_pShaderDLL, pShaderParam, sizeof( actualCaps.m_pShaderDLL ) ); + } + + return true; +} + + +//----------------------------------------------------------------------------- +// Override caps based on a particular dx level +//----------------------------------------------------------------------------- +void CShaderAPIDx8::OverrideCaps( int nForcedDXLevel ) +{ + // Just use the actual caps if we can't use what was requested or if the default is requested + if ( nForcedDXLevel <= 0 ) + { + nForcedDXLevel = g_pHardwareConfig->ActualCaps().m_nDXSupportLevel; + } + nForcedDXLevel = g_pShaderDeviceMgr->GetClosestActualDXLevel( nForcedDXLevel ); + + g_pHardwareConfig->SetupHardwareCaps( nForcedDXLevel, g_pHardwareConfig->ActualCaps() ); +} + + +//----------------------------------------------------------------------------- +// Called when the dx support level has changed +//----------------------------------------------------------------------------- +void CShaderAPIDx8::DXSupportLevelChanged() +{ + LOCK_SHADERAPI(); + if ( IsPC() ) + { + OverrideCaps( ShaderUtil()->GetConfig().dxSupportLevel ); + } + else + { + Assert( 0 ); + } +} + + +//----------------------------------------------------------------------------- +// FIXME: Remove! Backward compat only +//----------------------------------------------------------------------------- +int CShaderAPIDx8::GetActualTextureStageCount() const +{ + return g_pHardwareConfig->GetActualTextureStageCount(); +} + +int CShaderAPIDx8::GetActualSamplerCount() const +{ + return g_pHardwareConfig->GetActualSamplerCount(); +} + +int CShaderAPIDx8::StencilBufferBits() const +{ + return m_bUsingStencil ? m_iStencilBufferBits : 0; +} + +bool CShaderAPIDx8::IsAAEnabled() const +{ + bool bAntialiasing = ( m_PresentParameters.MultiSampleType != D3DMULTISAMPLE_NONE ); + return bAntialiasing; +} + + +//----------------------------------------------------------------------------- +// Methods related to queuing functions to be called per-(pMesh->Draw call) or per-pass +//----------------------------------------------------------------------------- +bool CShaderAPIDx8::IsCommitFuncInUse( CommitFuncType_t func, CommitShaderType_t shader, int nFunc ) const +{ + Assert( nFunc < COMMIT_FUNC_COUNT ); + return ( m_pCommitFlags[func][shader][ nFunc >> 3 ] & ( 1 << ( nFunc & 0x7 ) ) ) != 0; +} + +void CShaderAPIDx8::MarkCommitFuncInUse( CommitFuncType_t func, CommitShaderType_t shader, int nFunc ) +{ + m_pCommitFlags[func][shader][ nFunc >> 3 ] |= 1 << ( nFunc & 0x7 ); +} + +void CShaderAPIDx8::AddCommitFunc( CommitFuncType_t func, CommitShaderType_t shader, StateCommitFunc_t f ) +{ + m_CommitFuncs[func][shader].AddToTail( f ); +} + + +//----------------------------------------------------------------------------- +// Clears all commit functions +//----------------------------------------------------------------------------- +void CShaderAPIDx8::ClearAllCommitFuncs( CommitFuncType_t func, CommitShaderType_t shader ) +{ + memset( m_pCommitFlags[func][shader], 0, COMMIT_FUNC_BYTE_COUNT ); + m_CommitFuncs[func][shader].RemoveAll(); +} + + +//----------------------------------------------------------------------------- +// Calls all commit functions in a particular list +//----------------------------------------------------------------------------- +void CShaderAPIDx8::CallCommitFuncs( CommitFuncType_t func, CommitShaderType_t shader, bool bForce ) +{ + // 360 does not have have a FF pipe + Assert ( IsPC() || ( IsX360() && shader != COMMIT_FIXED_FUNCTION ) ); + + // Don't bother committing anything if we're deactivated + if ( IsDeactivated() ) + return; + + CUtlVector< StateCommitFunc_t > &funcList = m_CommitFuncs[func][shader]; + int nCount = funcList.Count(); + if ( nCount == 0 ) + return; + + for ( int i = 0; i < nCount; ++i ) + { + funcList[i]( Dx9Device(), m_DesiredState, m_DynamicState, bForce ); + } + ClearAllCommitFuncs( func, shader ); +} + + +//----------------------------------------------------------------------------- +// Calls all per-mesh draw commit functions +//----------------------------------------------------------------------------- +void CShaderAPIDx8::CallCommitFuncs( CommitFuncType_t func, bool bUsingFixedFunction, bool bForce ) +{ + // Fixed-Function only funcs + if ( IsPC() && ( bUsingFixedFunction || bForce ) ) + { + CallCommitFuncs( func, COMMIT_FIXED_FUNCTION, bForce ); + } + + // Vertex-shader only funcs + if ( !bUsingFixedFunction || bForce ) + { + CallCommitFuncs( func, COMMIT_VERTEX_SHADER, bForce ); + } + + // State set in both FF + VS + CallCommitFuncs( func, COMMIT_ALWAYS, bForce ); +} + +//----------------------------------------------------------------------------- +// Sets the sampler state +//----------------------------------------------------------------------------- +static FORCEINLINE void SetSamplerState( IDirect3DDevice9 *pDevice, int stage, D3DSAMPLERSTATETYPE state, DWORD val ) +{ + RECORD_SAMPLER_STATE( stage, state, val ); + +#if defined( _X360 ) + if ( state == D3DSAMP_NOTSUPPORTED ) + return; +#endif + + pDevice->SetSamplerState( stage, state, val ); +} + +inline void CShaderAPIDx8::SetSamplerState( int stage, D3DSAMPLERSTATETYPE state, DWORD val ) +{ +#ifndef DX_TO_GL_ABSTRACTION + if ( IsDeactivated() ) + return; +#endif + + ::SetSamplerState( Dx9Device(), stage, state, val ); +} + +//----------------------------------------------------------------------------- +// Sets the texture stage state +//----------------------------------------------------------------------------- +inline void CShaderAPIDx8::SetTextureStageState( int stage, D3DTEXTURESTAGESTATETYPE state, DWORD val ) +{ +#if !defined( _X360 ) + if ( IsDeactivated() ) + return; + + Dx9Device()->SetTextureStageState( stage, state, val ); +#endif +} + +inline void CShaderAPIDx8::SetRenderState( D3DRENDERSTATETYPE state, DWORD val, bool bFlushIfChanged ) +{ +#if ( !defined( _X360 ) && !defined( DX_TO_GL_ABSTRACTION ) ) + { + if ( IsDeactivated() ) + return; + } +#else + { + Assert( state != D3DRS_NOTSUPPORTED ); //Use SetSupportedRenderState() macro to avoid this at compile time + //if ( state == D3DRS_NOTSUPPORTED ) + // return; + } +#endif + + Assert( state >= 0 && ( int )state < MAX_NUM_RENDERSTATES ); + if ( m_DynamicState.m_RenderState[state] != val ) + { + if ( bFlushIfChanged ) + { + FlushBufferedPrimitives(); + } +#ifdef DX_TO_GL_ABSTRACTION + Dx9Device()->SetRenderStateInline( state, val ); +#else + Dx9Device()->SetRenderState( state, val ); +#endif + m_DynamicState.m_RenderState[state] = val; + } +} + +#ifdef DX_TO_GL_ABSTRACTION + // Purposely always writing the new state (even if it's not changed), in case SetRenderStateConstInline() compiles away to nothing (it sometimes does) + #define SetRenderStateConstMacro(t, state, val ) \ + do \ + { \ + Assert( state >= 0 && ( int )state < MAX_NUM_RENDERSTATES ); \ + if ( t->m_DynamicState.m_RenderState[state] != (DWORD)val ) \ + { \ + Dx9Device()->SetRenderStateConstInline( state, val ); \ + } \ + t->m_DynamicState.m_RenderState[state] = val; \ + } while(0) +#else + #define SetRenderStateConstMacro(t, state, val ) t->SetRenderState( state, val ); +#endif + +//----------------------------------------------------------------------------- +// Commits viewports +//----------------------------------------------------------------------------- +static void CommitSetScissorRect( IDirect3DDevice9 *pDevice, const DynamicState_t &desiredState, DynamicState_t ¤tState, bool bForce ) +{ + // Set the enable/disable renderstate + + bool bEnableChanged = desiredState.m_RenderState[D3DRS_SCISSORTESTENABLE] != currentState.m_RenderState[D3DRS_SCISSORTESTENABLE]; + if ( bEnableChanged ) + { + Dx9Device()->SetRenderState( D3DRS_SCISSORTESTENABLE, desiredState.m_RenderState[D3DRS_SCISSORTESTENABLE] ); + currentState.m_RenderState[D3DRS_SCISSORTESTENABLE] = desiredState.m_RenderState[D3DRS_SCISSORTESTENABLE]; + } + + // Only bother with the rect if we're enabling + if ( desiredState.m_RenderState[D3DRS_SCISSORTESTENABLE] ) + { + int nWidth, nHeight; + ITexture *pTexture = ShaderAPI()->GetRenderTargetEx( 0 ); + if ( pTexture == NULL ) + { + ShaderAPI()->GetBackBufferDimensions( nWidth, nHeight ); + } + else + { + nWidth = pTexture->GetActualWidth(); + nHeight = pTexture->GetActualHeight(); + } + + Assert( (desiredState.m_ScissorRect.left <= nWidth) && (desiredState.m_ScissorRect.bottom <= nHeight) && + ( desiredState.m_ScissorRect.top >= 0 ) && (desiredState.m_ScissorRect.left >= 0) ); + + clamp( desiredState.m_ScissorRect.right, 0, nWidth ); + clamp( desiredState.m_ScissorRect.left, 0, nWidth ); + clamp( desiredState.m_ScissorRect.top, 0, nHeight ); + clamp( desiredState.m_ScissorRect.bottom, 0, nHeight ); + + Dx9Device()->SetScissorRect( &desiredState.m_ScissorRect ); + currentState.m_ScissorRect = desiredState.m_ScissorRect; + } +} + +// Routine for setting scissor rect +// If pScissorRect is NULL, disable scissoring by setting the render state +// If pScissorRect is non-NULL, set the RECT state in Direct3D AND set the renderstate +inline void CShaderAPIDx8::SetScissorRect( const int nLeft, const int nTop, const int nRight, const int nBottom, const bool bEnableScissor ) +{ + Assert( (nLeft <= nRight) && (nTop <= nBottom) ); //360 craps itself if this isn't true + if ( !g_pHardwareConfig->Caps().m_bScissorSupported ) + return; + + // If we're turning it on, check the validity of the rect + if ( bEnableScissor ) + { + int nWidth, nHeight; + ITexture *pTexture = GetRenderTargetEx( 0 ); + if ( pTexture == NULL ) + { + GetBackBufferDimensions( nWidth, nHeight ); + } + else + { + nWidth = pTexture->GetActualWidth(); + nHeight = pTexture->GetActualHeight(); + } + + Assert( (nRight <= nWidth) && (nBottom <= nHeight) && ( nTop >= 0 ) && (nLeft >= 0) ); + + clamp( nRight, 0, nWidth ); + clamp( nLeft, 0, nWidth ); + clamp( nTop, 0, nHeight ); + clamp( nBottom, 0, nHeight ); + } + + DWORD dwEnableScissor = bEnableScissor ? TRUE : FALSE; + RECT newScissorRect; + newScissorRect.left = nLeft; + newScissorRect.top = nTop; + newScissorRect.right = nRight; + newScissorRect.bottom = nBottom; + + if ( !m_bResettingRenderState ) + { + bool bEnableChanged = m_DesiredState.m_RenderState[D3DRS_SCISSORTESTENABLE] != dwEnableScissor; + bool bRectChanged = memcmp( &newScissorRect, &m_DesiredState.m_ScissorRect, sizeof(RECT) ) != 0; + + if ( !bEnableChanged && !bRectChanged ) + return; + } + + if ( !IsDeactivated() ) + { + // Do we need to do this always? + FlushBufferedPrimitives(); + } + + m_DesiredState.m_RenderState[D3DRS_SCISSORTESTENABLE] = dwEnableScissor; + memcpy( &m_DesiredState.m_ScissorRect, &newScissorRect, sizeof(RECT) ); + + ADD_COMMIT_FUNC( COMMIT_PER_DRAW, COMMIT_ALWAYS, CommitSetScissorRect ); +} + +inline void CShaderAPIDx8::SetRenderStateForce( D3DRENDERSTATETYPE state, DWORD val ) +{ +#if ( !defined( _X360 ) && !defined( DX_TO_GL_ABSTRACTION ) ) + { + if ( IsDeactivated() ) + return; + } +#else + { + Assert( state != D3DRS_NOTSUPPORTED ); //Use SetSupportedRenderStateForce() macro to avoid this at compile time + //if ( state == D3DRS_NOTSUPPORTED ) + // return; + } +#endif + + Dx9Device()->SetRenderState( state, val ); + m_DynamicState.m_RenderState[state] = val; +} + + +//----------------------------------------------------------------------------- +// Set the values for pixel shader constants that don't change. +//----------------------------------------------------------------------------- +void CShaderAPIDx8::SetStandardVertexShaderConstants( float fOverbright ) +{ + LOCK_SHADERAPI(); + if ( g_pHardwareConfig->GetDXSupportLevel() < 80 ) + return; + + // Set a couple standard constants.... + Vector4D standardVertexShaderConstant( 0.0f, 1.0f, 2.0f, 0.5f ); + SetVertexShaderConstant( VERTEX_SHADER_MATH_CONSTANTS0, standardVertexShaderConstant.Base(), 1 ); + + // [ gamma, overbright, 1/3, 1/overbright ] + standardVertexShaderConstant.Init( 1.0f/2.2f, fOverbright, 1.0f / 3.0f, 1.0f / fOverbright ); + SetVertexShaderConstant( VERTEX_SHADER_MATH_CONSTANTS1, standardVertexShaderConstant.Base(), 1 ); + + int nModelIndex = g_pHardwareConfig->Caps().m_nDXSupportLevel < 90 ? VERTEX_SHADER_MODEL - 10 : VERTEX_SHADER_MODEL; + +/* + if ( g_pHardwareConfig->Caps().m_SupportsVertexShaders_3_0 ) + { + Vector4D factors[4]; + factors[0].Init( 1, 0, 0, 0 ); + factors[1].Init( 0, 1, 0, 0 ); + factors[2].Init( 0, 0, 1, 0 ); + factors[3].Init( 0, 0, 0, 1 ); + SetVertexShaderConstant( VERTEX_SHADER_DOT_PRODUCT_FACTORS, factors[0].Base(), 4 ); + } +*/ + + if ( g_pHardwareConfig->Caps().m_SupportsVertexShaders_2_0 ) + { + float c[4] = { 0.0f, 0.0f, 0.0f, 0.0f }; + SetVertexShaderConstant( VERTEX_SHADER_FLEXSCALE, c, 1 ); + } + else + { + // These point to the lighting and the transforms + standardVertexShaderConstant.Init( + VERTEX_SHADER_LIGHTS, + VERTEX_SHADER_LIGHTS + 5, + // Use COLOR instead of UBYTE4 since Geforce3 does not support it + // vConst.w should be 3, but due to about hack, mul by 255 and add epsilon + // 360 supports UBYTE4, so no fixup required + (IsPC() || !IsX360()) ? 765.01f : 3.0f, + nModelIndex ); // DX8 has different constant packing + + SetVertexShaderConstant( VERTEX_SHADER_LIGHT_INDEX, standardVertexShaderConstant.Base(), 1 ); + } +} + +//----------------------------------------------------------------------------- +// Initialize vertex and pixel shaders +//----------------------------------------------------------------------------- +void CShaderAPIDx8::InitVertexAndPixelShaders() +{ + // Allocate space for the pixel and vertex shader constants... + if ( g_pHardwareConfig->Caps().m_SupportsPixelShaders ) + { + // pixel shaders + { + if (m_DynamicState.m_pVectorPixelShaderConstant) + { + delete[] m_DynamicState.m_pVectorPixelShaderConstant; + } + m_DynamicState.m_pVectorPixelShaderConstant = new Vector4D[g_pHardwareConfig->Caps().m_NumPixelShaderConstants]; + + if (m_DesiredState.m_pVectorPixelShaderConstant) + { + delete[] m_DesiredState.m_pVectorPixelShaderConstant; + } + m_DesiredState.m_pVectorPixelShaderConstant = new Vector4D[g_pHardwareConfig->Caps().m_NumPixelShaderConstants]; + + if (m_DynamicState.m_pBooleanPixelShaderConstant) + { + delete[] m_DynamicState.m_pBooleanPixelShaderConstant; + } + m_DynamicState.m_pBooleanPixelShaderConstant = new BOOL[g_pHardwareConfig->Caps().m_NumBooleanPixelShaderConstants]; + + if (m_DesiredState.m_pBooleanPixelShaderConstant) + { + delete[] m_DesiredState.m_pBooleanPixelShaderConstant; + } + m_DesiredState.m_pBooleanPixelShaderConstant = new BOOL[g_pHardwareConfig->Caps().m_NumBooleanPixelShaderConstants]; + + if (m_DynamicState.m_pIntegerPixelShaderConstant) + { + delete[] m_DynamicState.m_pIntegerPixelShaderConstant; + } + m_DynamicState.m_pIntegerPixelShaderConstant = new IntVector4D[g_pHardwareConfig->Caps().m_NumIntegerPixelShaderConstants]; + + if (m_DesiredState.m_pIntegerPixelShaderConstant) + { + delete[] m_DesiredState.m_pIntegerPixelShaderConstant; + } + m_DesiredState.m_pIntegerPixelShaderConstant = new IntVector4D[g_pHardwareConfig->Caps().m_NumIntegerPixelShaderConstants]; + + // force reset vector pixel constants + int i; + for ( i = 0; i < g_pHardwareConfig->Caps().m_NumPixelShaderConstants; ++i ) + { + m_DesiredState.m_pVectorPixelShaderConstant[i].Init(); + } + SetPixelShaderConstant( 0, m_DesiredState.m_pVectorPixelShaderConstant[0].Base(), g_pHardwareConfig->Caps().m_NumPixelShaderConstants, true ); + + // force reset boolean pixel constants + int nNumBooleanPixelShaderConstants = g_pHardwareConfig->Caps().m_NumBooleanPixelShaderConstants; + if ( nNumBooleanPixelShaderConstants ) + { + for ( i = 0; i < nNumBooleanPixelShaderConstants; ++i ) + { + m_DesiredState.m_pBooleanPixelShaderConstant[i] = 0; + } + SetBooleanPixelShaderConstant( 0, m_DesiredState.m_pBooleanPixelShaderConstant, nNumBooleanPixelShaderConstants, true ); + } + + // force reset integer pixel constants + int nNumIntegerPixelShaderConstants = g_pHardwareConfig->Caps().m_NumIntegerPixelShaderConstants; + if ( nNumIntegerPixelShaderConstants ) + { + for ( i = 0; i < nNumIntegerPixelShaderConstants; ++i ) + { + m_DesiredState.m_pIntegerPixelShaderConstant[i].Init(); + } + SetIntegerPixelShaderConstant( 0, m_DesiredState.m_pIntegerPixelShaderConstant[0].Base(), nNumIntegerPixelShaderConstants, true ); + } + } + + // vertex shaders + { + if (m_DynamicState.m_pVectorVertexShaderConstant) + { + delete[] m_DynamicState.m_pVectorVertexShaderConstant; + } + m_DynamicState.m_pVectorVertexShaderConstant = new Vector4D[g_pHardwareConfig->Caps().m_NumVertexShaderConstants]; + + if (m_DesiredState.m_pVectorVertexShaderConstant) + { + delete[] m_DesiredState.m_pVectorVertexShaderConstant; + } + m_DesiredState.m_pVectorVertexShaderConstant = new Vector4D[g_pHardwareConfig->Caps().m_NumVertexShaderConstants]; + + if (m_DynamicState.m_pBooleanVertexShaderConstant) + { + delete[] m_DynamicState.m_pBooleanVertexShaderConstant; + } + m_DynamicState.m_pBooleanVertexShaderConstant = new BOOL[g_pHardwareConfig->Caps().m_NumBooleanVertexShaderConstants]; + + if (m_DesiredState.m_pBooleanVertexShaderConstant) + { + delete[] m_DesiredState.m_pBooleanVertexShaderConstant; + } + m_DesiredState.m_pBooleanVertexShaderConstant = new BOOL[g_pHardwareConfig->Caps().m_NumBooleanVertexShaderConstants]; + + if (m_DynamicState.m_pIntegerVertexShaderConstant) + { + delete[] m_DynamicState.m_pIntegerVertexShaderConstant; + } + m_DynamicState.m_pIntegerVertexShaderConstant = new IntVector4D[g_pHardwareConfig->Caps().m_NumIntegerVertexShaderConstants]; + + if (m_DesiredState.m_pIntegerVertexShaderConstant) + { + delete[] m_DesiredState.m_pIntegerVertexShaderConstant; + } + m_DesiredState.m_pIntegerVertexShaderConstant = new IntVector4D[g_pHardwareConfig->Caps().m_NumIntegerVertexShaderConstants]; + + // force reset vector vertex constants + int i; + for ( i = 0; i < g_pHardwareConfig->Caps().m_NumVertexShaderConstants; ++i ) + { + m_DesiredState.m_pVectorVertexShaderConstant[i].Init(); + } + SetVertexShaderConstant( 0, m_DesiredState.m_pVectorVertexShaderConstant[0].Base(), g_pHardwareConfig->Caps().m_NumVertexShaderConstants, true ); + + // force reset boolean vertex constants + for ( i = 0; i < g_pHardwareConfig->Caps().m_NumBooleanVertexShaderConstants; ++i ) + { + m_DesiredState.m_pBooleanVertexShaderConstant[i] = 0; + } + SetBooleanVertexShaderConstant( 0, m_DesiredState.m_pBooleanVertexShaderConstant, g_pHardwareConfig->Caps().m_NumBooleanVertexShaderConstants, true ); + + // force reset integer vertex constants + for ( i = 0; i < g_pHardwareConfig->Caps().m_NumIntegerVertexShaderConstants; ++i ) + { + m_DesiredState.m_pIntegerVertexShaderConstant[i].Init(); + } + SetIntegerVertexShaderConstant( 0, m_DesiredState.m_pIntegerVertexShaderConstant[0].Base(), g_pHardwareConfig->Caps().m_NumIntegerVertexShaderConstants, true ); + } + + if ( IsX360() ) + { + // to init/update all constants, must disable ownership + bool bPreviousState = OwnGPUResources( false ); + WriteShaderConstantsToGPU(); + OwnGPUResources( bPreviousState ); + } + SetStandardVertexShaderConstants( OVERBRIGHT ); + } + + // Set up the vertex and pixel shader stuff + ShaderManager()->ResetShaderState(); +} + + +//----------------------------------------------------------------------------- +// Initialize render state +//----------------------------------------------------------------------------- +void CShaderAPIDx8::InitRenderState() +{ + // Set the default shadow state + g_pShaderShadowDx8->SetDefaultState(); + + // Grab a snapshot of this state; we'll be using it to set the board + // state to something well defined. + m_TransitionTable.TakeDefaultStateSnapshot(); + + if ( !IsDeactivated() ) + { + ResetRenderState(); + } +} + + +//----------------------------------------------------------------------------- +// Commits vertex textures +//----------------------------------------------------------------------------- +static void CommitVertexTextures( IDirect3DDevice9 *pDevice, const DynamicState_t &desiredState, DynamicState_t ¤tState, bool bForce ) +{ + int nCount = g_pMaterialSystemHardwareConfig->GetVertexTextureCount(); + for ( int i = 0; i < nCount; ++i ) + { + VertexTextureState_t ¤tVTState = currentState.m_VertexTextureState[i]; + ShaderAPITextureHandle_t textureHandle = desiredState.m_VertexTextureState[i].m_BoundTexture; + + Texture_t *pTexture = ( textureHandle != INVALID_SHADERAPI_TEXTURE_HANDLE ) ? &g_ShaderAPIDX8.GetTexture( textureHandle ) : NULL; + if ( pTexture && ( pTexture->m_Flags & Texture_t::IS_VERTEX_TEXTURE ) == 0 ) + { + Warning( "Attempting to bind a vertex texture (%s) which was not created as a vertex texture!\n", pTexture->m_DebugName.String() ); + } + + if ( bForce || ( currentVTState.m_BoundTexture != textureHandle ) ) + { + currentVTState.m_BoundTexture = textureHandle; + +// RECORD_COMMAND( DX8_SET_VERTEXTEXTURE, 3 ); +// RECORD_INT( D3DVERTEXTEXTURESAMPLER0 + nStage ); +// RECORD_INT( pTexture ? pTexture->GetUniqueID() : 0xFFFF ); +// RECORD_INT( 0 ); + + IDirect3DBaseTexture *pD3DTexture = ( textureHandle >= 0 ) ? g_ShaderAPIDX8.GetD3DTexture( textureHandle ) : NULL; + + pDevice->SetTexture( D3DVERTEXTEXTURESAMPLER0 + i, pD3DTexture ); + } + + if ( !pTexture ) + continue; + + D3DTEXTUREADDRESS nNewUTexWrap = pTexture->m_UTexWrap; + if ( bForce || ( currentVTState.m_UTexWrap != nNewUTexWrap ) ) + { + currentVTState.m_UTexWrap = nNewUTexWrap; + SetSamplerState( pDevice, D3DVERTEXTEXTURESAMPLER0 + i, D3DSAMP_ADDRESSU, currentVTState.m_UTexWrap ); + } + + D3DTEXTUREADDRESS nNewVTexWrap = pTexture->m_VTexWrap; + if ( bForce || ( currentVTState.m_VTexWrap != nNewVTexWrap ) ) + { + currentVTState.m_VTexWrap = nNewVTexWrap; + SetSamplerState( pDevice, D3DVERTEXTEXTURESAMPLER0 + i, D3DSAMP_ADDRESSV, currentVTState.m_VTexWrap ); + } + + /* + D3DTEXTUREADDRESS nNewWTexWrap = pTexture->GetWTexWrap(); + if ( bForce || ( currentVTState.m_WTexWrap != nNewWTexWrap ) ) + { + currentVTState.m_WTexWrap = nNewWTexWrap; + SetSamplerState( pDevice, D3DVERTEXTEXTURESAMPLER0 + i, D3DSAMP_ADDRESSW, currentVTState.m_WTexWrap ); + } + */ + + D3DTEXTUREFILTERTYPE nNewMinFilter = pTexture->m_MinFilter; + if ( bForce || ( currentVTState.m_MinFilter != nNewMinFilter ) ) + { + currentVTState.m_MinFilter = nNewMinFilter; + SetSamplerState( pDevice, D3DVERTEXTEXTURESAMPLER0 + i, D3DSAMP_MINFILTER, currentVTState.m_MinFilter ); + } + + D3DTEXTUREFILTERTYPE nNewMagFilter = pTexture->m_MagFilter; + if ( bForce || ( currentVTState.m_MagFilter != nNewMagFilter ) ) + { + currentVTState.m_MagFilter = nNewMagFilter; + SetSamplerState( pDevice, D3DVERTEXTEXTURESAMPLER0 + i, D3DSAMP_MAGFILTER, currentVTState.m_MagFilter ); + } + + D3DTEXTUREFILTERTYPE nNewMipFilter = pTexture->m_MipFilter; + if ( bForce || ( currentVTState.m_MipFilter != nNewMipFilter ) ) + { + currentVTState.m_MipFilter = nNewMipFilter; + SetSamplerState( pDevice, D3DVERTEXTEXTURESAMPLER0 + i, D3DSAMP_MIPFILTER, currentVTState.m_MipFilter ); + } + } +} + + +//----------------------------------------------------------------------------- +// Returns true if the state snapshot is translucent +//----------------------------------------------------------------------------- +bool CShaderAPIDx8::IsTranslucent( StateSnapshot_t id ) const +{ + LOCK_SHADERAPI(); + return m_TransitionTable.GetSnapshot(id).m_AlphaBlendEnable; +} + + +//----------------------------------------------------------------------------- +// Returns true if the state snapshot is alpha tested +//----------------------------------------------------------------------------- +bool CShaderAPIDx8::IsAlphaTested( StateSnapshot_t id ) const +{ + LOCK_SHADERAPI(); + return m_TransitionTable.GetSnapshot(id).m_AlphaTestEnable; +} + + +//----------------------------------------------------------------------------- +// Gets at the shadow state for a particular state snapshot +//----------------------------------------------------------------------------- +bool CShaderAPIDx8::IsDepthWriteEnabled( StateSnapshot_t id ) const +{ + LOCK_SHADERAPI(); + return m_TransitionTable.GetSnapshot(id).m_ZWriteEnable; +} + + + +//----------------------------------------------------------------------------- +// Returns true if the state snapshot uses shaders +//----------------------------------------------------------------------------- +bool CShaderAPIDx8::UsesVertexAndPixelShaders( StateSnapshot_t id ) const +{ + LOCK_SHADERAPI(); + return m_TransitionTable.GetSnapshotShader(id).m_VertexShader != INVALID_SHADER; +} + + +//----------------------------------------------------------------------------- +// Takes a snapshot +//----------------------------------------------------------------------------- +StateSnapshot_t CShaderAPIDx8::TakeSnapshot( ) +{ + LOCK_SHADERAPI(); + + return m_TransitionTable.TakeSnapshot(); +} + +void CShaderAPIDx8::ResetDXRenderState( void ) +{ + float zero = 0.0f; + float one = 1.0f; + DWORD dZero = *((DWORD*)(&zero)); + DWORD dOne = *((DWORD*)(&one)); + + // Set default values for all dx render states. + // NOTE: this does not include states encapsulated by the transition table, + // which are reset in CTransitionTable::UseDefaultState + SetSupportedRenderStateForce( D3DRS_FILLMODE, D3DFILL_SOLID ); + SetSupportedRenderStateForce( D3DRS_SHADEMODE, D3DSHADE_GOURAUD ); + SetSupportedRenderStateForce( D3DRS_LASTPIXEL, TRUE ); + SetSupportedRenderStateForce( D3DRS_CULLMODE, D3DCULL_CCW ); + SetSupportedRenderStateForce( D3DRS_DITHERENABLE, FALSE ); + SetSupportedRenderStateForce( D3DRS_FOGENABLE, FALSE ); + SetSupportedRenderStateForce( D3DRS_SPECULARENABLE, FALSE ); + SetSupportedRenderStateForce( D3DRS_FOGCOLOR, 0 ); + SetSupportedRenderStateForce( D3DRS_FOGTABLEMODE, D3DFOG_NONE ); + SetSupportedRenderStateForce( D3DRS_FOGSTART, dZero ); + SetSupportedRenderStateForce( D3DRS_FOGEND, dOne ); + SetSupportedRenderStateForce( D3DRS_FOGDENSITY, dZero ); + SetSupportedRenderStateForce( D3DRS_RANGEFOGENABLE, FALSE ); + SetSupportedRenderStateForce( D3DRS_STENCILENABLE, FALSE); + SetSupportedRenderStateForce( D3DRS_STENCILFAIL, D3DSTENCILOP_KEEP ); + SetSupportedRenderStateForce( D3DRS_STENCILZFAIL, D3DSTENCILOP_KEEP ); + SetSupportedRenderStateForce( D3DRS_STENCILPASS, D3DSTENCILOP_KEEP ); + SetSupportedRenderStateForce( D3DRS_STENCILFUNC, D3DCMP_ALWAYS ); + SetSupportedRenderStateForce( D3DRS_STENCILREF, 0 ); + SetSupportedRenderStateForce( D3DRS_STENCILMASK, 0xFFFFFFFF ); + SetSupportedRenderStateForce( D3DRS_STENCILWRITEMASK, 0xFFFFFFFF ); + SetSupportedRenderStateForce( D3DRS_TEXTUREFACTOR, 0xFFFFFFFF ); + SetSupportedRenderStateForce( D3DRS_WRAP0, 0 ); + SetSupportedRenderStateForce( D3DRS_WRAP1, 0 ); + SetSupportedRenderStateForce( D3DRS_WRAP2, 0 ); + SetSupportedRenderStateForce( D3DRS_WRAP3, 0 ); + SetSupportedRenderStateForce( D3DRS_WRAP4, 0 ); + SetSupportedRenderStateForce( D3DRS_WRAP5, 0 ); + SetSupportedRenderStateForce( D3DRS_WRAP6, 0 ); + SetSupportedRenderStateForce( D3DRS_WRAP7, 0 ); + SetSupportedRenderStateForce( D3DRS_CLIPPING, TRUE ); + SetSupportedRenderStateForce( D3DRS_LIGHTING, TRUE ); + SetSupportedRenderStateForce( D3DRS_AMBIENT, 0 ); + SetSupportedRenderStateForce( D3DRS_FOGVERTEXMODE, D3DFOG_NONE); + SetSupportedRenderStateForce( D3DRS_COLORVERTEX, TRUE ); + SetSupportedRenderStateForce( D3DRS_LOCALVIEWER, TRUE ); + SetSupportedRenderStateForce( D3DRS_NORMALIZENORMALS, FALSE ); + SetSupportedRenderStateForce( D3DRS_SPECULARMATERIALSOURCE, D3DMCS_COLOR2 ); + SetSupportedRenderStateForce( D3DRS_AMBIENTMATERIALSOURCE, D3DMCS_MATERIAL ); + SetSupportedRenderStateForce( D3DRS_EMISSIVEMATERIALSOURCE, D3DMCS_MATERIAL ); + SetSupportedRenderStateForce( D3DRS_VERTEXBLEND, D3DVBF_DISABLE ); + SetSupportedRenderStateForce( D3DRS_CLIPPLANEENABLE, 0 ); + + // -disable_d3d9_hacks is for debugging. For example, the "CENT" driver hack thing causes the flashlight pass to appear much brighter on NVidia drivers. + if ( IsPC() && !IsOpenGL() && !CommandLine()->CheckParm( "-disable_d3d9_hacks" ) ) + { + if ( g_pHardwareConfig->Caps().m_bNeedsATICentroidHack && ( g_pHardwareConfig->Caps().m_VendorID == VENDORID_ATI ) ) + { + SetSupportedRenderStateForce( D3DRS_POINTSIZE, MAKEFOURCC( 'C', 'E', 'N', 'T' ) ); + } + + if( g_pHardwareConfig->Caps().m_bDisableShaderOptimizations ) + { + SetSupportedRenderStateForce( D3DRS_ADAPTIVETESS_Y, MAKEFOURCC( 'C', 'O', 'P', 'M' ) ); + } + } + SetSupportedRenderStateForce( D3DRS_POINTSIZE, dOne ); + SetSupportedRenderStateForce( D3DRS_POINTSIZE_MIN, dOne ); + SetSupportedRenderStateForce( D3DRS_POINTSPRITEENABLE, FALSE ); + SetSupportedRenderStateForce( D3DRS_POINTSCALEENABLE, FALSE ); + SetSupportedRenderStateForce( D3DRS_POINTSCALE_A, dOne ); + SetSupportedRenderStateForce( D3DRS_POINTSCALE_B, dZero ); + SetSupportedRenderStateForce( D3DRS_POINTSCALE_C, dZero ); + SetSupportedRenderStateForce( D3DRS_MULTISAMPLEANTIALIAS, TRUE ); + SetSupportedRenderStateForce( D3DRS_MULTISAMPLEMASK, 0xFFFFFFFF ); + SetSupportedRenderStateForce( D3DRS_PATCHEDGESTYLE, D3DPATCHEDGE_DISCRETE ); + SetSupportedRenderStateForce( D3DRS_DEBUGMONITORTOKEN, D3DDMT_ENABLE ); + float sixtyFour = 64.0f; + SetSupportedRenderStateForce( D3DRS_POINTSIZE_MAX, *((DWORD*)(&sixtyFour))); + SetSupportedRenderStateForce( D3DRS_INDEXEDVERTEXBLENDENABLE, FALSE ); + SetSupportedRenderStateForce( D3DRS_TWEENFACTOR, dZero ); + SetSupportedRenderStateForce( D3DRS_POSITIONDEGREE, D3DDEGREE_CUBIC ); + SetSupportedRenderStateForce( D3DRS_NORMALDEGREE, D3DDEGREE_LINEAR ); + SetSupportedRenderStateForce( D3DRS_SCISSORTESTENABLE, FALSE); + SetSupportedRenderStateForce( D3DRS_SLOPESCALEDEPTHBIAS, dZero ); + SetSupportedRenderStateForce( D3DRS_ANTIALIASEDLINEENABLE, FALSE ); + SetSupportedRenderStateForce( D3DRS_MINTESSELLATIONLEVEL, dOne ); + SetSupportedRenderStateForce( D3DRS_MAXTESSELLATIONLEVEL, dOne ); + SetSupportedRenderStateForce( D3DRS_ADAPTIVETESS_X, dZero ); + SetSupportedRenderStateForce( D3DRS_ADAPTIVETESS_Y, dZero ); + SetSupportedRenderStateForce( D3DRS_ADAPTIVETESS_Z, dOne ); + SetSupportedRenderStateForce( D3DRS_ADAPTIVETESS_W, dZero ); + SetSupportedRenderStateForce( D3DRS_ENABLEADAPTIVETESSELLATION, FALSE ); + SetSupportedRenderStateForce( D3DRS_TWOSIDEDSTENCILMODE, FALSE ); + SetSupportedRenderStateForce( D3DRS_CCW_STENCILFAIL, D3DSTENCILOP_KEEP ); + SetSupportedRenderStateForce( D3DRS_CCW_STENCILZFAIL, D3DSTENCILOP_KEEP ); + SetSupportedRenderStateForce( D3DRS_CCW_STENCILPASS, D3DSTENCILOP_KEEP ); + SetSupportedRenderStateForce( D3DRS_CCW_STENCILFUNC, D3DCMP_ALWAYS ); + SetSupportedRenderStateForce( D3DRS_COLORWRITEENABLE1, 0x0000000f ); + SetSupportedRenderStateForce( D3DRS_COLORWRITEENABLE2, 0x0000000f ); + SetSupportedRenderStateForce( D3DRS_COLORWRITEENABLE3, 0x0000000f ); + SetSupportedRenderStateForce( D3DRS_BLENDFACTOR, 0xffffffff ); + SetSupportedRenderStateForce( D3DRS_SRGBWRITEENABLE, 0); + SetSupportedRenderStateForce( D3DRS_DEPTHBIAS, dZero ); + SetSupportedRenderStateForce( D3DRS_WRAP8, 0 ); + SetSupportedRenderStateForce( D3DRS_WRAP9, 0 ); + SetSupportedRenderStateForce( D3DRS_WRAP10, 0 ); + SetSupportedRenderStateForce( D3DRS_WRAP11, 0 ); + SetSupportedRenderStateForce( D3DRS_WRAP12, 0 ); + SetSupportedRenderStateForce( D3DRS_WRAP13, 0 ); + SetSupportedRenderStateForce( D3DRS_WRAP14, 0 ); + SetSupportedRenderStateForce( D3DRS_WRAP15, 0 ); + SetSupportedRenderStateForce( D3DRS_BLENDOP, D3DBLENDOP_ADD ); + SetSupportedRenderStateForce( D3DRS_BLENDOPALPHA, D3DBLENDOP_ADD ); + +#if defined( _X360 ) + SetSupportedRenderStateForce( D3DRS_HIZENABLE, D3DHIZ_AUTOMATIC ); + SetSupportedRenderStateForce( D3DRS_HIZWRITEENABLE, D3DHIZ_AUTOMATIC ); + + SetSupportedRenderStateForce( D3DRS_HISTENCILENABLE, FALSE ); + SetSupportedRenderStateForce( D3DRS_HISTENCILWRITEENABLE, FALSE ); + SetSupportedRenderStateForce( D3DRS_HISTENCILFUNC, D3DHSCMP_EQUAL ); + SetSupportedRenderStateForce( D3DRS_HISTENCILREF, 0 ); +#endif +} + +//----------------------------------------------------------------------------- +// Own GPU Resources. Return previous state. +//----------------------------------------------------------------------------- +bool CShaderAPIDx8::OwnGPUResources( bool bEnable ) +{ +#if defined( _X360 ) + if ( m_bGPUOwned == bEnable ) + { + return m_bGPUOwned; + } + + if ( !bEnable ) + { + Dx9Device()->GpuDisownAll(); + } + else + { + // owned GPU constants can be set very fast, and must be in blocks of 4 + // there are 256, but the game only uses 217 (snapped to 220), leaving just enough room for shader literals + COMPILE_TIME_ASSERT( VERTEX_SHADER_MODEL + 3*NUM_MODEL_TRANSFORMS == 217 ); + Dx9Device()->GpuOwnVertexShaderConstantF( 0, AlignValue( VERTEX_SHADER_MODEL + 3*NUM_MODEL_TRANSFORMS, 4 ) ); + // there are 256, but the game only utilizes 32, leaving lots of room for shader literals + Dx9Device()->GpuOwnPixelShaderConstantF( 0, 32 ); + } + + bool bPrevious = m_bGPUOwned; + m_bGPUOwned = bEnable; + + return bPrevious; +#else + return false; +#endif +} + +//----------------------------------------------------------------------------- +// Reset render state (to its initial value) +//----------------------------------------------------------------------------- +void CShaderAPIDx8::ResetRenderState( bool bFullReset ) +{ + LOCK_SHADERAPI(); + RECORD_DEBUG_STRING( "BEGIN ResetRenderState" ); + + if ( !CanDownloadTextures() ) + { + QueueResetRenderState(); + return; + } + + m_bResettingRenderState = true; + + OwnGPUResources( true ); + + ResetDXRenderState(); + + // We're not currently rendering anything + m_nCurrentSnapshot = -1; + + D3DXMatrixIdentity( &m_CachedPolyOffsetProjectionMatrix ); + D3DXMatrixIdentity( &m_CachedFastClipProjectionMatrix ); + D3DXMatrixIdentity( &m_CachedFastClipPolyOffsetProjectionMatrix ); + m_UsingTextureRenderTarget = false; + + m_SceneFogColor[0] = 0; + m_SceneFogColor[1] = 0; + m_SceneFogColor[2] = 0; + m_SceneFogMode = MATERIAL_FOG_NONE; + + // This is state that isn't part of the snapshot per-se, because we + // don't need it when it's actually time to render. This just helps us + // to construct the shadow state. + m_DynamicState.m_ClearColor = D3DCOLOR_XRGB(0,0,0); + + if ( bFullReset ) + { + InitVertexAndPixelShaders(); + } + else + { + // just need to dirty the dynamic state, desired state gets copied into below + Q_memset( m_DynamicState.m_pVectorPixelShaderConstant, 0, g_pHardwareConfig->Caps().m_NumPixelShaderConstants * sizeof( Vector4D ) ); + Q_memset( m_DynamicState.m_pBooleanPixelShaderConstant, 0, g_pHardwareConfig->Caps().m_NumBooleanPixelShaderConstants * sizeof( BOOL ) ); + Q_memset( m_DynamicState.m_pIntegerPixelShaderConstant, 0, g_pHardwareConfig->Caps().m_NumIntegerPixelShaderConstants * sizeof( IntVector4D ) ); + + Q_memset( m_DynamicState.m_pVectorVertexShaderConstant, 0, g_pHardwareConfig->Caps().m_NumVertexShaderConstants * sizeof( Vector4D ) ); + Q_memset( m_DynamicState.m_pBooleanVertexShaderConstant, 0, g_pHardwareConfig->Caps().m_NumBooleanVertexShaderConstants * sizeof( BOOL ) ); + Q_memset( m_DynamicState.m_pIntegerVertexShaderConstant, 0, g_pHardwareConfig->Caps().m_NumIntegerVertexShaderConstants * sizeof( IntVector4D ) ); + + SetStandardVertexShaderConstants( OVERBRIGHT ); + } + + //Set the default compressed depth range written to dest alpha. Only need to compress it for 8bit alpha to get a useful gradient. + m_DynamicState.m_DestAlphaDepthRange = (g_pHardwareConfig->GetHDRType() == HDR_TYPE_FLOAT) ? 8192.0f : 192.0f; + + m_CachedAmbientLightCube = STATE_CHANGED; + + // Set the constant color + m_DynamicState.m_ConstantColor = 0xFFFFFFFF; + Color4ub( 255, 255, 255, 255 ); + + // Ambient light color + m_DynamicState.m_Ambient = 0; + SetSupportedRenderState( D3DRS_AMBIENT, m_DynamicState.m_Ambient ); + + // Fog + m_VertexShaderFogParams[0] = m_VertexShaderFogParams[1] = 0.0f; + m_WorldSpaceCameraPositon.Init( 0, 0, 0.01f, 0 ); // Don't let z be zero, as some pixel shaders will divide by this + m_DynamicState.m_FogColor = 0xFFFFFFFF; + m_DynamicState.m_PixelFogColor[0] = m_DynamicState.m_PixelFogColor[1] = + m_DynamicState.m_PixelFogColor[2] = m_DynamicState.m_PixelFogColor[3] = 0.0f; + m_DynamicState.m_bFogGammaCorrectionDisabled = false; + m_DynamicState.m_FogEnable = false; + m_DynamicState.m_SceneFog = MATERIAL_FOG_NONE; + m_DynamicState.m_FogMode = D3DFOG_NONE; + m_DynamicState.m_FogStart = -1; + m_DynamicState.m_FogEnd = -1; + m_DynamicState.m_FogMaxDensity = -1.0f; + m_DynamicState.m_FogZ = 0.0f; + + SetSupportedRenderState( D3DRS_FOGCOLOR, m_DynamicState.m_FogColor ); + SetSupportedRenderState( D3DRS_FOGENABLE, m_DynamicState.m_FogEnable ); + SetSupportedRenderState( D3DRS_FOGTABLEMODE, D3DFOG_NONE ); + SetSupportedRenderState( D3DRS_FOGVERTEXMODE, D3DFOG_NONE ); + SetSupportedRenderState( D3DRS_RANGEFOGENABLE, false ); + + FogStart( 0 ); + FogEnd( 0 ); + FogMaxDensity( 1.0f ); + + m_DynamicState.m_bSRGBWritesEnabled = false; + + // Set the cull mode + m_DynamicState.m_bCullEnabled = true; + m_DynamicState.m_CullMode = D3DCULL_CCW; + m_DynamicState.m_DesiredCullMode = D3DCULL_CCW; + SetRenderState( D3DRS_CULLMODE, D3DCULL_CCW ); + + // No shade mode yet + m_DynamicState.m_ShadeMode = (D3DSHADEMODE)-1; + ShadeMode( SHADER_SMOOTH ); + + m_DynamicState.m_bHWMorphingEnabled = false; + + // Skinning... + m_DynamicState.m_NumBones = 0; + m_DynamicState.m_VertexBlend = (D3DVERTEXBLENDFLAGS)-1; + SetSupportedRenderState( D3DRS_VERTEXBLEND, D3DVBF_DISABLE ); + SetSupportedRenderState( D3DRS_INDEXEDVERTEXBLENDENABLE, FALSE ); + + // No normal normalization + m_DynamicState.m_NormalizeNormals = false; + SetSupportedRenderState( D3DRS_NORMALIZENORMALS, m_DynamicState.m_NormalizeNormals ); + + bool bAntialiasing = ( m_PresentParameters.MultiSampleType != D3DMULTISAMPLE_NONE ); + if ( g_pHardwareConfig->GetHDRType() == HDR_TYPE_FLOAT ) + { + bAntialiasing = false; + } + SetRenderState( D3DRS_MULTISAMPLEANTIALIAS, bAntialiasing ); + + // Anisotropic filtering is disabled by default + if ( bFullReset ) + { + SetAnisotropicLevel( 1 ); + } + + int i; + for ( i = 0; i < g_pHardwareConfig->ActualCaps().m_NumTextureStages; ++i ) + { + TextureStage(i).m_TextureTransformFlags = D3DTTFF_DISABLE; + TextureStage(i).m_BumpEnvMat00 = 1.0f; + TextureStage(i).m_BumpEnvMat01 = 0.0f; + TextureStage(i).m_BumpEnvMat10 = 0.0f; + TextureStage(i).m_BumpEnvMat11 = 1.0f; + + SetTextureStageState( i, D3DTSS_TEXTURETRANSFORMFLAGS, TextureStage(i).m_TextureTransformFlags ); + SetTextureStageState( i, D3DTSS_BUMPENVMAT00, *( ( LPDWORD ) (&TextureStage(i).m_BumpEnvMat00) ) ); + SetTextureStageState( i, D3DTSS_BUMPENVMAT01, *( ( LPDWORD ) (&TextureStage(i).m_BumpEnvMat01) ) ); + SetTextureStageState( i, D3DTSS_BUMPENVMAT10, *( ( LPDWORD ) (&TextureStage(i).m_BumpEnvMat10) ) ); + SetTextureStageState( i, D3DTSS_BUMPENVMAT11, *( ( LPDWORD ) (&TextureStage(i).m_BumpEnvMat11) ) ); + } + + for ( i = 0; i < g_pHardwareConfig->ActualCaps().m_NumSamplers; ++i ) + { + SamplerState(i).m_BoundTexture = INVALID_SHADERAPI_TEXTURE_HANDLE; + SamplerState(i).m_UTexWrap = D3DTADDRESS_WRAP; + SamplerState(i).m_VTexWrap = D3DTADDRESS_WRAP; + SamplerState(i).m_WTexWrap = D3DTADDRESS_WRAP; + SamplerState(i).m_MagFilter = D3DTEXF_POINT; + SamplerState(i).m_MinFilter = D3DTEXF_POINT; + SamplerState(i).m_MipFilter = D3DTEXF_NONE; + SamplerState(i).m_FinestMipmapLevel = 0; + SamplerState(i).m_LodBias = 0.0f; + SamplerState(i).m_TextureEnable = false; + SamplerState(i).m_SRGBReadEnable = false; + + // Just some initial state... + Dx9Device()->SetTexture( i, 0 ); + + SetSamplerState( i, D3DSAMP_ADDRESSU, SamplerState(i).m_UTexWrap ); + SetSamplerState( i, D3DSAMP_ADDRESSV, SamplerState(i).m_VTexWrap ); + SetSamplerState( i, D3DSAMP_ADDRESSW, SamplerState(i).m_WTexWrap ); + SetSamplerState( i, D3DSAMP_MINFILTER, SamplerState(i).m_MinFilter ); + SetSamplerState( i, D3DSAMP_MAGFILTER, SamplerState(i).m_MagFilter ); + SetSamplerState( i, D3DSAMP_MIPFILTER, SamplerState(i).m_MipFilter ); + SetSamplerState( i, D3DSAMP_MAXMIPLEVEL, SamplerState(i).m_FinestMipmapLevel ); + SetSamplerState( i, D3DSAMP_MIPMAPLODBIAS, SamplerState(i).m_LodBias ); + + SetSamplerState( i, D3DSAMP_BORDERCOLOR, RGB( 0,0,0 ) ); + } + + // FIXME!!!!! : This barfs with the debug runtime on 6800. + for( i = 0; i < g_pHardwareConfig->ActualCaps().m_nVertexTextureCount; i++ ) + { + m_DynamicState.m_VertexTextureState[i].m_BoundTexture = INVALID_SHADERAPI_TEXTURE_HANDLE; + Dx9Device()->SetTexture( D3DVERTEXTEXTURESAMPLER0 + i, NULL ); + + m_DynamicState.m_VertexTextureState[i].m_UTexWrap = D3DTADDRESS_CLAMP; + m_DynamicState.m_VertexTextureState[i].m_VTexWrap = D3DTADDRESS_CLAMP; +// m_DynamicState.m_VertexTextureState[i].m_WTexWrap = D3DTADDRESS_CLAMP; + m_DynamicState.m_VertexTextureState[i].m_MinFilter = D3DTEXF_POINT; + m_DynamicState.m_VertexTextureState[i].m_MagFilter = D3DTEXF_POINT; + m_DynamicState.m_VertexTextureState[i].m_MipFilter = D3DTEXF_POINT; + SetSamplerState( D3DVERTEXTEXTURESAMPLER0 + i, D3DSAMP_ADDRESSU, m_DynamicState.m_VertexTextureState[i].m_UTexWrap ); + SetSamplerState( D3DVERTEXTEXTURESAMPLER0 + i, D3DSAMP_ADDRESSV, m_DynamicState.m_VertexTextureState[i].m_VTexWrap ); +// SetSamplerState( D3DVERTEXTEXTURESAMPLER0 + i, D3DSAMP_ADDRESSW, m_DynamicState.m_VertexTextureState[i].m_WTexWrap ); + SetSamplerState( D3DVERTEXTEXTURESAMPLER0 + i, D3DSAMP_MINFILTER, m_DynamicState.m_VertexTextureState[i].m_MinFilter ); + SetSamplerState( D3DVERTEXTEXTURESAMPLER0 + i, D3DSAMP_MAGFILTER, m_DynamicState.m_VertexTextureState[i].m_MagFilter ); + SetSamplerState( D3DVERTEXTEXTURESAMPLER0 + i, D3DSAMP_MIPFILTER, m_DynamicState.m_VertexTextureState[i].m_MipFilter ); + } + + m_DynamicState.m_NumLights = 0; + for ( i = 0; i < MAX_NUM_LIGHTS; ++i) + { + m_DynamicState.m_LightEnable[i] = false; + m_DynamicState.m_LightChanged[i] = STATE_CHANGED; + m_DynamicState.m_LightEnableChanged[i] = STATE_CHANGED; + } + + for ( i = 0; i < NUM_MATRIX_MODES; ++i) + { + // By setting this to *not* be identity, we force an update... + m_DynamicState.m_TransformType[i] = TRANSFORM_IS_GENERAL; + m_DynamicState.m_TransformChanged[i] = STATE_CHANGED; + } + + // set the board state to match the default state + m_TransitionTable.UseDefaultState(); + + // Set the default render state + SetDefaultState(); + + // Constant for all time + SetSupportedRenderState( D3DRS_CLIPPING, TRUE ); + SetSupportedRenderState( D3DRS_LOCALVIEWER, TRUE ); + SetSupportedRenderState( D3DRS_POINTSCALEENABLE, FALSE ); + SetSupportedRenderState( D3DRS_DIFFUSEMATERIALSOURCE, D3DMCS_MATERIAL ); + SetSupportedRenderState( D3DRS_SPECULARMATERIALSOURCE, D3DMCS_COLOR2 ); + SetSupportedRenderState( D3DRS_AMBIENTMATERIALSOURCE, D3DMCS_MATERIAL ); + SetSupportedRenderState( D3DRS_EMISSIVEMATERIALSOURCE, D3DMCS_MATERIAL ); + SetSupportedRenderState( D3DRS_COLORVERTEX, TRUE ); // This defaults to true anyways. . . + + // Set a default identity material. + SetDefaultMaterial(); + +#if 0 + float fBias = -1.0f; + SetTextureStageState( 0, D3DTSS_MIPMAPLODBIAS, *( ( LPDWORD ) (&fBias) ) ); + SetTextureStageState( 1, D3DTSS_MIPMAPLODBIAS, *( ( LPDWORD ) (&fBias) ) ); + SetTextureStageState( 2, D3DTSS_MIPMAPLODBIAS, *( ( LPDWORD ) (&fBias) ) ); + SetTextureStageState( 3, D3DTSS_MIPMAPLODBIAS, *( ( LPDWORD ) (&fBias) ) ); +#endif + + if ( bFullReset ) + { + // Set the modelview matrix to identity too + for ( i = 0; i < NUM_MODEL_TRANSFORMS; ++i ) + { + SetIdentityMatrix( m_boneMatrix[i] ); + } + MatrixMode( MATERIAL_VIEW ); + LoadIdentity(); + MatrixMode( MATERIAL_PROJECTION ); + LoadIdentity(); + } + +#ifdef _X360 + m_DynamicState.m_bBuffer2Frames = m_bBuffer2FramesAhead; + SetRenderState( D3DRS_BUFFER2FRAMES, m_DynamicState.m_bBuffer2Frames ); +#endif + + m_DynamicState.m_Viewport.X = m_DynamicState.m_Viewport.Y = + m_DynamicState.m_Viewport.Width = m_DynamicState.m_Viewport.Height = 0xFFFFFFFF; + m_DynamicState.m_Viewport.MinZ = m_DynamicState.m_Viewport.MaxZ = 0.0; + + // Be sure scissoring is off + m_DynamicState.m_RenderState[D3DRS_SCISSORTESTENABLE] = FALSE; + SetRenderState( D3DRS_SCISSORTESTENABLE, FALSE ); + m_DynamicState.m_ScissorRect.left = -1; + m_DynamicState.m_ScissorRect.top = -1; + m_DynamicState.m_ScissorRect.right = -1; + m_DynamicState.m_ScissorRect.bottom = -1; + + //SetHeightClipMode( MATERIAL_HEIGHTCLIPMODE_DISABLE ); + EnableFastClip( false ); + float fFakePlane[4]; + unsigned int iFakePlaneVal = 0xFFFFFFFF; + fFakePlane[0] = fFakePlane[1] = fFakePlane[2] = fFakePlane[3] = *((float *)&iFakePlaneVal); + SetFastClipPlane( fFakePlane ); //doing this to better wire up plane change detection + + float zero[4] = { 0.0f, 0.0f, 0.0f, 0.0f }; + + // Make sure that our state is dirty. + m_DynamicState.m_UserClipPlaneEnabled = 0; + m_DynamicState.m_UserClipPlaneChanged = 0; + m_DynamicState.m_UserClipLastUpdatedUsingFixedFunction = false; + for( i = 0; i < g_pHardwareConfig->MaxUserClipPlanes(); i++ ) + { + // Make sure that our state is dirty. + m_DynamicState.m_UserClipPlaneWorld[i][0] = -1.0f; + m_DynamicState.m_UserClipPlaneProj[i][0] = -9999.0f; + m_DynamicState.m_UserClipPlaneEnabled |= ( 1 << i ); + SetClipPlane( i, zero ); + EnableClipPlane( i, false ); + Assert( m_DynamicState.m_UserClipPlaneEnabled == 0 ); + } + Assert( m_DynamicState.m_UserClipPlaneChanged == ((1 << g_pHardwareConfig->MaxUserClipPlanes()) - 1) ); + + m_DynamicState.m_FastClipEnabled = false; + m_DynamicState.m_bFastClipPlaneChanged = true; + + // User clip override + m_DynamicState.m_bUserClipTransformOverride = false; + D3DXMatrixIdentity( &m_DynamicState.m_UserClipTransform ); + + // Viewport defaults to the window size + RECT windowRect; +#if !defined( DX_TO_GL_ABSTRACTION ) + GetClientRect( (HWND)m_hWnd, &windowRect ); +#else + toglGetClientRect( (VD3DHWND)m_hWnd, &windowRect ); +#endif + + ShaderViewport_t viewport; + viewport.Init( windowRect.left, windowRect.top, + windowRect.right - windowRect.left, windowRect.bottom - windowRect.top ); + SetViewports( 1, &viewport ); + + // No render mesh + m_pRenderMesh = 0; + + // Reset cached vertex decl + m_DynamicState.m_pVertexDecl = NULL; + + // Reset the render target to be the normal backbuffer + if ( IsX360() ) + { + m_hCachedRenderTarget = INVALID_SHADERAPI_TEXTURE_HANDLE; + m_bUsingSRGBRenderTarget = false; + } + AcquireInternalRenderTargets(); + SetRenderTarget(); + + // Maintain vertex + pixel shader constant buffers + Vector4D *pVectorPixelShaderConstants = m_DesiredState.m_pVectorPixelShaderConstant; + int *pBooleanPixelShaderConstants = m_DesiredState.m_pBooleanPixelShaderConstant; + IntVector4D *pIntegerPixelShaderConstants = m_DesiredState.m_pIntegerPixelShaderConstant; + Vector4D *pVectorVertexShaderConstants = m_DesiredState.m_pVectorVertexShaderConstant; + int *pBooleanVertexShaderConstants = m_DesiredState.m_pBooleanVertexShaderConstant; + IntVector4D *pIntegerVertexShaderConstants = m_DesiredState.m_pIntegerVertexShaderConstant; + m_DesiredState = m_DynamicState; + m_DesiredState.m_pVectorPixelShaderConstant = pVectorPixelShaderConstants; + m_DesiredState.m_pBooleanPixelShaderConstant = pBooleanPixelShaderConstants; + m_DesiredState.m_pIntegerPixelShaderConstant = pIntegerPixelShaderConstants; + m_DesiredState.m_pVectorVertexShaderConstant = pVectorVertexShaderConstants; + m_DesiredState.m_pBooleanVertexShaderConstant = pBooleanVertexShaderConstants; + m_DesiredState.m_pIntegerVertexShaderConstant = pIntegerVertexShaderConstants; + if ( g_pHardwareConfig->Caps().m_SupportsPixelShaders ) + { + if ( !bFullReset ) + { + //Full resets init the values to defaults. Normal resets just leave them dirty. + if( g_pHardwareConfig->Caps().m_NumVertexShaderConstants != 0 ) + SetVertexShaderConstant( 0, m_DesiredState.m_pVectorVertexShaderConstant[0].Base(), IsX360() ? 217 : g_pHardwareConfig->Caps().m_NumVertexShaderConstants, true ); //217 on X360 to play nice with fast blatting code + + if( g_pHardwareConfig->Caps().m_NumIntegerVertexShaderConstants != 0 ) + SetIntegerVertexShaderConstant( 0, (int *)m_DesiredState.m_pIntegerVertexShaderConstant, g_pHardwareConfig->Caps().m_NumIntegerVertexShaderConstants, true ); + + if( g_pHardwareConfig->Caps().m_NumBooleanVertexShaderConstants != 0 ) + SetBooleanVertexShaderConstant( 0, m_DesiredState.m_pBooleanVertexShaderConstant, g_pHardwareConfig->Caps().m_NumBooleanVertexShaderConstants, true ); + + + if( g_pHardwareConfig->Caps().m_NumPixelShaderConstants != 0 ) + SetPixelShaderConstant( 0, m_DesiredState.m_pVectorPixelShaderConstant[0].Base(), g_pHardwareConfig->Caps().m_NumPixelShaderConstants, true ); + + if( g_pHardwareConfig->Caps().m_NumIntegerPixelShaderConstants != 0 ) + SetIntegerPixelShaderConstant( 0, (int *)m_DesiredState.m_pIntegerPixelShaderConstant, g_pHardwareConfig->Caps().m_NumIntegerPixelShaderConstants, true ); + + if( g_pHardwareConfig->Caps().m_NumBooleanPixelShaderConstants != 0 ) + SetBooleanPixelShaderConstant( 0, m_DesiredState.m_pBooleanPixelShaderConstant, g_pHardwareConfig->Caps().m_NumBooleanPixelShaderConstants, true ); + } + } + + RECORD_DEBUG_STRING( "END ResetRenderState" ); + + m_bResettingRenderState = false; +} + +//----------------------------------------------------------------------------- +// Sets the default render state +//----------------------------------------------------------------------------- +void CShaderAPIDx8::SetDefaultState() +{ + LOCK_SHADERAPI(); + + // NOTE: This used to be in the material system, but I want to avoid all the per pass/batch + // virtual function calls. + int numTextureStages = g_pHardwareConfig->GetTextureStageCount(); + + // FIXME: This is a brutal hack. We only need to load these transforms for fixed-function + // hardware. Cap the max here to 4. + if ( IsPC() ) + { + numTextureStages = min( numTextureStages, 4 ); + int i; + for( i = 0; i < numTextureStages; i++ ) + { + CShaderAPIDx8::DisableTextureTransform( (TextureStage_t)i ); + CShaderAPIDx8::MatrixMode( (MaterialMatrixMode_t)(MATERIAL_TEXTURE0 + i) ); + CShaderAPIDx8::LoadIdentity( ); + } + } + CShaderAPIDx8::MatrixMode( MATERIAL_MODEL ); + + CShaderAPIDx8::Color4ub( 255, 255, 255, 255 ); + CShaderAPIDx8::ShadeMode( SHADER_SMOOTH ); + CShaderAPIDx8::SetVertexShaderIndex( ); + CShaderAPIDx8::SetPixelShaderIndex( ); + + MeshMgr()->MarkUnusedVertexFields( 0, 0, NULL ); +} + + + +//----------------------------------------------------------------------------- +// +// Methods related to vertex format +// +//----------------------------------------------------------------------------- + + +//----------------------------------------------------------------------------- +// Sets the vertex +//----------------------------------------------------------------------------- +inline void CShaderAPIDx8::SetVertexDecl( VertexFormat_t vertexFormat, bool bHasColorMesh, bool bUsingFlex, bool bUsingMorph ) +{ + VPROF("CShaderAPIDx8::SetVertexDecl"); + IDirect3DVertexDeclaration9 *pDecl = FindOrCreateVertexDecl( vertexFormat, bHasColorMesh, bUsingFlex, bUsingMorph ); + Assert( pDecl ); + + if ( ( pDecl != m_DynamicState.m_pVertexDecl ) && pDecl ) + { + Dx9Device()->SetVertexDeclaration( pDecl ); + m_DynamicState.m_pVertexDecl = pDecl; + } +} + + + +//----------------------------------------------------------------------------- +// +// Methods related to vertex buffers +// +//----------------------------------------------------------------------------- + +IMesh *CShaderAPIDx8::GetFlexMesh() +{ + LOCK_SHADERAPI(); + return MeshMgr()->GetFlexMesh(); +} + +//----------------------------------------------------------------------------- +// Gets the dynamic mesh +//----------------------------------------------------------------------------- +IMesh* CShaderAPIDx8::GetDynamicMesh( IMaterial* pMaterial, int nHWSkinBoneCount, bool buffered, + IMesh* pVertexOverride, IMesh* pIndexOverride ) +{ + Assert( (pMaterial == NULL) || ((IMaterialInternal *)pMaterial)->IsRealTimeVersion() ); + + LOCK_SHADERAPI(); + return MeshMgr()->GetDynamicMesh( pMaterial, 0, nHWSkinBoneCount, buffered, pVertexOverride, pIndexOverride ); +} + +IMesh* CShaderAPIDx8::GetDynamicMeshEx( IMaterial* pMaterial, VertexFormat_t vertexFormat, int nHWSkinBoneCount, + bool bBuffered, IMesh* pVertexOverride, IMesh* pIndexOverride ) +{ + Assert( (pMaterial == NULL) || ((IMaterialInternal *)pMaterial)->IsRealTimeVersion() ); + + LOCK_SHADERAPI(); + return MeshMgr()->GetDynamicMesh( pMaterial, vertexFormat, nHWSkinBoneCount, bBuffered, pVertexOverride, pIndexOverride ); +} + +//----------------------------------------------------------------------------- +// Returns the number of vertices we can render using the dynamic mesh +//----------------------------------------------------------------------------- +void CShaderAPIDx8::GetMaxToRender( IMesh *pMesh, bool bMaxUntilFlush, int *pMaxVerts, int *pMaxIndices ) +{ + LOCK_SHADERAPI(); + MeshMgr()->GetMaxToRender( pMesh, bMaxUntilFlush, pMaxVerts, pMaxIndices ); +} + +int CShaderAPIDx8::GetMaxVerticesToRender( IMaterial *pMaterial ) +{ + pMaterial = ((IMaterialInternal *)pMaterial)->GetRealTimeVersion(); //always work with the realtime version internally + + LOCK_SHADERAPI(); + return MeshMgr()->GetMaxVerticesToRender( pMaterial ); +} + +int CShaderAPIDx8::GetMaxIndicesToRender( ) +{ + LOCK_SHADERAPI(); + return MeshMgr()->GetMaxIndicesToRender( ); +} + +void CShaderAPIDx8::MarkUnusedVertexFields( unsigned int nFlags, int nTexCoordCount, bool *pUnusedTexCoords ) +{ + LOCK_SHADERAPI(); + MeshMgr()->MarkUnusedVertexFields( nFlags, nTexCoordCount, pUnusedTexCoords ); +} + +//----------------------------------------------------------------------------- +// Draws the mesh +//----------------------------------------------------------------------------- +void CShaderAPIDx8::DrawMesh( CMeshBase *pMesh ) +{ + VPROF("CShaderAPIDx8::DrawMesh"); + if ( ShaderUtil()->GetConfig().m_bSuppressRendering ) + return; + +#if defined( PIX_INSTRUMENTATION ) || defined( NVPERFHUD ) + PIXifyName( s_pPIXMaterialName, sizeof( s_pPIXMaterialName ), m_pMaterial->GetName() ); + BeginPIXEvent( PIX_VALVE_ORANGE, s_pPIXMaterialName ); +#endif + + m_pRenderMesh = pMesh; + VertexFormat_t vertexFormat = m_pRenderMesh->GetVertexFormat(); + SetVertexDecl( vertexFormat, m_pRenderMesh->HasColorMesh(), m_pRenderMesh->HasFlexMesh(), m_pMaterial->IsUsingVertexID() ); + CommitStateChanges(); + Assert( m_pRenderMesh && m_pMaterial ); + m_pMaterial->DrawMesh( CompressionType( vertexFormat ) ); + m_pRenderMesh = NULL; + +#if defined( PIX_INSTRUMENTATION ) || defined( NVPERFHUD ) + EndPIXEvent(); +#endif +} + +void CShaderAPIDx8::DrawWithVertexAndIndexBuffers( void ) +{ + VPROF("CShaderAPIDx8::DrawWithVertexAndIndexBuffers"); + if ( ShaderUtil()->GetConfig().m_bSuppressRendering ) + return; + +#if defined( PIX_INSTRUMENTATION ) || defined( NVPERFHUD ) + PIXifyName( s_pPIXMaterialName, sizeof( s_pPIXMaterialName ), m_pMaterial->GetName()); + BeginPIXEvent( PIX_VALVE_ORANGE, s_pPIXMaterialName ); +#endif + +// m_pRenderMesh = pMesh; + // FIXME: need to make this deal with multiple streams, etc. + VertexFormat_t vertexFormat = MeshMgr()->GetCurrentVertexFormat(); + SetVertexDecl( vertexFormat, false /*m_pRenderMesh->HasColorMesh()*/, + false /*m_pRenderMesh->HasFlexMesh()*/, false /*m_pRenderMesh->IsUsingMorphData()*/ ); + CommitStateChanges(); + if ( m_pMaterial ) + { + m_pMaterial->DrawMesh( CompressionType( vertexFormat ) ); + } + else + { + MeshMgr()->RenderPassWithVertexAndIndexBuffers(); + } +// m_pRenderMesh = NULL; + +#if defined( PIX_INSTRUMENTATION ) || defined( NVPERFHUD ) + EndPIXEvent(); +#endif +} + +//----------------------------------------------------------------------------- +// Discards the vertex buffers +//----------------------------------------------------------------------------- +void CShaderAPIDx8::DiscardVertexBuffers() +{ + MeshMgr()->DiscardVertexBuffers(); +} + +void CShaderAPIDx8::ForceHardwareSync_WithManagedTexture() +{ + if ( IsX360() || !m_pFrameSyncTexture ) + return; + + // Set the default state for everything so we don't get more than we ask for here! + SetDefaultState(); + + D3DLOCKED_RECT rect; + + tmZone( TELEMETRY_LEVEL1, TMZF_NONE, "%s", __FUNCTION__ ); + + HRESULT hr = m_pFrameSyncTexture->LockRect( 0, &rect, NULL, 0 ); + if ( SUCCEEDED( hr ) ) + { + // modify.. + unsigned long *pData = (unsigned long*)rect.pBits; + (*pData)++; + + m_pFrameSyncTexture->UnlockRect( 0 ); + + // Now draw something with this texture. + DWORD iStage = 0; + IDirect3DBaseTexture9 *pOldTexture; + hr = Dx9Device()->GetTexture( iStage, &pOldTexture ); + if ( SUCCEEDED( hr ) ) + { + Dx9Device()->SetTexture( iStage, m_pFrameSyncTexture ); + // Remember the old FVF. + DWORD oldFVF; + hr = Dx9Device()->GetFVF( &oldFVF ); + if ( SUCCEEDED( hr ) ) + { + // Set the new FVF. + Dx9Device()->SetFVF( D3DFVF_XYZ ); + // Now, draw the simplest primitive D3D has ever seen. + unsigned short indices[3] = { 0, 1, 2 }; + Vector verts[3] = {vec3_origin, vec3_origin, vec3_origin}; + + tmZone( TELEMETRY_LEVEL1, TMZF_NONE, "DrawIndexedPrimitiveUP" ); + + Dx9Device()->DrawIndexedPrimitiveUP( + D3DPT_TRIANGLELIST, + 0, // Min vertex index + 3, // Num vertices used + 1, // # primitives + indices, // indices + D3DFMT_INDEX16, // index format + verts, // Vertices + sizeof( Vector )// Vertex stride + ); + + Dx9Device()->SetFVF( oldFVF ); + } + Dx9Device()->SetTexture( iStage, pOldTexture ); + } + } + // If this assert fails, then we failed somewhere above. + AssertOnce( SUCCEEDED( hr ) ); +} + +void CShaderAPIDx8::UpdateFrameSyncQuery( int queryIndex, bool bIssue ) +{ + Assert(queryIndex < NUM_FRAME_SYNC_QUERIES); + // wait if already issued + if ( m_bQueryIssued[queryIndex] ) + { + tmZone( TELEMETRY_LEVEL1, TMZF_NONE, "%s", __FUNCTION__ ); + + double flStartTime = Plat_FloatTime(); + BOOL dummyData = 0; + HRESULT hr = S_OK; + // NOTE: This fix helps out motherboards that are a little freaky. + // On such boards, sometimes the driver has to reset itself (an event which takes several seconds) + // and when that happens, the frame sync query object gets lost + for (;;) + { + hr = m_pFrameSyncQueryObject[queryIndex]->GetData( &dummyData, sizeof( dummyData ), D3DGETDATA_FLUSH ); + if ( hr != S_FALSE ) + break; + double flCurrTime = Plat_FloatTime(); + // don't wait more than 200ms (5fps) for these + if ( flCurrTime - flStartTime > 0.200f ) + break; + // Avoid burning a full core while waiting for the query. Spinning can actually harm performance + // because there might be driver threads that are trying to do work that end up starved, and the + // power drawn by the CPU may take away from the power available to the integrated graphics chip. + // A sleep of one millisecond should never be long enough to affect performance, especially since + // this should only trigger when the CPU is already ahead of the GPU. + // On L4D2/TF2 in GL mode this spinning was causing slowdowns. + ThreadSleep( 1 ); + } + m_bQueryIssued[queryIndex] = false; + Assert(hr == S_OK || hr == D3DERR_DEVICELOST); + + if ( hr == D3DERR_DEVICELOST ) + { + MarkDeviceLost( ); + return; + } + } + if ( bIssue ) + { + m_pFrameSyncQueryObject[queryIndex]->Issue( D3DISSUE_END ); + m_bQueryIssued[queryIndex] = true; + } +} + +void CShaderAPIDx8::ForceHardwareSync( void ) +{ + LOCK_SHADERAPI(); + VPROF( "CShaderAPIDx8::ForceHardwareSync" ); + +#ifdef DX_TO_GL_ABSTRACTION + if ( true ) +#else + if ( !mat_frame_sync_enable.GetInt() ) +#endif + return; + + // need to flush the dynamic buffer and make sure the entire image is there + FlushBufferedPrimitives(); + + RECORD_COMMAND( DX8_HARDWARE_SYNC, 0 ); + +#if !defined( _X360 ) + // How do you query dx9 for how many frames behind the hardware is or, alternatively, how do you tell the hardware to never be more than N frames behind? + // 1) The old QueryPendingFrameCount design was removed. It was + // a simple transaction with the driver through the + // GetDriverState, trivial for the drivers to lie. We came up + // with a much better scheme for tracking pending frames where + // the driver can not lie without a possible performance loss: + // use the asynchronous query system with D3DQUERYTYPE_EVENT and + // data size 0. When GetData returns S_OK for the query, you + // know that frame has finished. + if ( mat_frame_sync_force_texture.GetBool() ) + { + ForceHardwareSync_WithManagedTexture(); + } + else if ( m_pFrameSyncQueryObject[0] ) + { + // FIXME: Could install a callback into the materialsystem to do something while waiting for + // the frame to finish (update sound, etc.) + + // Disable VCR mode here or else it'll screw up (and we don't really care if this part plays back in exactly the same amount of time). + VCRSetEnabled( false ); + + m_currentSyncQuery ++; + if ( m_currentSyncQuery >= ARRAYSIZE(m_pFrameSyncQueryObject) ) + { + m_currentSyncQuery = 0; + } + double fStart = Plat_FloatTime(); + int waitIndex = ((m_currentSyncQuery + NUM_FRAME_SYNC_QUERIES) - (NUM_FRAME_SYNC_FRAMES_LATENCY+1)) % NUM_FRAME_SYNC_QUERIES; + UpdateFrameSyncQuery( waitIndex, false ); + UpdateFrameSyncQuery( m_currentSyncQuery, true ); + VCRSetEnabled( true ); + } +#else + DWORD hFence = Dx9Device()->InsertFence(); + Dx9Device()->BlockOnFence( hFence ); +#endif +} + + +//----------------------------------------------------------------------------- +// Needs render state +//----------------------------------------------------------------------------- +void CShaderAPIDx8::QueueResetRenderState() +{ + m_bResetRenderStateNeeded = true; +} + + +//----------------------------------------------------------------------------- +// Use this to begin and end the frame +//----------------------------------------------------------------------------- +void CShaderAPIDx8::BeginFrame() +{ + LOCK_SHADERAPI(); + + if ( m_bResetRenderStateNeeded ) + { + ResetRenderState( false ); + m_bResetRenderStateNeeded = false; + } + +#if ALLOW_SMP_ACCESS + Dx9Device()->SetASyncMode( mat_use_smp.GetInt() != 0 ); +#endif + + ++m_CurrentFrame; + m_nTextureMemoryUsedLastFrame = 0; +} + +void CShaderAPIDx8::EndFrame() +{ + LOCK_SHADERAPI(); + +#if !defined( _X360 ) + MEMCHECK; +#endif + + ExportTextureList(); +} + + +void CShaderAPIDx8::AddBufferToTextureList( const char *pName, D3DSURFACE_DESC &desc ) +{ +// ImageFormat imageFormat; +// imageFormat = ImageLoader::D3DFormatToImageFormat( desc.Format ); +// if( imageFormat < 0 ) +// { +// Assert( 0 ); +// return; +// } + KeyValues *pSubKey = m_pDebugTextureList->CreateNewKey(); + pSubKey->SetString( "Name", pName ); + pSubKey->SetString( "TexGroup", TEXTURE_GROUP_RENDER_TARGET ); + pSubKey->SetInt( "Size", +// ImageLoader::SizeInBytes( imageFormat ) * desc.Width * desc.Height ); + 4 * desc.Width * desc.Height ); + pSubKey->SetString( "Format", "32 bit buffer (hack)" );//ImageLoader::GetName( imageFormat ) ); + pSubKey->SetInt( "Width", desc.Width ); + pSubKey->SetInt( "Height", desc.Height ); + + pSubKey->SetInt( "BindsMax", 1 ); + pSubKey->SetInt( "BindsFrame", 1 ); +} + +void CShaderAPIDx8::ExportTextureList() +{ + if ( !m_bEnableDebugTextureList ) + return; + + if ( !m_pBackBufferSurface || !m_pZBufferSurface ) + // Device vanished... + return; + + m_nDebugDataExportFrame = m_CurrentFrame; + + if ( IsPC() || !IsX360() ) + { + if ( m_pDebugTextureList ) + m_pDebugTextureList->deleteThis(); + + m_pDebugTextureList = new KeyValues( "TextureList" ); + + m_nTextureMemoryUsedTotal = 0; + m_nTextureMemoryUsedPicMip1 = 0; + m_nTextureMemoryUsedPicMip2 = 0; + for ( ShaderAPITextureHandle_t hTexture = m_Textures.Head() ; hTexture != m_Textures.InvalidIndex(); hTexture = m_Textures.Next( hTexture ) ) + { + Texture_t &tex = m_Textures[hTexture]; + + if ( !( tex.m_Flags & Texture_t::IS_ALLOCATED ) ) + continue; + + // Compute total texture memory usage + m_nTextureMemoryUsedTotal += tex.GetMemUsage(); + + // Compute picmip memory usage + { + int numBytes = tex.GetMemUsage(); + + if ( tex.m_NumLevels > 1 ) + { + if ( tex.GetWidth() > 4 || tex.GetHeight() > 4 || tex.GetDepth() > 4 ) + { + int topmipsize = ImageLoader::GetMemRequired( tex.GetWidth(), tex.GetHeight(), tex.GetDepth(), tex.GetImageFormat(), false ); + numBytes -= topmipsize; + + m_nTextureMemoryUsedPicMip1 += numBytes; + + if ( tex.GetWidth() > 8 || tex.GetHeight() > 8 || tex.GetDepth() > 8 ) + { + int othermipsizeRatio = ( ( tex.GetWidth() > 8 ) ? 2 : 1 ) * ( ( tex.GetHeight() > 8 ) ? 2 : 1 ) * ( ( tex.GetDepth() > 8 ) ? 2 : 1 ); + int othermipsize = topmipsize / othermipsizeRatio; + numBytes -= othermipsize; + } + + m_nTextureMemoryUsedPicMip1 += numBytes; + } + else + { + m_nTextureMemoryUsedPicMip1 += numBytes; + m_nTextureMemoryUsedPicMip2 += numBytes; + } + } + else + { + m_nTextureMemoryUsedPicMip1 += numBytes; + m_nTextureMemoryUsedPicMip2 += numBytes; + } + } + + if ( !m_bDebugGetAllTextures && + tex.m_LastBoundFrame != m_CurrentFrame ) + continue; + + if ( tex.m_LastBoundFrame != m_CurrentFrame ) + tex.m_nTimesBoundThisFrame = 0; + + KeyValues *pSubKey = m_pDebugTextureList->CreateNewKey(); + pSubKey->SetString( "Name", tex.m_DebugName.String() ); + pSubKey->SetString( "TexGroup", tex.m_TextureGroupName.String() ); + pSubKey->SetInt( "Size", tex.GetMemUsage() ); + if ( tex.GetCount() > 1 ) + pSubKey->SetInt( "Count", tex.GetCount() ); + pSubKey->SetString( "Format", ImageLoader::GetName( tex.GetImageFormat() ) ); + pSubKey->SetInt( "Width", tex.GetWidth() ); + pSubKey->SetInt( "Height", tex.GetHeight() ); + + pSubKey->SetInt( "BindsMax", tex.m_nTimesBoundMax ); + pSubKey->SetInt( "BindsFrame", tex.m_nTimesBoundThisFrame ); + } + + D3DSURFACE_DESC desc; + m_pBackBufferSurface->GetDesc( &desc ); + AddBufferToTextureList( "BACKBUFFER", desc ); + AddBufferToTextureList( "FRONTBUFFER", desc ); + // ImageFormat imageFormat = ImageLoader::D3DFormatToImageFormat( desc.Format ); + // if( imageFormat >= 0 ) + { + VPROF_INCREMENT_GROUP_COUNTER( "TexGroup_frame_" TEXTURE_GROUP_RENDER_TARGET, + COUNTER_GROUP_TEXTURE_PER_FRAME, + // ImageLoader::SizeInBytes( imageFormat ) * desc.Width * desc.Height ); + 2 * 4 * desc.Width * desc.Height ); // hack (times 2 for front and back buffer) + } + + m_pZBufferSurface->GetDesc( &desc ); + AddBufferToTextureList( "DEPTHBUFFER", desc ); + // imageFormat = ImageLoader::D3DFormatToImageFormat( desc.Format ); + // if( imageFormat >= 0 ) + { + VPROF_INCREMENT_GROUP_COUNTER( "TexGroup_frame_" TEXTURE_GROUP_RENDER_TARGET, + COUNTER_GROUP_TEXTURE_PER_FRAME, + // ImageLoader::SizeInBytes( imageFormat ) * desc.Width * desc.Height ); + 4 * desc.Width * desc.Height ); // hack + } + } + +#if defined( _X360 ) + // toggle to do one shot transmission + m_bEnableDebugTextureList = false; + + int numTextures = m_Textures.Count() + 3; + xTextureList_t* pXTextureList = (xTextureList_t *)_alloca( numTextures * sizeof( xTextureList_t ) ); + memset( pXTextureList, 0, numTextures * sizeof( xTextureList_t ) ); + + numTextures = 0; + for ( ShaderAPITextureHandle_t hTexture = m_Textures.Head() ; hTexture != m_Textures.InvalidIndex(); hTexture = m_Textures.Next( hTexture ) ) + { + Texture_t &tex = m_Textures[hTexture]; + + if ( !m_bDebugGetAllTextures && tex.m_LastBoundFrame != m_CurrentFrame ) + { + continue; + } + if ( !( tex.m_Flags & Texture_t::IS_ALLOCATED ) ) + { + continue; + } + + int refCount; + if ( tex.m_Flags & Texture_t::IS_DEPTH_STENCIL ) + { + // interface forces us to ignore these + refCount = -1; + } + else + { + refCount = GetD3DTextureRefCount( CShaderAPIDx8::GetD3DTexture( hTexture ) ); + } + + pXTextureList[numTextures].pName = tex.m_DebugName.String(); + pXTextureList[numTextures].size = tex.m_SizeBytes * tex.m_NumCopies; + pXTextureList[numTextures].pGroupName = tex.m_TextureGroupName.String(); + pXTextureList[numTextures].pFormatName = D3DFormatName( ImageLoader::ImageFormatToD3DFormat( tex.GetImageFormat() ) ); + pXTextureList[numTextures].width = tex.GetWidth(); + pXTextureList[numTextures].height = tex.GetHeight(); + pXTextureList[numTextures].depth = tex.GetDepth(); + pXTextureList[numTextures].numLevels = tex.m_NumLevels; + pXTextureList[numTextures].binds = tex.m_nTimesBoundThisFrame; + pXTextureList[numTextures].refCount = refCount; + pXTextureList[numTextures].edram = ( tex.m_Flags & Texture_t::IS_RENDER_TARGET_SURFACE ) != 0; + pXTextureList[numTextures].procedural = tex.m_NumCopies > 1; + pXTextureList[numTextures].final = ( tex.m_Flags & Texture_t::IS_FINALIZED ) != 0; + pXTextureList[numTextures].failed = ( tex.m_Flags & Texture_t::IS_FAILED ) != 0; + numTextures++; + } + + // build special entries for implicit surfaces/textures + D3DSURFACE_DESC desc; + m_pBackBufferSurface->GetDesc( &desc ); + int size = ImageLoader::GetMemRequired( + desc.Width, + desc.Height, + 0, + ImageLoader::D3DFormatToImageFormat( desc.Format ), + false ); + pXTextureList[numTextures].pName = "_rt_BackBuffer"; + pXTextureList[numTextures].size = size; + pXTextureList[numTextures].pGroupName = TEXTURE_GROUP_RENDER_TARGET_SURFACE; + pXTextureList[numTextures].pFormatName = D3DFormatName( desc.Format ); + pXTextureList[numTextures].width = desc.Width; + pXTextureList[numTextures].height = desc.Height; + pXTextureList[numTextures].depth = 1; + pXTextureList[numTextures].binds = 1; + pXTextureList[numTextures].refCount = 1; + pXTextureList[numTextures].sRGB = IS_D3DFORMAT_SRGB( desc.Format ); + pXTextureList[numTextures].edram = true; + numTextures++; + + m_pZBufferSurface->GetDesc( &desc ); + pXTextureList[numTextures].pName = "_rt_DepthBuffer"; + pXTextureList[numTextures].size = size; + pXTextureList[numTextures].pGroupName = TEXTURE_GROUP_RENDER_TARGET_SURFACE; + pXTextureList[numTextures].pFormatName = D3DFormatName( desc.Format ); + pXTextureList[numTextures].width = desc.Width; + pXTextureList[numTextures].height = desc.Height; + pXTextureList[numTextures].depth = 1; + pXTextureList[numTextures].binds = 1; + pXTextureList[numTextures].refCount = 1; + pXTextureList[numTextures].sRGB = IS_D3DFORMAT_SRGB( desc.Format ); + pXTextureList[numTextures].edram = true; + numTextures++; + + // front buffer resides in DDR + pXTextureList[numTextures].pName = "_rt_FrontBuffer"; + pXTextureList[numTextures].size = size; + pXTextureList[numTextures].pGroupName = TEXTURE_GROUP_RENDER_TARGET; + pXTextureList[numTextures].pFormatName = D3DFormatName( desc.Format ); + pXTextureList[numTextures].width = desc.Width; + pXTextureList[numTextures].height = desc.Height; + pXTextureList[numTextures].depth = 1; + pXTextureList[numTextures].binds = 1; + pXTextureList[numTextures].refCount = 1; + pXTextureList[numTextures].sRGB = IS_D3DFORMAT_SRGB( desc.Format ); + numTextures++; + + int totalMemory = 0; + for ( int i = 0; i < numTextures; i++ ) + { + if ( pXTextureList[i].edram ) + { + // skip edram based items + continue; + } + totalMemory += pXTextureList[i].size; + } + Msg( "Total D3D Texture Memory: %.2f MB\n", (float)totalMemory/( 1024.0f * 1024.0f ) ); + + // transmit to console + XBX_rTextureList( numTextures, pXTextureList ); +#endif +} + + +//----------------------------------------------------------------------------- +// Releases/reloads resources when other apps want some memory +//----------------------------------------------------------------------------- +void CShaderAPIDx8::ReleaseShaderObjects() +{ + ReleaseInternalRenderTargets(); + EvictManagedResourcesInternal(); + + // FIXME: Move into shaderdevice when textures move over. + +#ifdef _DEBUG + // Helps to find the unreleased textures. + if ( TextureCount() > 0 ) + { + ShaderAPITextureHandle_t hTexture; + for ( hTexture = m_Textures.Head(); hTexture != m_Textures.InvalidIndex(); hTexture = m_Textures.Next( hTexture ) ) + { + if ( GetTexture( hTexture ).m_NumCopies == 1 ) + { + if ( GetTexture( hTexture ).GetTexture() ) + { + Warning( "Didn't correctly clean up texture 0x%8.8x (%s)\n", hTexture, GetTexture( hTexture ).m_DebugName.String() ); + } + } + else + { + for ( int k = GetTexture( hTexture ).m_NumCopies; --k >= 0; ) + { + if ( GetTexture( hTexture ).GetTexture( k ) != 0 ) + { + Warning( "Didn't correctly clean up texture 0x%8.8x (%s)\n", hTexture, GetTexture( hTexture ).m_DebugName.String() ); + break; + } + } + } + } + } +#endif + + Assert( TextureCount() == 0 ); +} + +void CShaderAPIDx8::RestoreShaderObjects() +{ + AcquireInternalRenderTargets(); + SetRenderTarget(); +} + + +//-------------------------------------------------------------------- +// PIX instrumentation routines +// Windows only for now. Turn these on with PIX_INSTRUMENTATION above +//-------------------------------------------------------------------- + +#if 0 // hack versions for OSX to be able to PIX log even when not built debug... +void CShaderAPIDx8::BeginPIXEvent( unsigned long color, const char* szName ) + { + LOCK_SHADERAPI(); + GLMBeginPIXEvent( szName ); // direct call no macro + return; + } + + void CShaderAPIDx8::EndPIXEvent( void ) + { + LOCK_SHADERAPI(); + GLMEndPIXEvent(); // direct call no macro + return; + } + +#else + +#if defined( PIX_INSTRUMENTATION ) || defined( NVPERFHUD ) +ConVar pix_break_on_event( "pix_break_on_event", "" ); +#endif + +void CShaderAPIDx8::BeginPIXEvent( unsigned long color, const char* szName ) +{ +#if ( defined( PIX_INSTRUMENTATION ) || defined( NVPERFHUD ) ) + //LOCK_SHADERAPI(); + + const char *p = pix_break_on_event.GetString(); + if ( p && V_strlen( p ) ) + { + if ( V_stristr( szName, p ) != NULL ) + { + DebuggerBreak(); + } + } + +#if defined ( DX_TO_GL_ABSTRACTION ) + GLMBeginPIXEvent( szName ); + +#if defined( _WIN32 ) + // AMD PerfStudio integration: Call into D3D9.DLL's D3DPERF_BeginEvent() (this gets intercepted by PerfStudio even in GL mode). + if ( g_pShaderDeviceMgrDx8->m_pBeginEvent ) + { + wchar_t wszName[128]; + mbstowcs( wszName, szName, 128 ); + + g_pShaderDeviceMgrDx8->m_pBeginEvent( 0x2F2F2F2F, wszName ); + } +#endif +#elif defined(_X360 ) +#ifndef _DEBUG + char szPIXEventName[32]; + PIXifyName( szPIXEventName, szName ); + PIXBeginNamedEvent( color, szPIXEventName ); +#endif +#else // PC + if ( PIXError() ) + return; + + wchar_t wszName[128]; + mbstowcs( wszName, szName, 128 ); + + // Fire the PIX event, trapping for errors... + if ( D3DPERF_BeginEvent( color, wszName ) < 0 ) + { + Warning( "PIX error Beginning %s event\n", szName ); + IncrementPIXError(); + } +#endif +#endif // #if defined( PIX_INSTRUMENTATION ) +} + +void CShaderAPIDx8::EndPIXEvent( void ) +{ +#if ( defined( PIX_INSTRUMENTATION ) || defined( NVPERFHUD ) ) + //LOCK_SHADERAPI(); + +#if defined ( DX_TO_GL_ABSTRACTION ) + GLMEndPIXEvent(); + +#if defined( _WIN32 ) + // AMD PerfStudio integration: Call into D3D9.DLL's D3DPERF_EndEvent() (this gets intercepted by PerfStudio even in GL mode). + if ( g_pShaderDeviceMgrDx8->m_pEndEvent ) + { + g_pShaderDeviceMgrDx8->m_pEndEvent(); + } +#endif +#elif defined( _X360 ) +#ifndef _DEBUG + PIXEndNamedEvent(); +#endif +#else // PC + if ( PIXError() ) + return; + +#if !defined( NVPERFHUD ) + // Fire the PIX event, trapping for errors... + if ( D3DPERF_EndEvent() < 0 ) + { + Warning("PIX error ending event\n"); + IncrementPIXError(); + } +#endif +#endif +#endif // #if defined( PIX_INSTRUMENTATION ) +} + +#endif + +void CShaderAPIDx8::AdvancePIXFrame() +{ +#if defined( PIX_INSTRUMENTATION ) + // Ping PIX when this bool goes from false to true + if ( r_pix_start.GetBool() && (!m_bPixCapturing) ) + { + StartPIXInstrumentation(); + m_bPixCapturing = true; + } + + // If we want to record frames... + if ( r_pix_recordframes.GetInt() ) + { + if ( m_nPixFrame == 0 ) // First frame to record + { + StartPIXInstrumentation(); + m_nPixFrame++; + } + else if( m_nPixFrame == r_pix_recordframes.GetInt() ) // Last frame to record + { + EndPIXInstrumentation(); + r_pix_recordframes.SetValue(0); + m_nPixFrame = 0; + } + else + { + m_nPixFrame++; // Recording frames... + } + } +#endif +} + +// No begin-end for this...use this to put discrete markers in the PIX stream +void CShaderAPIDx8::SetPIXMarker( unsigned long color, const char* szName ) +{ +#if defined( PIX_INSTRUMENTATION ) + LOCK_SHADERAPI(); + +#if defined( DX_TO_GL_ABSTRACTION ) + if ( g_pShaderDeviceMgrDx8->m_pSetMarker ) + { + wchar_t wszName[128]; + mbstowcs(wszName, szName, 128 ); + g_pShaderDeviceMgrDx8->m_pSetMarker( 0x2F2F2F2F, wszName ); + } +#elif defined( _X360 ) +#ifndef _DEBUG + char szPIXMarkerName[32]; + PIXifyName( szPIXMarkerName, szName ); + PIXSetMarker( color, szPIXMarkerName ); +#endif +#else // PC + if ( PIXError() ) + return; + wchar_t wszName[128]; + mbstowcs(wszName, szName, 128 ); + D3DPERF_SetMarker( color, wszName ); +#endif + +#endif // PIX_INSTRUMENTATION +} + +void CShaderAPIDx8::StartPIXInstrumentation() +{ +#if defined( PIX_INSTRUMENTATION ) + SetPIXMarker( PIX_VALVE_ORANGE, "Valve_PIX_Capture_Start" ); +#endif +} + +void CShaderAPIDx8::EndPIXInstrumentation() +{ +#if defined( PIX_INSTRUMENTATION ) + SetPIXMarker( PIX_VALVE_ORANGE, "Valve_PIX_Capture_End" ); +#endif +} + +void CShaderAPIDx8::IncrementPIXError() +{ +#if defined( PIX_INSTRUMENTATION ) && !defined( NVPERFHUD ) + m_nPIXErrorCount++; + if ( m_nPIXErrorCount >= MAX_PIX_ERRORS ) + { + Warning( "Source engine built with PIX instrumentation, but PIX doesn't seem to have been used to instantiate the game, which is necessary on PC.\n" ); + } +#endif +} + +// Have we already hit several PIX errors? +bool CShaderAPIDx8::PIXError() +{ +#if defined( PIX_INSTRUMENTATION ) && !defined( NVPERFHUD ) + return m_nPIXErrorCount >= MAX_PIX_ERRORS; +#else + return false; +#endif +} + + +//----------------------------------------------------------------------------- +// Check for device lost +//----------------------------------------------------------------------------- +void CShaderAPIDx8::ChangeVideoMode( const ShaderDeviceInfo_t &info ) +{ + if ( IsX360() ) + return; + + LOCK_SHADERAPI(); + + m_PendingVideoModeChangeConfig = info; + m_bPendingVideoModeChange = true; + + if ( info.m_DisplayMode.m_nWidth != 0 && info.m_DisplayMode.m_nHeight != 0 ) + { + m_nWindowWidth = info.m_DisplayMode.m_nWidth; + m_nWindowHeight = info.m_DisplayMode.m_nHeight; + } +} + + +//----------------------------------------------------------------------------- +// Compute fill rate +//----------------------------------------------------------------------------- +void CShaderAPIDx8::ComputeFillRate() +{ + if ( IsX360() ) + { + // not valid + return; + } + + static unsigned char* pBuf = 0; + + int width, height; + GetWindowSize( width, height ); + // Snapshot; look at total # pixels drawn... + if ( !pBuf ) + { + int memSize = ShaderUtil()->GetMemRequired( + width, + height, + 1, + IMAGE_FORMAT_RGB888, + false ) + 4; + + pBuf = (unsigned char*)malloc( memSize ); + } + + ReadPixels( + 0, + 0, + width, + height, + pBuf, + IMAGE_FORMAT_RGB888 ); + + int mask = 0xFF; + int count = 0; + unsigned char* pRead = pBuf; + for (int i = 0; i < height; ++i) + { + for (int j = 0; j < width; ++j) + { + int val = *(int*)pRead; + count += (val & mask); + pRead += 3; + } + } +} + +//----------------------------------------------------------------------------- +// Use this to get the mesh builder that allows us to modify vertex data +//----------------------------------------------------------------------------- +CMeshBuilder* CShaderAPIDx8::GetVertexModifyBuilder() +{ + return &m_ModifyBuilder; +} + +bool CShaderAPIDx8::InFlashlightMode() const +{ + return ShaderUtil()->InFlashlightMode(); +} + +bool CShaderAPIDx8::InEditorMode() const +{ + return ShaderUtil()->InEditorMode(); +} + +//----------------------------------------------------------------------------- +// Gets the bound morph's vertex format; returns 0 if no morph is bound +//----------------------------------------------------------------------------- +MorphFormat_t CShaderAPIDx8::GetBoundMorphFormat() +{ + return ShaderUtil()->GetBoundMorphFormat(); +} + +//----------------------------------------------------------------------------- +// returns the current time in seconds... +//----------------------------------------------------------------------------- +double CShaderAPIDx8::CurrentTime() const +{ + // FIXME: Return game time instead of real time! + // Or eliminate this altogether and put it into a material var + // (this is used by vertex modifiers in shader code at the moment) + return Plat_FloatTime(); +} + + +//----------------------------------------------------------------------------- +// Methods called by the transition table that use dynamic state... +//----------------------------------------------------------------------------- +void CShaderAPIDx8::ApplyZBias( const ShadowState_t& shaderState ) +{ + MaterialSystem_Config_t &config = ShaderUtil()->GetConfig(); + float a = (config.m_SlopeScaleDepthBias_Decal != 0.0f) ? 1.0f / config.m_SlopeScaleDepthBias_Decal : 0.0f; + float b = (config.m_SlopeScaleDepthBias_Normal != 0.0f) ? 1.0f / config.m_SlopeScaleDepthBias_Normal : 0.0f; + float c = (config.m_DepthBias_Decal != 0.0f) ? 1.0f / config.m_DepthBias_Decal : 0.0f; + float d = (config.m_DepthBias_Normal != 0.0f) ? 1.0f / config.m_DepthBias_Normal : 0.0f; + + // FIXME: No longer necessary; may be necessary if you want to use cat 4.3 drivers? + // GR - hack for R200 + bool bPS14Only = g_pHardwareConfig->Caps().m_SupportsPixelShaders_1_4 && !g_pHardwareConfig->Caps().m_SupportsPixelShaders_2_0; + if( ( g_pHardwareConfig->Caps().m_VendorID == 0x1002 ) && bPS14Only ) + { + // Slam to m_SlopeScaleDepthBias_Decal = 0, m_DepthBias_Decal = -4096 + // which empirically is what appears to look good on r200 + // NOTE: Slamming to 0 instead of -1.0 / 4096 because on Cat 4.9, WinXP, 8500, + // this causes the z values to be more than 50 units away from the original z values + + a = 0.0f; + c = -1.0/4096.0; + } + + // bias = (s * D3DRS_SLOPESCALEDEPTHBIAS) + D3DRS_DEPTHBIAS, where s is the maximum depth slope of the triangle being rendered + if ( g_pHardwareConfig->Caps().m_ZBiasAndSlopeScaledDepthBiasSupported ) + { + float fSlopeScaleDepthBias, fDepthBias; + if ( shaderState.m_ZBias == SHADER_POLYOFFSET_DECAL ) + { + fSlopeScaleDepthBias = a; + fDepthBias = c; + } + else if ( shaderState.m_ZBias == SHADER_POLYOFFSET_SHADOW_BIAS ) + { + fSlopeScaleDepthBias = m_fShadowSlopeScaleDepthBias; + fDepthBias = m_fShadowDepthBias; + } + else // assume SHADER_POLYOFFSET_DISABLE + { + fSlopeScaleDepthBias = b; + fDepthBias = d; + } + + if( ReverseDepthOnX360() ) + { + fSlopeScaleDepthBias = -fSlopeScaleDepthBias; + fDepthBias = -fDepthBias; + } + + SetRenderStateConstMacro( this, D3DRS_SLOPESCALEDEPTHBIAS, *((DWORD*) (&fSlopeScaleDepthBias)) ); + SetRenderStateConstMacro( this, D3DRS_DEPTHBIAS, *((DWORD*) (&fDepthBias)) ); + } + else + { + MarkAllUserClipPlanesDirty(); + m_DynamicState.m_TransformChanged[MATERIAL_PROJECTION] |= + STATE_CHANGED_VERTEX_SHADER | STATE_CHANGED_FIXED_FUNCTION; + } +} + +void CShaderAPIDx8::ApplyTextureEnable( const ShadowState_t& state, int nSampler ) +{ + if ( state.m_SamplerState[nSampler].m_TextureEnable == SamplerState(nSampler).m_TextureEnable ) + return; + + if ( state.m_SamplerState[nSampler].m_TextureEnable ) + { + SamplerState( nSampler ).m_TextureEnable = true; + + // Should not be necessary/possible (SetTextureState() calls D3D9/DXAbstract, so the calling thread must already own the device. + //LOCK_SHADERAPI(); + + // Don't do this here!! It ends up giving us extra texture sets. + // We'll Assert in debug mode if you enable a texture stage + // but don't bind a texture. + // see CShaderAPIDx8::RenderPass() for this check. + // NOTE: We aren't doing this optimization quite yet. There are situations + // where you want a texture stage enabled for its texture coordinates, but + // you don't actually bind a texture (texmvspec for example.) + SetTextureState( (Sampler_t)nSampler, SamplerState(nSampler).m_BoundTexture, true ); + } + else + { + SamplerState( nSampler ).m_TextureEnable = false; + SetTextureState( (Sampler_t)nSampler, INVALID_SHADERAPI_TEXTURE_HANDLE ); + } +} + + +//----------------------------------------------------------------------------- +// Used to clear the transition table when we know it's become invalid. +//----------------------------------------------------------------------------- +void CShaderAPIDx8::ClearSnapshots() +{ + LOCK_SHADERAPI(); + FlushBufferedPrimitives(); + m_TransitionTable.Reset(); + InitRenderState(); +} + + +static void KillTranslation( D3DXMATRIX& mat ) +{ + mat[3] = 0.0f; + mat[7] = 0.0f; + mat[11] = 0.0f; + mat[12] = 0.0f; + mat[13] = 0.0f; + mat[14] = 0.0f; + mat[15] = 1.0f; +} + +static void PrintMatrix( const char *name, const D3DXMATRIX& mat ) +{ + int row, col; + char buf[128]; + + Plat_DebugString( name ); + Plat_DebugString( "\n" ); + for( row = 0; row < 4; row++ ) + { + Plat_DebugString( " " ); + for( col = 0; col < 4; col++ ) + { + sprintf( buf, "%f ", ( float )mat( row, col ) ); + Plat_DebugString( buf ); + } + Plat_DebugString( "\n" ); + } + Plat_DebugString( "\n" ); +} + + +//----------------------------------------------------------------------------- +// Gets the vertex format for a particular snapshot id +//----------------------------------------------------------------------------- +VertexFormat_t CShaderAPIDx8::ComputeVertexUsage( int num, StateSnapshot_t* pIds ) const +{ + LOCK_SHADERAPI(); + if (num == 0) + return 0; + + // We don't have to all sorts of crazy stuff if there's only one snapshot + if ( num == 1 ) + { + const ShadowShaderState_t& state = m_TransitionTable.GetSnapshotShader( pIds[0] ); + return state.m_VertexUsage; + } + + Assert( pIds ); + + // Aggregating vertex formats is a little tricky; + // For example, what do we do when two passes want user data? + // Can we assume they are the same? For now, I'm going to + // just print a warning in debug. + + VertexCompressionType_t compression = VERTEX_COMPRESSION_INVALID; + int userDataSize = 0; + int numBones = 0; + int texCoordSize[VERTEX_MAX_TEXTURE_COORDINATES] = { 0, 0, 0, 0, 0, 0, 0, 0 }; + int flags = 0; + + for (int i = num; --i >= 0; ) + { + const ShadowShaderState_t& state = m_TransitionTable.GetSnapshotShader( pIds[i] ); + VertexFormat_t fmt = state.m_VertexUsage; + flags |= VertexFlags(fmt); + + VertexCompressionType_t newCompression = CompressionType( fmt ); + if ( ( compression != newCompression ) && ( compression != VERTEX_COMPRESSION_INVALID ) ) + { + Warning("Encountered a material with two passes that specify different vertex compression types!\n"); + compression = VERTEX_COMPRESSION_NONE; // Be safe, disable compression + } + + int newNumBones = NumBoneWeights(fmt); + if ((numBones != newNumBones) && (newNumBones != 0)) + { + if (numBones != 0) + { + Warning("Encountered a material with two passes that use different numbers of bones!\n"); + } + numBones = newNumBones; + } + + int newUserSize = UserDataSize(fmt); + if ((userDataSize != newUserSize) && (newUserSize != 0)) + { + if (userDataSize != 0) + { + Warning("Encountered a material with two passes that use different user data sizes!\n"); + } + userDataSize = newUserSize; + } + + for ( int j = 0; j < VERTEX_MAX_TEXTURE_COORDINATES; ++j ) + { + int newSize = TexCoordSize( (TextureStage_t)j, fmt ); + if ( ( texCoordSize[j] != newSize ) && ( newSize != 0 ) ) + { + if ( texCoordSize[j] != 0 ) + { + Warning("Encountered a material with two passes that use different texture coord sizes!\n"); + } + if ( texCoordSize[j] < newSize ) + { + texCoordSize[j] = newSize; + } + } + } + } + + return MeshMgr()->ComputeVertexFormat( flags, VERTEX_MAX_TEXTURE_COORDINATES, + texCoordSize, numBones, userDataSize ); +} + +VertexFormat_t CShaderAPIDx8::ComputeVertexFormat( int num, StateSnapshot_t* pIds ) const +{ + LOCK_SHADERAPI(); + VertexFormat_t fmt = ComputeVertexUsage( num, pIds ); + return fmt; +} + + +//----------------------------------------------------------------------------- +// What fields in the morph do we actually use? +//----------------------------------------------------------------------------- +MorphFormat_t CShaderAPIDx8::ComputeMorphFormat( int numSnapshots, StateSnapshot_t* pIds ) const +{ + LOCK_SHADERAPI(); + MorphFormat_t format = 0; + for ( int i = 0; i < numSnapshots; ++i ) + { + MorphFormat_t fmt = m_TransitionTable.GetSnapshotShader( pIds[i] ).m_MorphUsage; + format |= VertexFlags(fmt); + } + return format; +} + +//----------------------------------------------------------------------------- +// Commits a range of vertex shader constants +//----------------------------------------------------------------------------- +static void CommitVertexShaderConstantRange( IDirect3DDevice9 *pDevice, const DynamicState_t &desiredState, + DynamicState_t ¤tState, bool bForce, int nFirstConstant, int nCount ) +{ + if ( IsX360() ) + { + // invalid code path for 360, not coded for 360 GPU contant awareness + Assert( 0 ); + return; + } + + int nFirstCommit = nFirstConstant; + int nCommitCount = 0; + + for ( int i = 0; i < nCount; ++i ) + { + int nVar = nFirstConstant + i; + + bool bDifferentValue = bForce || ( desiredState.m_pVectorVertexShaderConstant[nVar] != currentState.m_pVectorVertexShaderConstant[nVar] ); + if ( !bDifferentValue ) + { + if ( nCommitCount != 0 ) + { + // flush the prior range + pDevice->SetVertexShaderConstantF( nFirstCommit, desiredState.m_pVectorVertexShaderConstant[nFirstCommit].Base(), nCommitCount ); + + memcpy( ¤tState.m_pVectorVertexShaderConstant[nFirstCommit], + &desiredState.m_pVectorVertexShaderConstant[nFirstCommit], nCommitCount * 4 * sizeof(float) ); + } + + // start of new range + nFirstCommit = nVar + 1; + nCommitCount = 0; + } + else + { + ++nCommitCount; + } + } + + if ( nCommitCount != 0 ) + { + // flush range + pDevice->SetVertexShaderConstantF( nFirstCommit, desiredState.m_pVectorVertexShaderConstant[nFirstCommit].Base(), nCommitCount ); + + memcpy( ¤tState.m_pVectorVertexShaderConstant[nFirstCommit], + &desiredState.m_pVectorVertexShaderConstant[nFirstCommit], nCommitCount * 4 * sizeof(float) ); + } +} + + +//----------------------------------------------------------------------------- +// Gets the current buffered state... (debug only) +//----------------------------------------------------------------------------- +void CShaderAPIDx8::GetBufferedState( BufferedState_t& state ) +{ + memcpy( &state.m_Transform[0], &GetTransform(MATERIAL_MODEL), sizeof(D3DXMATRIX) ); + memcpy( &state.m_Transform[1], &GetTransform(MATERIAL_VIEW), sizeof(D3DXMATRIX) ); + memcpy( &state.m_Transform[2], &GetTransform(MATERIAL_PROJECTION), sizeof(D3DXMATRIX) ); + memcpy( &state.m_Viewport, &m_DynamicState.m_Viewport, sizeof(state.m_Viewport) ); + state.m_PixelShader = ShaderManager()->GetCurrentPixelShader(); + state.m_VertexShader = ShaderManager()->GetCurrentVertexShader(); + for (int i = 0; i < g_pHardwareConfig->GetSamplerCount(); ++i) + { + state.m_BoundTexture[i] = m_DynamicState.m_SamplerState[i].m_BoundTexture; + } +} + + +//----------------------------------------------------------------------------- +// constant color methods +//----------------------------------------------------------------------------- + +void CShaderAPIDx8::Color3f( float r, float g, float b ) +{ + unsigned int color = D3DCOLOR_ARGB( 255, (int)(r * 255), + (int)(g * 255), (int)(b * 255) ); + if (color != m_DynamicState.m_ConstantColor) + { + m_DynamicState.m_ConstantColor = color; + SetSupportedRenderState( D3DRS_TEXTUREFACTOR, color ); + } +} + +void CShaderAPIDx8::Color4f( float r, float g, float b, float a ) +{ + unsigned int color = D3DCOLOR_ARGB( (int)(a * 255), (int)(r * 255), + (int)(g * 255), (int)(b * 255) ); + if (color != m_DynamicState.m_ConstantColor) + { + m_DynamicState.m_ConstantColor = color; + SetSupportedRenderState( D3DRS_TEXTUREFACTOR, color ); + } +} + +void CShaderAPIDx8::Color3fv( float const *c ) +{ + Assert( c ); + unsigned int color = D3DCOLOR_ARGB( 255, (int)(c[0] * 255), + (int)(c[1] * 255), (int)(c[2] * 255) ); + if (color != m_DynamicState.m_ConstantColor) + { + m_DynamicState.m_ConstantColor = color; + SetSupportedRenderState( D3DRS_TEXTUREFACTOR, color ); + } +} + +void CShaderAPIDx8::Color4fv( float const *c ) +{ + Assert( c ); + unsigned int color = D3DCOLOR_ARGB( (int)(c[3] * 255), (int)(c[0] * 255), + (int)(c[1] * 255), (int)(c[2] * 255) ); + if (color != m_DynamicState.m_ConstantColor) + { + m_DynamicState.m_ConstantColor = color; + SetSupportedRenderState( D3DRS_TEXTUREFACTOR, color ); + } +} + +void CShaderAPIDx8::Color3ub( unsigned char r, unsigned char g, unsigned char b ) +{ + unsigned int color = D3DCOLOR_ARGB( 255, r, g, b ); + if (color != m_DynamicState.m_ConstantColor) + { + m_DynamicState.m_ConstantColor = color; + SetSupportedRenderState( D3DRS_TEXTUREFACTOR, color ); + } +} + +void CShaderAPIDx8::Color3ubv( unsigned char const* pColor ) +{ + Assert( pColor ); + unsigned int color = D3DCOLOR_ARGB( 255, pColor[0], pColor[1], pColor[2] ); + if (color != m_DynamicState.m_ConstantColor) + { + m_DynamicState.m_ConstantColor = color; + SetSupportedRenderState( D3DRS_TEXTUREFACTOR, color ); + } +} + +void CShaderAPIDx8::Color4ub( unsigned char r, unsigned char g, unsigned char b, unsigned char a ) +{ + unsigned int color = D3DCOLOR_ARGB( a, r, g, b ); + if (color != m_DynamicState.m_ConstantColor) + { + m_DynamicState.m_ConstantColor = color; + SetSupportedRenderState( D3DRS_TEXTUREFACTOR, color ); + } +} + +void CShaderAPIDx8::Color4ubv( unsigned char const* pColor ) +{ + Assert( pColor ); + unsigned int color = D3DCOLOR_ARGB( pColor[3], pColor[0], pColor[1], pColor[2] ); + if (color != m_DynamicState.m_ConstantColor) + { + m_DynamicState.m_ConstantColor = color; + SetSupportedRenderState( D3DRS_TEXTUREFACTOR, color ); + } +} + + +//----------------------------------------------------------------------------- +// The shade mode +//----------------------------------------------------------------------------- +void CShaderAPIDx8::ShadeMode( ShaderShadeMode_t mode ) +{ + LOCK_SHADERAPI(); + D3DSHADEMODE shadeMode = (mode == SHADER_FLAT) ? D3DSHADE_FLAT : D3DSHADE_GOURAUD; + if (m_DynamicState.m_ShadeMode != shadeMode) + { + m_DynamicState.m_ShadeMode = shadeMode; + SetRenderStateConstMacro( this, D3DRS_SHADEMODE, shadeMode ); + } +} + + +//----------------------------------------------------------------------------- +// Buffering 2 frames ahead +//----------------------------------------------------------------------------- +void CShaderAPIDx8::EnableBuffer2FramesAhead( bool bEnable ) +{ +#ifdef _X360 + m_bBuffer2FramesAhead = bEnable; + if ( bEnable != m_DynamicState.m_bBuffer2Frames ) + { + SetRenderState( D3DRS_BUFFER2FRAMES, bEnable ); + m_DynamicState.m_bBuffer2Frames = bEnable; + } +#endif +} + +void CShaderAPIDx8::SetDepthFeatheringPixelShaderConstant( int iConstant, float fDepthBlendScale ) +{ + float fConstantValues[4]; + + if( IsX360() ) + { + const D3DMATRIX &projMatrix = GetProjectionMatrix(); + + fConstantValues[0] = 50.0f / fDepthBlendScale; + fConstantValues[1] = 1.0f / projMatrix.m[2][2]; + fConstantValues[2] = 1.0f / projMatrix.m[3][2]; + fConstantValues[3] = projMatrix.m[2][2]; + + /* + D3DXMATRIX invProjMatrix; + D3DXMatrixInverse( &invProjMatrix, NULL, (D3DXMATRIX *)&projMatrix ); + fConstantValues[1] = invProjMatrix.m[3][2]; + fConstantValues[2] = invProjMatrix.m[3][3]; + fConstantValues[3] = invProjMatrix.m[2][2]; + */ + } + else + { + fConstantValues[0] = m_DynamicState.m_DestAlphaDepthRange / fDepthBlendScale; + fConstantValues[1] = fConstantValues[2] = fConstantValues[3] = 0.0f; //empty + } + + SetPixelShaderConstant( iConstant, fConstantValues ); +} + + +//----------------------------------------------------------------------------- +// Cull mode.. +//----------------------------------------------------------------------------- +void CShaderAPIDx8::SetCullModeState( bool bEnable, D3DCULL nDesiredCullMode ) +{ + D3DCULL nCullMode = bEnable ? nDesiredCullMode : D3DCULL_NONE; + if ( nCullMode != m_DynamicState.m_CullMode ) + { + SetRenderStateConstMacro( this, D3DRS_CULLMODE, nCullMode ); + m_DynamicState.m_CullMode = nCullMode; + } +} + +void CShaderAPIDx8::ApplyCullEnable( bool bEnable ) +{ + m_DynamicState.m_bCullEnabled = bEnable; + SetCullModeState( m_DynamicState.m_bCullEnabled, m_DynamicState.m_DesiredCullMode ); +} + +void CShaderAPIDx8::CullMode( MaterialCullMode_t nCullMode ) +{ + LOCK_SHADERAPI(); + D3DCULL nNewCullMode; + switch( nCullMode ) + { + case MATERIAL_CULLMODE_CCW: + // Culls backfacing polys (normal) + nNewCullMode = D3DCULL_CCW; + break; + + case MATERIAL_CULLMODE_CW: + // Culls frontfacing polys + nNewCullMode = D3DCULL_CW; + break; + + default: + Warning( "CullMode: invalid cullMode\n" ); + return; + } + + if (m_DynamicState.m_DesiredCullMode != nNewCullMode) + { + FlushBufferedPrimitives(); + m_DynamicState.m_DesiredCullMode = nNewCullMode; + SetCullModeState( m_DynamicState.m_bCullEnabled, m_DynamicState.m_DesiredCullMode ); + } +} + +static ConVar mat_alphacoverage( "mat_alphacoverage", "1" ); +void CShaderAPIDx8::ApplyAlphaToCoverage( bool bEnable ) +{ + if ( mat_alphacoverage.GetBool() ) + { + if ( bEnable ) + EnableAlphaToCoverage(); + else + DisableAlphaToCoverage(); + } + else + { + DisableAlphaToCoverage(); + } +} + +//----------------------------------------------------------------------------- +// Returns the current cull mode of the current material (for selection mode only) +//----------------------------------------------------------------------------- +D3DCULL CShaderAPIDx8::GetCullMode() const +{ + Assert( m_pMaterial ); + if ( m_pMaterial->GetMaterialVarFlag( MATERIAL_VAR_NOCULL ) ) + return D3DCULL_NONE; + return m_DynamicState.m_DesiredCullMode; +} + +void CShaderAPIDx8::SetRasterState( const ShaderRasterState_t& state ) +{ + // FIXME: Implement! +} + + +void CShaderAPIDx8::ForceDepthFuncEquals( bool bEnable ) +{ + LOCK_SHADERAPI(); + if ( !g_pShaderDeviceDx8->IsDeactivated() ) + { + m_TransitionTable.ForceDepthFuncEquals( bEnable ); + } +} + +void CShaderAPIDx8::OverrideDepthEnable( bool bEnable, bool bDepthEnable ) +{ + LOCK_SHADERAPI(); + if ( !g_pShaderDeviceDx8->IsDeactivated() ) + { + m_TransitionTable.OverrideDepthEnable( bEnable, bDepthEnable ); + } +} + +void CShaderAPIDx8::OverrideAlphaWriteEnable( bool bOverrideEnable, bool bAlphaWriteEnable ) +{ + LOCK_SHADERAPI(); + if ( !g_pShaderDeviceDx8->IsDeactivated() ) + { + m_TransitionTable.OverrideAlphaWriteEnable( bOverrideEnable, bAlphaWriteEnable ); + } +} + +void CShaderAPIDx8::OverrideColorWriteEnable( bool bOverrideEnable, bool bColorWriteEnable ) +{ + LOCK_SHADERAPI(); + if ( !g_pShaderDeviceDx8->IsDeactivated() ) + { + m_TransitionTable.OverrideColorWriteEnable( bOverrideEnable, bColorWriteEnable ); + } +} + +void CShaderAPIDx8::UpdateFastClipUserClipPlane( void ) +{ + float plane[4]; + switch( m_DynamicState.m_HeightClipMode ) + { + case MATERIAL_HEIGHTCLIPMODE_DISABLE: + EnableFastClip( false ); + break; + case MATERIAL_HEIGHTCLIPMODE_RENDER_ABOVE_HEIGHT: + plane[0] = 0.0f; + plane[1] = 0.0f; + plane[2] = 1.0f; + plane[3] = m_DynamicState.m_HeightClipZ; + EnableFastClip( true ); + SetFastClipPlane(plane); + break; + case MATERIAL_HEIGHTCLIPMODE_RENDER_BELOW_HEIGHT: + plane[0] = 0.0f; + plane[1] = 0.0f; + plane[2] = -1.0f; + plane[3] = -m_DynamicState.m_HeightClipZ; + EnableFastClip( true ); + SetFastClipPlane(plane); + break; + } +} + +void CShaderAPIDx8::SetHeightClipZ( float z ) +{ + LOCK_SHADERAPI(); + if( z != m_DynamicState.m_HeightClipZ ) + { + FlushBufferedPrimitives(); + m_DynamicState.m_HeightClipZ = z; + UpdateVertexShaderFogParams(); + UpdateFastClipUserClipPlane(); + m_DynamicState.m_TransformChanged[MATERIAL_PROJECTION] |= + STATE_CHANGED_VERTEX_SHADER | STATE_CHANGED_FIXED_FUNCTION; + } +} + +void CShaderAPIDx8::SetHeightClipMode( MaterialHeightClipMode_t heightClipMode ) +{ + LOCK_SHADERAPI(); + if( heightClipMode != m_DynamicState.m_HeightClipMode ) + { + FlushBufferedPrimitives(); + m_DynamicState.m_HeightClipMode = heightClipMode; + UpdateVertexShaderFogParams(); + UpdateFastClipUserClipPlane(); + m_DynamicState.m_TransformChanged[MATERIAL_PROJECTION] |= + STATE_CHANGED_VERTEX_SHADER | STATE_CHANGED_FIXED_FUNCTION; + } +} + +void CShaderAPIDx8::SetClipPlane( int index, const float *pPlane ) +{ + LOCK_SHADERAPI(); + Assert( index < g_pHardwareConfig->MaxUserClipPlanes() && index >= 0 ); + + // NOTE: The plane here is specified in *world space* + // NOTE: This is done because they assume Ax+By+Cz+Dw = 0 (where w = 1 in real space) + // while we use Ax+By+Cz=D + D3DXPLANE plane; + plane.a = pPlane[0]; + plane.b = pPlane[1]; + plane.c = pPlane[2]; + plane.d = -pPlane[3]; + + if ( plane != m_DynamicState.m_UserClipPlaneWorld[index] ) + { + FlushBufferedPrimitives(); + + m_DynamicState.m_UserClipPlaneChanged |= ( 1 << index ); + m_DynamicState.m_UserClipPlaneWorld[index] = plane; + } +} + + +//----------------------------------------------------------------------------- +// Converts a D3DXMatrix to a VMatrix and back +//----------------------------------------------------------------------------- +void CShaderAPIDx8::D3DXMatrixToVMatrix( const D3DXMATRIX &in, VMatrix &out ) +{ + MatrixTranspose( *(const VMatrix*)&in, out ); +} + +void CShaderAPIDx8::VMatrixToD3DXMatrix( const VMatrix &in, D3DXMATRIX &out ) +{ + MatrixTranspose( in, *(VMatrix*)&out ); +} + + +//----------------------------------------------------------------------------- +// Mark all user clip planes as being dirty +//----------------------------------------------------------------------------- +void CShaderAPIDx8::MarkAllUserClipPlanesDirty() +{ + m_DynamicState.m_UserClipPlaneChanged |= ( 1 << g_pHardwareConfig->MaxUserClipPlanes() ) - 1; + m_DynamicState.m_bFastClipPlaneChanged = true; +} + + +//----------------------------------------------------------------------------- +// User clip plane override +//----------------------------------------------------------------------------- +void CShaderAPIDx8::EnableUserClipTransformOverride( bool bEnable ) +{ + LOCK_SHADERAPI(); + if ( m_DynamicState.m_bUserClipTransformOverride != bEnable ) + { + FlushBufferedPrimitives(); + m_DynamicState.m_bUserClipTransformOverride = bEnable; + MarkAllUserClipPlanesDirty(); + } +} + + +//----------------------------------------------------------------------------- +// Specify user clip transform +//----------------------------------------------------------------------------- +void CShaderAPIDx8::UserClipTransform( const VMatrix &worldToProjection ) +{ + LOCK_SHADERAPI(); + D3DXMATRIX dxWorldToProjection; + VMatrixToD3DXMatrix( worldToProjection, dxWorldToProjection ); + + if ( m_DynamicState.m_UserClipTransform != dxWorldToProjection ) + { + m_DynamicState.m_UserClipTransform = dxWorldToProjection; + if ( m_DynamicState.m_bUserClipTransformOverride ) + { + FlushBufferedPrimitives(); + MarkAllUserClipPlanesDirty(); + } + } +} + + +//----------------------------------------------------------------------------- +// Enables a user clip plane +//----------------------------------------------------------------------------- +void CShaderAPIDx8::EnableClipPlane( int index, bool bEnable ) +{ + LOCK_SHADERAPI(); + Assert( index < g_pHardwareConfig->MaxUserClipPlanes() && index >= 0 ); + if( ( m_DynamicState.m_UserClipPlaneEnabled & ( 1 << index ) ? true : false ) != bEnable ) + { + FlushBufferedPrimitives(); + if( bEnable ) + { + m_DynamicState.m_UserClipPlaneEnabled |= ( 1 << index ); + } + else + { + m_DynamicState.m_UserClipPlaneEnabled &= ~( 1 << index ); + } + SetRenderStateConstMacro( this, D3DRS_CLIPPLANEENABLE, m_DynamicState.m_UserClipPlaneEnabled ); + } +} + +//----------------------------------------------------------------------------- +// Recomputes the fast-clip plane matrices based on the current fast-clip plane +//----------------------------------------------------------------------------- +void CShaderAPIDx8::CommitFastClipPlane( ) +{ + // Don't bother recomputing if unchanged or disabled + if ( !m_DynamicState.m_bFastClipPlaneChanged || !m_DynamicState.m_FastClipEnabled ) + return; + + m_DynamicState.m_bFastClipPlaneChanged = false; + + D3DXMatrixIdentity( &m_CachedFastClipProjectionMatrix ); + + // Compute worldToProjection - need inv. transpose for transforming plane. + D3DXMATRIX viewToProjInvTrans, viewToProjInv, viewToProj = GetTransform(MATERIAL_PROJECTION); + viewToProj._43 *= 0.5f; // pull in zNear because the shear in effect + // moves it out: clipping artifacts when looking down at water + // could occur if this multiply is not done + + D3DXMATRIX worldToViewInvTrans, worldToViewInv, worldToView = GetUserClipTransform(); + + D3DXMatrixInverse( &worldToViewInv, NULL, &worldToView ); + D3DXMatrixTranspose( &worldToViewInvTrans, &worldToViewInv ); + + D3DXMatrixInverse( &viewToProjInv, NULL, &viewToProj ); + D3DXMatrixTranspose( &viewToProjInvTrans, &viewToProjInv ); + + D3DXPLANE plane; + D3DXPlaneNormalize( &plane, &m_DynamicState.m_FastClipPlane ); + D3DXVECTOR4 clipPlane( plane.a, plane.b, plane.c, plane.d ); + + // transform clip plane into view space + D3DXVec4Transform( &clipPlane, &clipPlane, &worldToViewInvTrans ); + + // transform clip plane into projection space + D3DXVec4Transform( &clipPlane, &clipPlane, &viewToProjInvTrans ); + +#define ALLOW_FOR_FASTCLIPDUMPS 0 + +#if (ALLOW_FOR_FASTCLIPDUMPS == 1) + static ConVar shader_dumpfastclipprojectioncoords( "shader_dumpfastclipprojectioncoords", "0", 0, "dump fast clip projected matrix" ); + if( shader_dumpfastclipprojectioncoords.GetBool() ) + DevMsg( "Fast clip plane projected coordinates: %f %f %f %f", clipPlane.x, clipPlane.y, clipPlane.z, clipPlane.w ); +#endif + + if( (clipPlane.z * clipPlane.w) <= -0.4f ) // a plane with (z*w) > -0.4 at this point is behind the camera and will cause graphical glitches. Toss it. (0.4 found through experimentation) + { +#if (ALLOW_FOR_FASTCLIPDUMPS == 1) + if( shader_dumpfastclipprojectioncoords.GetBool() ) + DevMsg( " %f %f %f %f\n", clipPlane.x, clipPlane.y, clipPlane.z, clipPlane.w ); +#endif + + D3DXVec4Normalize( &clipPlane, &clipPlane ); + + //if ((fabs(clipPlane.z) > 0.01) && (fabs(clipPlane.w) > 0.01f)) + { + // put projection space clip plane in Z column + m_CachedFastClipProjectionMatrix._13 = clipPlane.x; + m_CachedFastClipProjectionMatrix._23 = clipPlane.y; + m_CachedFastClipProjectionMatrix._33 = clipPlane.z; + m_CachedFastClipProjectionMatrix._43 = clipPlane.w; + } + } +#if (ALLOW_FOR_FASTCLIPDUMPS == 1) + else + { + if( shader_dumpfastclipprojectioncoords.GetBool() ) + DevMsg( "\n" ); //finish off the line above + } +#endif + + m_CachedFastClipProjectionMatrix = viewToProj * m_CachedFastClipProjectionMatrix; + + // Update the cached polyoffset matrix (with clip) too: + ComputePolyOffsetMatrix( m_CachedFastClipProjectionMatrix, m_CachedFastClipPolyOffsetProjectionMatrix ); +} + +//----------------------------------------------------------------------------- +// Sets the fast-clip plane; but doesn't update the matrices +//----------------------------------------------------------------------------- +void CShaderAPIDx8::SetFastClipPlane( const float *pPlane ) +{ + LOCK_SHADERAPI(); + D3DXPLANE plane; + plane.a = pPlane[0]; + plane.b = pPlane[1]; + plane.c = pPlane[2]; + plane.d = -pPlane[3]; + if ( plane != m_DynamicState.m_FastClipPlane ) + { + FlushBufferedPrimitives(); + UpdateVertexShaderFogParams(); + + m_DynamicState.m_FastClipPlane = plane; + + // Mark a dirty bit so when it comes time to commit view + projection transforms, + // we also update the fast clip matrices + m_DynamicState.m_bFastClipPlaneChanged = true; + + m_DynamicState.m_TransformChanged[MATERIAL_PROJECTION] |= + STATE_CHANGED_VERTEX_SHADER | STATE_CHANGED_FIXED_FUNCTION; + } +} + + +//----------------------------------------------------------------------------- +// Enables/disables fast-clip mode +//----------------------------------------------------------------------------- +void CShaderAPIDx8::EnableFastClip( bool bEnable ) +{ + LOCK_SHADERAPI(); + if( m_DynamicState.m_FastClipEnabled != bEnable ) + { + FlushBufferedPrimitives(); + UpdateVertexShaderFogParams(); + + m_DynamicState.m_FastClipEnabled = bEnable; + + m_DynamicState.m_TransformChanged[MATERIAL_PROJECTION] |= + STATE_CHANGED_VERTEX_SHADER | STATE_CHANGED_FIXED_FUNCTION; + } +} + +/* +// ----------------------------------------------------------------------------- +// SetInvariantClipVolume - This routine takes six planes as input and sets the +// appropriate Direct3D user clip plane state +// What we mean by "invariant clipping" here is that certain devices implement +// user clip planes at the raster level, which means that multi-pass rendering +// where one pass is unclipped (such as base geometry) and another pass *IS* +// clipped (such as flashlight geometry), there is no z-fighting since the +// clipping is implemented at the raster level in an "invariant" way +// ----------------------------------------------------------------------------- +void CShaderAPIDx8::SetInvariantClipVolume( Frustum_t *pFrustumPlanes ) +{ + // Only do this on modern nVidia hardware, which does invariant clipping + if ( m_VendorID == VENDORID_NVIDIA ) + { + if ( pFrustumPlanes ) + { +// if () +// { +// +// } + + for (int i=0; i<6; i++) + { + const cplane_t *pPlane = pFrustumPlanes->GetPlane(i); + + SetClipPlane( i, (float *) &pPlane->normal ); + EnableClipPlane( i, true ); + +// FRUSTUM_RIGHT = 0, +// FRUSTUM_LEFT = 1, +// FRUSTUM_TOP = 2, +// FRUSTUM_BOTTOM = 3, +// FRUSTUM_NEARZ = 4, +// FRUSTUM_FARZ = 5, + + } + } + else // NULL disables the invariant clip volume... + { + for (int i=0; i<6; i++) + { + EnableClipPlane( i, false ); + } + } + } +} +*/ + +//----------------------------------------------------------------------------- +// Vertex blending +//----------------------------------------------------------------------------- +void CShaderAPIDx8::SetVertexBlendState( int numBones ) +{ + if (numBones < 0) + { + numBones = m_DynamicState.m_NumBones; + } + + // For fixed-function, the number of weights is actually one less than + // the number of bones + if (numBones > 0) + --numBones; + + bool normalizeNormals = true; + D3DVERTEXBLENDFLAGS vertexBlend; + switch(numBones) + { + case 0: + vertexBlend = D3DVBF_DISABLE; + normalizeNormals = false; + break; + + case 1: + vertexBlend = D3DVBF_1WEIGHTS; + break; + + case 2: + vertexBlend = D3DVBF_2WEIGHTS; + break; + + case 3: + vertexBlend = D3DVBF_3WEIGHTS; + break; + + default: + vertexBlend = D3DVBF_DISABLE; + Assert(0); + break; + } + + if (m_DynamicState.m_VertexBlend != vertexBlend) + { + m_DynamicState.m_VertexBlend = vertexBlend; + SetSupportedRenderState( D3DRS_VERTEXBLEND, vertexBlend ); + } + + // Activate normalize normals when skinning is on + if (m_DynamicState.m_NormalizeNormals != normalizeNormals) + { + m_DynamicState.m_NormalizeNormals = normalizeNormals; + SetSupportedRenderState( D3DRS_NORMALIZENORMALS, normalizeNormals ); + } +} + + +//----------------------------------------------------------------------------- +// Vertex blending +//----------------------------------------------------------------------------- +void CShaderAPIDx8::SetNumBoneWeights( int numBones ) +{ + LOCK_SHADERAPI(); + if (m_DynamicState.m_NumBones != numBones) + { + FlushBufferedPrimitives(); + m_DynamicState.m_NumBones = numBones; + + if ( m_TransitionTable.CurrentShadowState() ) + { + SetVertexBlendState( m_TransitionTable.CurrentShadowState()->m_VertexBlendEnable ? -1 : 0 ); + } + } +} + +void CShaderAPIDx8::EnableHWMorphing( bool bEnable ) +{ + LOCK_SHADERAPI(); + if ( m_DynamicState.m_bHWMorphingEnabled != bEnable ) + { + FlushBufferedPrimitives(); + m_DynamicState.m_bHWMorphingEnabled = bEnable; + } +} + +void CShaderAPIDx8::EnabledSRGBWrite( bool bEnabled ) +{ + m_DynamicState.m_bSRGBWritesEnabled = bEnabled; + + if ( g_pHardwareConfig->SupportsPixelShaders_2_b() ) + { + UpdatePixelFogColorConstant(); + + if ( bEnabled && g_pHardwareConfig->NeedsShaderSRGBConversion() ) + BindTexture( SHADER_SAMPLER15, m_hLinearToGammaTableTexture ); + else + BindTexture( SHADER_SAMPLER15, m_hLinearToGammaTableIdentityTexture ); + } +} + +#if defined( _X360 ) +void CShaderAPIDx8::ApplySRGBReadState( int iTextureStage, bool bSRGBReadEnabled ) +{ + Sampler_t sampler = (Sampler_t)iTextureStage; + SamplerState_t &samplerState = SamplerState( sampler ); + samplerState.m_SRGBReadEnable = bSRGBReadEnabled; + + if ( ( samplerState.m_BoundTexture == INVALID_SHADERAPI_TEXTURE_HANDLE ) || !samplerState.m_TextureEnable ) + { + return; + } + + IDirect3DBaseTexture *pBindTexture = CShaderAPIDx8::GetD3DTexture( samplerState.m_BoundTexture ); + if ( !pBindTexture ) + { + return; + } + + DWORD linearFormatBackup = pBindTexture->Format.dword[0]; //if we convert to srgb format, we need the original format for reverting. We only need the first DWORD of GPUTEXTURE_FETCH_CONSTANT. + if ( bSRGBReadEnabled ) + { + pBindTexture->Format.SignX = pBindTexture->Format.SignY = pBindTexture->Format.SignZ = 3; //convert to srgb format for the bind. This effectively emulates the old srgb read sampler state + } + + Dx9Device()->SetTexture( sampler, pBindTexture ); + + // copy back the format in case we changed it + pBindTexture->Format.dword[0] = linearFormatBackup; +} +#endif + +//----------------------------------------------------------------------------- +// Fog methods... +//----------------------------------------------------------------------------- +void CShaderAPIDx8::UpdatePixelFogColorConstant( void ) +{ + Assert( HardwareConfig()->SupportsPixelShaders_2_b() ); + float fogColor[4]; + + switch( GetPixelFogMode() ) + { + case MATERIAL_FOG_NONE: + { + for( int i = 0; i != 3; ++i ) + fogColor[i] = 0.0f; + } + break; + + case MATERIAL_FOG_LINEAR: + { + //setup the fog for mixing linear fog in the pixel shader so that it emulates ff range fog + for( int i = 0; i != 3; ++i ) + fogColor[i] = m_DynamicState.m_PixelFogColor[i]; + + if( m_DynamicState.m_bSRGBWritesEnabled ) + { + //since the fog color will assuredly get converted from linear to gamma, we should probably convert it from gamma to linear + for( int i = 0; i != 3; ++i ) + fogColor[i] = GammaToLinear_HardwareSpecific( fogColor[i] ); + } + + if( (!m_DynamicState.m_bFogGammaCorrectionDisabled) && (g_pHardwareConfig->GetHDRType() == HDR_TYPE_INTEGER) ) + { + for( int i = 0; i != 3; ++i ) + fogColor[i] *= m_ToneMappingScale.x; // Linear + } + } + break; + + case MATERIAL_FOG_LINEAR_BELOW_FOG_Z: + { + //water fog has been around a while and has never tonemap scaled, and has always been in linear space + if( g_pHardwareConfig->NeedsShaderSRGBConversion() ) + { + //srgb in ps2b uses the 2.2 curve + for( int i = 0; i != 3; ++i ) + fogColor[i] = pow( m_DynamicState.m_PixelFogColor[i], 2.2f ); + } + else + { + for( int i = 0; i != 3; ++i ) + fogColor[i] = GammaToLinear_HardwareSpecific( m_DynamicState.m_PixelFogColor[i] ); //this is how water fog color has always been setup in the past + } + + if( (!m_DynamicState.m_bFogGammaCorrectionDisabled) && (g_pHardwareConfig->GetHDRType() == HDR_TYPE_INTEGER) ) + { + for( int i = 0; i != 3; ++i ) + fogColor[i] *= m_ToneMappingScale.x; // Linear + } + } + break; + + NO_DEFAULT; + }; + + fogColor[3] = 1.0f / m_DynamicState.m_DestAlphaDepthRange; + + SetPixelShaderConstant( LINEAR_FOG_COLOR, fogColor ); +} + + +void CShaderAPIDx8::ApplyFogMode( ShaderFogMode_t fogMode, bool bSRGBWritesEnabled, bool bDisableFogGammaCorrection ) +{ + HDRType_t hdrType = g_pHardwareConfig->GetHDRType(); + + if ( fogMode == SHADER_FOGMODE_DISABLED ) + { + if( hdrType != HDR_TYPE_FLOAT ) + { + FogMode( MATERIAL_FOG_NONE ); + } + + if( m_DelayedShaderConstants.iPixelShaderFogParams != -1 ) + SetPixelShaderFogParams( m_DelayedShaderConstants.iPixelShaderFogParams, fogMode ); + + return; + } + + bool bShouldGammaCorrect = true; // By default, we'll gamma correct. + unsigned char r = 0, g = 0, b = 0; // Black fog + + + if( hdrType != HDR_TYPE_FLOAT ) + { + FogMode( m_SceneFogMode ); + } + + if( m_DelayedShaderConstants.iPixelShaderFogParams != -1 ) + SetPixelShaderFogParams( m_DelayedShaderConstants.iPixelShaderFogParams, fogMode ); + + switch( fogMode ) + { + case SHADER_FOGMODE_BLACK: // Additive decals + bShouldGammaCorrect = false; + break; + case SHADER_FOGMODE_OO_OVERBRIGHT: + case SHADER_FOGMODE_GREY: // Mod2x decals + r = g = b = 128; + break; + case SHADER_FOGMODE_WHITE: // Multiplicative decals + r = g = b = 255; + bShouldGammaCorrect = false; + break; + case SHADER_FOGMODE_FOGCOLOR: + GetSceneFogColor( &r, &g, &b ); // Scene fog color + break; + NO_DEFAULT + } + + bShouldGammaCorrect &= !bDisableFogGammaCorrection; + m_DynamicState.m_bFogGammaCorrectionDisabled = !bShouldGammaCorrect; + + D3DCOLOR color; + if ( bShouldGammaCorrect ) + { + color = ComputeGammaCorrectedFogColor( r, g, b, bSRGBWritesEnabled ); + } + else + { + color = D3DCOLOR_ARGB( 255, r, g, b ); + } + + + const float fColorScale = 1.0f / 255.0f; + m_DynamicState.m_PixelFogColor[0] = (float)(r) * fColorScale; + m_DynamicState.m_PixelFogColor[1] = (float)(g) * fColorScale; + m_DynamicState.m_PixelFogColor[2] = (float)(b) * fColorScale; + + if( g_pMaterialSystemHardwareConfig->SupportsPixelShaders_2_b() ) + { + UpdatePixelFogColorConstant(); + } + + if ( color != m_DynamicState.m_FogColor ) + { + if( hdrType != HDR_TYPE_FLOAT ) + { + m_DynamicState.m_FogColor = color; + SetRenderStateConstMacro( this, D3DRS_FOGCOLOR, m_DynamicState.m_FogColor ); + } + } +} + +void CShaderAPIDx8::SceneFogMode( MaterialFogMode_t fogMode ) +{ + LOCK_SHADERAPI(); + if( m_SceneFogMode != fogMode ) + { + FlushBufferedPrimitives(); + m_SceneFogMode = fogMode; + + if ( m_TransitionTable.CurrentShadowState() ) + { + // Get the shadow state in sync since it depends on SceneFogMode. + ApplyFogMode( m_TransitionTable.CurrentShadowState()->m_FogMode, m_TransitionTable.CurrentShadowState()->m_SRGBWriteEnable, m_TransitionTable.CurrentShadowState()->m_bDisableFogGammaCorrection ); + } + } +} + +MaterialFogMode_t CShaderAPIDx8::GetSceneFogMode() +{ + return m_SceneFogMode; +} + +MaterialFogMode_t CShaderAPIDx8::GetPixelFogMode() +{ + if( ShouldUsePixelFogForMode( m_SceneFogMode ) ) + return m_SceneFogMode; + else + return MATERIAL_FOG_NONE; +} + +int CShaderAPIDx8::GetPixelFogCombo( void ) +{ + if( (m_SceneFogMode != MATERIAL_FOG_NONE) && ShouldUsePixelFogForMode( m_SceneFogMode ) ) + return m_SceneFogMode - 1; //PIXELFOGTYPE dynamic combos are shifted down one. MATERIAL_FOG_NONE is simulated by range fog with rigged parameters. Gets rid of a dynamic combo across the ps2x set + else + return MATERIAL_FOG_NONE; +} + + +static ConVar r_pixelfog( "r_pixelfog", "1" ); + +bool CShaderAPIDx8::ShouldUsePixelFogForMode( MaterialFogMode_t fogMode ) +{ + if( fogMode == MATERIAL_FOG_NONE ) + return false; + + if( IsX360() || IsPosix() ) // Always use pixel fog on X360 and Posix + return true; + + if( g_pHardwareConfig->Caps().m_nDXSupportLevel < 90 ) //pixel fog not available until at least ps2.0 + return false; + + switch( m_SceneFogMode ) + { + case MATERIAL_FOG_LINEAR: + return (g_pHardwareConfig->SupportsPixelShaders_2_b() && //lightmappedgeneric_ps20.fxc can't handle the instruction count + r_pixelfog.GetBool()); //use pixel fog if preferred + + case MATERIAL_FOG_LINEAR_BELOW_FOG_Z: + return true; + }; + + return false; +} + +//----------------------------------------------------------------------------- +// Fog methods... +//----------------------------------------------------------------------------- +void CShaderAPIDx8::FogMode( MaterialFogMode_t fogMode ) +{ + bool bFogEnable; + + if ( IsX360() ) + { + // FF fog not applicable on 360 + return; + } + + m_DynamicState.m_SceneFog = fogMode; + switch( fogMode ) + { + default: + Assert( 0 ); + // fall through + + case MATERIAL_FOG_NONE: + bFogEnable = false; + break; + + case MATERIAL_FOG_LINEAR: + // use vertex fog to achieve linear range fog + bFogEnable = true; + break; + + case MATERIAL_FOG_LINEAR_BELOW_FOG_Z: + // use pixel fog on 9.0 and greater for height fog + bFogEnable = g_pHardwareConfig->Caps().m_nDXSupportLevel < 90; + break; + } + + if( ShouldUsePixelFogForMode( fogMode ) ) + { + bFogEnable = false; //disable FF fog when doing fog in the pixel shader + } + +#if 0 + // HACK - do this to disable fog always + bFogEnable = false; + m_DynamicState.m_SceneFog = MATERIAL_FOG_NONE; +#endif + + // These two are always set to this, so don't bother setting them. + // We are always using vertex fog. +// SetRenderState( D3DRS_FOGTABLEMODE, D3DFOG_NONE ); +// SetRenderState( D3DRS_RANGEFOGENABLE, false ); + + // Set fog enable if it's different than before. + if ( bFogEnable != m_DynamicState.m_FogEnable ) + { + SetSupportedRenderState( D3DRS_FOGENABLE, bFogEnable ); + m_DynamicState.m_FogEnable = bFogEnable; + } +} + + +void CShaderAPIDx8::FogStart( float fStart ) +{ + LOCK_SHADERAPI(); + if (fStart != m_DynamicState.m_FogStart) + { + // need to flush the dynamic buffer + FlushBufferedPrimitives(); + + SetRenderStateConstMacro( this, D3DRS_FOGSTART, *((DWORD*)(&fStart))); + m_VertexShaderFogParams[0] = fStart; + UpdateVertexShaderFogParams(); + m_DynamicState.m_FogStart = fStart; + } +} + +void CShaderAPIDx8::FogEnd( float fEnd ) +{ + LOCK_SHADERAPI(); + if (fEnd != m_DynamicState.m_FogEnd) + { + // need to flush the dynamic buffer + FlushBufferedPrimitives(); + + SetRenderStateConstMacro( this, D3DRS_FOGEND, *((DWORD*)(&fEnd))); + m_VertexShaderFogParams[1] = fEnd; + UpdateVertexShaderFogParams(); + m_DynamicState.m_FogEnd = fEnd; + } +} + +void CShaderAPIDx8::SetFogZ( float fogZ ) +{ + LOCK_SHADERAPI(); + if (fogZ != m_DynamicState.m_FogZ) + { + // need to flush the dynamic buffer + FlushBufferedPrimitives(); + m_DynamicState.m_FogZ = fogZ; + UpdateVertexShaderFogParams(); + } +} + +void CShaderAPIDx8::FogMaxDensity( float flMaxDensity ) +{ + LOCK_SHADERAPI(); + if (flMaxDensity != m_DynamicState.m_FogMaxDensity) + { + // need to flush the dynamic buffer + FlushBufferedPrimitives(); + +// SetRenderState(D3DRS_FOGDENSITY, *((DWORD*)(&flMaxDensity))); // ??? do I need to to this ??? + m_flFogMaxDensity = flMaxDensity; + UpdateVertexShaderFogParams(); + m_DynamicState.m_FogMaxDensity = flMaxDensity; + } +} + +void CShaderAPIDx8::GetFogDistances( float *fStart, float *fEnd, float *fFogZ ) +{ + LOCK_SHADERAPI(); + if( fStart ) + *fStart = m_DynamicState.m_FogStart; + + if( fEnd ) + *fEnd = m_DynamicState.m_FogEnd; + + if( fFogZ ) + *fFogZ = m_DynamicState.m_FogZ; +} + +void CShaderAPIDx8::SceneFogColor3ub( unsigned char r, unsigned char g, unsigned char b ) +{ + LOCK_SHADERAPI(); + if( m_SceneFogColor[0] != r || m_SceneFogColor[1] != g || m_SceneFogColor[2] != b ) + { + FlushBufferedPrimitives(); + m_SceneFogColor[0] = r; + m_SceneFogColor[1] = g; + m_SceneFogColor[2] = b; + + if ( m_TransitionTable.CurrentShadowState() ) + { + ApplyFogMode( m_TransitionTable.CurrentShadowState()->m_FogMode, m_TransitionTable.CurrentShadowState()->m_SRGBWriteEnable, m_TransitionTable.CurrentShadowState()->m_bDisableFogGammaCorrection ); + } + } +} + +void CShaderAPIDx8::GetSceneFogColor( unsigned char *rgb ) +{ + LOCK_SHADERAPI(); + rgb[0] = m_SceneFogColor[0]; + rgb[1] = m_SceneFogColor[1]; + rgb[2] = m_SceneFogColor[2]; +} + +void CShaderAPIDx8::GetSceneFogColor( unsigned char *r, unsigned char *g, unsigned char *b ) +{ + *r = m_SceneFogColor[0]; + *g = m_SceneFogColor[1]; + *b = m_SceneFogColor[2]; +} + + +//----------------------------------------------------------------------------- +// Gamma correction of fog color, or not... +//----------------------------------------------------------------------------- +D3DCOLOR CShaderAPIDx8::ComputeGammaCorrectedFogColor( unsigned char r, unsigned char g, unsigned char b, bool bSRGBWritesEnabled ) +{ +#ifdef _DEBUG + if( g_pHardwareConfig->GetHDRType() == HDR_TYPE_FLOAT && !bSRGBWritesEnabled ) + { +// Assert( 0 ); + } +#endif + bool bLinearSpace = g_pHardwareConfig->Caps().m_bFogColorAlwaysLinearSpace || + ( bSRGBWritesEnabled && ( g_pHardwareConfig->Caps().m_bFogColorSpecifiedInLinearSpace || g_pHardwareConfig->GetHDRType() == HDR_TYPE_FLOAT ) ); + + bool bScaleFogByToneMappingScale = g_pHardwareConfig->GetHDRType() == HDR_TYPE_INTEGER; + + float fr = ( r / 255.0f ); + float fg = ( g / 255.0f ); + float fb = ( b / 255.0f ); + if ( bLinearSpace ) + { + fr = GammaToLinear( fr ); + fg = GammaToLinear( fg ); + fb = GammaToLinear( fb ); + if ( bScaleFogByToneMappingScale ) + { + fr *= m_ToneMappingScale.x; // + fg *= m_ToneMappingScale.x; // Linear + fb *= m_ToneMappingScale.x; // + } + } + else if ( bScaleFogByToneMappingScale ) + { + fr *= m_ToneMappingScale.w; // + fg *= m_ToneMappingScale.w; // Gamma + fb *= m_ToneMappingScale.w; // + } + + fr = min( fr, 1.0f ); + fg = min( fg, 1.0f ); + fb = min( fb, 1.0f ); + r = (int)( fr * 255 ); + g = (int)( fg * 255 ); + b = (int)( fb * 255 ); + return D3DCOLOR_ARGB( 255, r, g, b ); +} + + +//----------------------------------------------------------------------------- +// Some methods chaining vertex + pixel shaders through to the shader manager +//----------------------------------------------------------------------------- +void CShaderAPIDx8::SetVertexShaderIndex( int vshIndex ) +{ + ShaderManager()->SetVertexShaderIndex( vshIndex ); +} + +void CShaderAPIDx8::SetPixelShaderIndex( int pshIndex ) +{ + ShaderManager()->SetPixelShaderIndex( pshIndex ); +} + +void CShaderAPIDx8::SyncToken( const char *pToken ) +{ + LOCK_SHADERAPI(); + RECORD_COMMAND( DX8_SYNC_TOKEN, 1 ); + RECORD_STRING( pToken ); +} + + +//----------------------------------------------------------------------------- +// Deals with vertex buffers +//----------------------------------------------------------------------------- +void CShaderAPIDx8::DestroyVertexBuffers( bool bExitingLevel ) +{ + LOCK_SHADERAPI(); + MeshMgr()->DestroyVertexBuffers( ); + // After a map is shut down, we switch to using smaller dynamic VBs + // (VGUI shouldn't need much), so that we have more memory free during map loading + m_nDynamicVBSize = bExitingLevel ? DYNAMIC_VERTEX_BUFFER_MEMORY_SMALL : DYNAMIC_VERTEX_BUFFER_MEMORY; +} + +int CShaderAPIDx8::GetCurrentDynamicVBSize( void ) +{ + return m_nDynamicVBSize; +} + +FORCEINLINE void CShaderAPIDx8::SetVertexShaderConstantInternal( int var, float const* pVec, int numVecs, bool bForce ) +{ + Assert( pVec ); + + // DX8 asm shaders use a constant mapping which has transforms and vertex shader + // specific constants shifted down by 10 constants (two 5-constant light structures) + if ( IsPC() ) + { + if ( (g_pHardwareConfig->Caps().m_nDXSupportLevel < 90) && (var >= VERTEX_SHADER_MODULATION_COLOR) ) + { + var -= 10; + } + Assert( var + numVecs <= g_pHardwareConfig->NumVertexShaderConstants() ); + + if ( !bForce ) + { + int skip = 0; + numVecs = AdjustUpdateRange( pVec, &m_DesiredState.m_pVectorVertexShaderConstant[var], numVecs, &skip ); + if ( !numVecs ) + return; + var += skip; + pVec += skip * 4; + } + Dx9Device()->SetVertexShaderConstantF( var, pVec, numVecs ); + memcpy( &m_DynamicState.m_pVectorVertexShaderConstant[var], pVec, numVecs * 4 * sizeof(float) ); + } + else + { + Assert( var + numVecs <= g_pHardwareConfig->NumVertexShaderConstants() ); + } + + memcpy( &m_DesiredState.m_pVectorVertexShaderConstant[var], pVec, numVecs * 4 * sizeof(float) ); + + if ( IsX360() ) + { + m_MaxVectorVertexShaderConstant = max( m_MaxVectorVertexShaderConstant, var + numVecs ); + } +} + + +//----------------------------------------------------------------------------- +// Sets the constant register for vertex and pixel shaders +//----------------------------------------------------------------------------- +void CShaderAPIDx8::SetVertexShaderConstant( int var, float const* pVec, int numVecs, bool bForce ) +{ + SetVertexShaderConstantInternal( var, pVec, numVecs, bForce ); +} + +//----------------------------------------------------------------------------- +// Sets the boolean registers for vertex shader control flow +//----------------------------------------------------------------------------- +void CShaderAPIDx8::SetBooleanVertexShaderConstant( int var, int const* pVec, int numBools, bool bForce ) +{ + Assert( pVec ); + Assert( var + numBools <= g_pHardwareConfig->NumBooleanVertexShaderConstants() ); + + if ( IsPC() && g_pHardwareConfig->GetDXSupportLevel() < 90 ) + { + return; + } + + if ( IsPC() && !bForce && memcmp( pVec, &m_DesiredState.m_pBooleanVertexShaderConstant[var], numBools * sizeof( BOOL ) ) == 0 ) + { + return; + } + + if ( IsPC() ) + { + Dx9Device()->SetVertexShaderConstantB( var, pVec, numBools ); + memcpy( &m_DynamicState.m_pBooleanVertexShaderConstant[var], pVec, numBools * sizeof(BOOL) ); + } + + memcpy( &m_DesiredState.m_pBooleanVertexShaderConstant[var], pVec, numBools * sizeof(BOOL) ); + + if ( IsX360() && var + numBools > m_MaxBooleanVertexShaderConstant ) + { + m_MaxBooleanVertexShaderConstant = var + numBools; + Assert( m_MaxBooleanVertexShaderConstant <= 16 ); + } +} + + +//----------------------------------------------------------------------------- +// Sets the integer registers for vertex shader control flow +//----------------------------------------------------------------------------- +void CShaderAPIDx8::SetIntegerVertexShaderConstant( int var, int const* pVec, int numIntVecs, bool bForce ) +{ + Assert( pVec ); + Assert( var + numIntVecs <= g_pHardwareConfig->NumIntegerVertexShaderConstants() ); + + if ( IsPC() && g_pHardwareConfig->GetDXSupportLevel() < 90 ) + { + return; + } + + if ( IsPC() && !bForce && memcmp( pVec, &m_DesiredState.m_pIntegerVertexShaderConstant[var], numIntVecs * sizeof( IntVector4D ) ) == 0 ) + { + return; + } + + if ( IsPC() ) + { + Dx9Device()->SetVertexShaderConstantI( var, pVec, numIntVecs ); + memcpy( &m_DynamicState.m_pIntegerVertexShaderConstant[var], pVec, numIntVecs * sizeof(IntVector4D) ); + } + memcpy( &m_DesiredState.m_pIntegerVertexShaderConstant[var], pVec, numIntVecs * sizeof(IntVector4D) ); + + if ( IsX360() && var + numIntVecs > m_MaxIntegerVertexShaderConstant ) + { + m_MaxIntegerVertexShaderConstant = var + numIntVecs; + Assert( m_MaxIntegerVertexShaderConstant <= 16 ); + } +} + +FORCEINLINE void CShaderAPIDx8::SetPixelShaderConstantInternal( int nStartConst, float const* pValues, int nNumConsts, bool bForce ) +{ + Assert( nStartConst + nNumConsts <= g_pHardwareConfig->NumPixelShaderConstants() ); + + if ( IsPC() ) + { + if ( ! bForce ) + { + int skip = 0; + nNumConsts = AdjustUpdateRange( pValues, &m_DesiredState.m_pVectorPixelShaderConstant[nStartConst], nNumConsts, &skip ); + if ( !nNumConsts ) + return; + nStartConst += skip; + pValues += skip * 4; + } + + Dx9Device()->SetPixelShaderConstantF( nStartConst, pValues, nNumConsts ); + memcpy( &m_DynamicState.m_pVectorPixelShaderConstant[nStartConst], pValues, nNumConsts * 4 * sizeof(float) ); + } + + memcpy( &m_DesiredState.m_pVectorPixelShaderConstant[nStartConst], pValues, nNumConsts * 4 * sizeof(float) ); + + if ( IsX360() ) + { + m_MaxVectorPixelShaderConstant = max( m_MaxVectorPixelShaderConstant, nStartConst + nNumConsts ); + Assert( m_MaxVectorPixelShaderConstant <= 32 ); + } +} + +void CShaderAPIDx8::SetPixelShaderConstant( int var, float const* pVec, int numVecs, bool bForce ) +{ + SetPixelShaderConstantInternal( var, pVec, numVecs, bForce ); +} + +template FORCEINLINE T GetData( uint8 const *pData ) +{ + return * ( reinterpret_cast< T const *>( pData ) ); +} + + + +void CShaderAPIDx8::SetStandardTextureHandle( StandardTextureId_t nId, ShaderAPITextureHandle_t nHandle ) +{ + Assert( nId < ARRAYSIZE( m_StdTextureHandles ) ); + m_StdTextureHandles[nId] = nHandle; +} + +void CShaderAPIDx8::ExecuteCommandBuffer( uint8 *pCmdBuf ) +{ + uint8 *pReturnStack[20]; + uint8 **pSP = &pReturnStack[ARRAYSIZE(pReturnStack)]; + uint8 *pLastCmd; + for(;;) + { + uint8 *pCmd=pCmdBuf; + int nCmd = GetData( pCmdBuf ); + switch( nCmd ) + { + case CBCMD_END: + { + if ( pSP == &pReturnStack[ARRAYSIZE(pReturnStack)] ) + return; + else + { + // pop pc + pCmdBuf = *( pSP ++ ); + break; + } + } + + case CBCMD_JUMP: + pCmdBuf = GetData( pCmdBuf + sizeof( int ) ); + break; + + case CBCMD_JSR: + { + Assert( pSP > &(pReturnStack[0] ) ); +// *(--pSP ) = pCmdBuf + sizeof( int ) + sizeof( uint8 *); +// pCmdBuf = GetData( pCmdBuf + sizeof( int ) ); + ExecuteCommandBuffer( GetData( pCmdBuf + sizeof( int ) ) ); + pCmdBuf = pCmdBuf + sizeof( int ) + sizeof( uint8 *); + break; + } + + case CBCMD_SET_PIXEL_SHADER_FLOAT_CONST: + { + int nStartConst = GetData( pCmdBuf + sizeof( int ) ); + int nNumConsts = GetData( pCmdBuf + 2 * sizeof( int ) ); + float const *pValues = reinterpret_cast< float const *> ( pCmdBuf + 3 * sizeof( int ) ); + pCmdBuf += nNumConsts * 4 * sizeof( float ) + 3 * sizeof( int ); + SetPixelShaderConstantInternal( nStartConst, pValues, nNumConsts, false ); + break; + } + + case CBCMD_SETPIXELSHADERFOGPARAMS: + { + int nReg = GetData( pCmdBuf + sizeof( int ) ); + pCmdBuf += 2 * sizeof( int ); + SetPixelShaderFogParams( nReg ); // !! speed fixme + break; + } + case CBCMD_STORE_EYE_POS_IN_PSCONST: + { + int nReg = GetData( pCmdBuf + sizeof( int ) ); + pCmdBuf += 2 * sizeof( int ); + SetPixelShaderConstantInternal( nReg, m_WorldSpaceCameraPositon.Base(), 1, false ); + break; + } + + case CBCMD_COMMITPIXELSHADERLIGHTING: + { + int nReg = GetData( pCmdBuf + sizeof( int ) ); + pCmdBuf += 2 * sizeof( int ); + CommitPixelShaderLighting( nReg ); + break; + } + + case CBCMD_SETPIXELSHADERSTATEAMBIENTLIGHTCUBE: + { + int nReg = GetData( pCmdBuf + sizeof( int ) ); + pCmdBuf += 2 * sizeof( int ); + float *pCubeBase = m_DynamicState.m_AmbientLightCube[0].Base(); + SetPixelShaderConstantInternal( nReg, pCubeBase, 6, false ); + break; + } + + case CBCMD_SETAMBIENTCUBEDYNAMICSTATEVERTEXSHADER: + { + pCmdBuf +=sizeof( int ); + SetVertexShaderStateAmbientLightCube(); + break; + } + + case CBCMD_SET_DEPTH_FEATHERING_CONST: + { + int nConst = GetData( pCmdBuf + sizeof( int ) ); + float fDepthBlendScale = GetData( pCmdBuf + 2 * sizeof( int ) ); + pCmdBuf += 2 * sizeof( int ) + sizeof( float ); + SetDepthFeatheringPixelShaderConstant( nConst, fDepthBlendScale ); + break; + } + + case CBCMD_SET_VERTEX_SHADER_FLOAT_CONST: + { + int nStartConst = GetData( pCmdBuf + sizeof( int ) ); + int nNumConsts = GetData( pCmdBuf + 2 * sizeof( int ) ); + float const *pValues = reinterpret_cast< float const *> ( pCmdBuf + 3 * sizeof( int ) ); + pCmdBuf += nNumConsts * 4 * sizeof( float ) + 3 * sizeof( int ); + SetVertexShaderConstantInternal( nStartConst, pValues, nNumConsts, false ); + break; + } + + case CBCMD_BIND_STANDARD_TEXTURE: + { + int nSampler = GetData( pCmdBuf + sizeof( int ) ); + int nTextureID = GetData( pCmdBuf + 2 * sizeof( int ) ); + pCmdBuf += 3 * sizeof( int ); + if ( m_StdTextureHandles[nTextureID] != INVALID_SHADERAPI_TEXTURE_HANDLE ) + { + BindTexture( (Sampler_t) nSampler, m_StdTextureHandles[nTextureID] ); + } + else + { + ShaderUtil()->BindStandardTexture( (Sampler_t) nSampler, (StandardTextureId_t ) nTextureID ); + } + break; + } + + case CBCMD_BIND_SHADERAPI_TEXTURE_HANDLE: + { + int nSampler = GetData( pCmdBuf + sizeof( int ) ); + ShaderAPITextureHandle_t hTexture = GetData( pCmdBuf + 2 * sizeof( int ) ); + Assert( hTexture != INVALID_SHADERAPI_TEXTURE_HANDLE ); + pCmdBuf += 2 * sizeof( int ) + sizeof( ShaderAPITextureHandle_t ); + BindTexture( (Sampler_t) nSampler, hTexture ); + break; + } + + case CBCMD_SET_PSHINDEX: + { + int nIdx = GetData( pCmdBuf + sizeof( int ) ); + ShaderManager()->SetPixelShaderIndex( nIdx ); + pCmdBuf += 2 * sizeof( int ); + break; + } + + case CBCMD_SET_VSHINDEX: + { + int nIdx = GetData( pCmdBuf + sizeof( int ) ); + ShaderManager()->SetVertexShaderIndex( nIdx ); + pCmdBuf += 2 * sizeof( int ); + break; + } + + + +#ifndef NDEBUG + default: + { + Assert(0); + } +#endif + + + } + pLastCmd = pCmd; + } +} + + +//----------------------------------------------------------------------------- +// Sets the boolean registers for pixel shader control flow +//----------------------------------------------------------------------------- +void CShaderAPIDx8::SetBooleanPixelShaderConstant( int var, int const* pVec, int numBools, bool bForce ) +{ + Assert( pVec ); + Assert( var + numBools <= g_pHardwareConfig->NumBooleanPixelShaderConstants() ); + + if ( IsPC() && !bForce && memcmp( pVec, &m_DesiredState.m_pBooleanPixelShaderConstant[var], numBools * sizeof( BOOL ) ) == 0 ) + { + return; + } + + if ( IsPC() ) + { + Dx9Device()->SetPixelShaderConstantB( var, pVec, numBools ); + memcpy( &m_DynamicState.m_pBooleanPixelShaderConstant[var], pVec, numBools * sizeof(BOOL) ); + } + + memcpy( &m_DesiredState.m_pBooleanPixelShaderConstant[var], pVec, numBools * sizeof(BOOL) ); + + if ( IsX360() && var + numBools > m_MaxBooleanPixelShaderConstant ) + { + m_MaxBooleanPixelShaderConstant = var + numBools; + Assert( m_MaxBooleanPixelShaderConstant <= 16 ); + } +} + + +//----------------------------------------------------------------------------- +// Sets the integer registers for pixel shader control flow +//----------------------------------------------------------------------------- +void CShaderAPIDx8::SetIntegerPixelShaderConstant( int var, int const* pVec, int numIntVecs, bool bForce ) +{ + Assert( pVec ); + Assert( var + numIntVecs <= g_pHardwareConfig->NumIntegerPixelShaderConstants() ); + + if ( IsPC() && !bForce && memcmp( pVec, &m_DesiredState.m_pIntegerPixelShaderConstant[var], numIntVecs * sizeof( IntVector4D ) ) == 0 ) + { + return; + } + + if ( IsPC() ) + { + Dx9Device()->SetPixelShaderConstantI( var, pVec, numIntVecs ); + memcpy( &m_DynamicState.m_pIntegerPixelShaderConstant[var], pVec, numIntVecs * sizeof(IntVector4D) ); + } + + memcpy( &m_DesiredState.m_pIntegerPixelShaderConstant[var], pVec, numIntVecs * sizeof(IntVector4D) ); + + if ( IsX360() && var + numIntVecs > m_MaxIntegerPixelShaderConstant ) + { + m_MaxIntegerPixelShaderConstant = var + numIntVecs; + Assert( m_MaxBooleanPixelShaderConstant <= 16 ); + } +} + + +void CShaderAPIDx8::InvalidateDelayedShaderConstants( void ) +{ + m_DelayedShaderConstants.Invalidate(); +} + +//----------------------------------------------------------------------------- +// +// Methods dealing with texture stage state +// +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +// Gets the texture associated with a texture state... +//----------------------------------------------------------------------------- + +inline IDirect3DBaseTexture* CShaderAPIDx8::GetD3DTexture( ShaderAPITextureHandle_t hTexture ) +{ + if ( hTexture == INVALID_SHADERAPI_TEXTURE_HANDLE ) + { + return NULL; + } + + AssertValidTextureHandle( hTexture ); + + Texture_t& tex = GetTexture( hTexture ); + if ( tex.m_NumCopies == 1 ) + { + return tex.GetTexture(); + } + else + { + return tex.GetTexture( tex.m_CurrentCopy ); + } +} + +//----------------------------------------------------------------------------- +// Inline methods +//----------------------------------------------------------------------------- +inline ShaderAPITextureHandle_t CShaderAPIDx8::GetModifyTextureHandle() const +{ + return m_ModifyTextureHandle; +} + +inline IDirect3DBaseTexture* CShaderAPIDx8::GetModifyTexture() +{ + return CShaderAPIDx8::GetD3DTexture( m_ModifyTextureHandle ); +} + +void CShaderAPIDx8::SetModifyTexture( IDirect3DBaseTexture* pTex ) +{ + if ( m_ModifyTextureHandle == INVALID_SHADERAPI_TEXTURE_HANDLE ) + return; + + Texture_t& tex = GetTexture( m_ModifyTextureHandle ); + if ( tex.m_NumCopies == 1 ) + { + tex.SetTexture( pTex ); + } + else + { + tex.SetTexture( tex.m_CurrentCopy, pTex ); + } +} + +inline ShaderAPITextureHandle_t CShaderAPIDx8::GetBoundTextureBindId( Sampler_t sampler ) const +{ + return SamplerState( sampler ).m_BoundTexture; +} + +inline bool CShaderAPIDx8::WouldBeOverTextureLimit( ShaderAPITextureHandle_t hTexture ) +{ + if ( IsPC() ) + { + if ( mat_texture_limit.GetInt() < 0 ) + return false; + + Texture_t &tex = GetTexture( hTexture ); + if ( tex.m_LastBoundFrame == m_CurrentFrame ) + return false; + + return m_nTextureMemoryUsedLastFrame + tex.GetMemUsage() > (mat_texture_limit.GetInt() * 1024); + } + return false; +} + + +#define SETSAMPLESTATEANDMIRROR( sampler, samplerState, state_type, mirror_field, value ) \ + if ( samplerState.mirror_field != value ) \ + { \ + samplerState.mirror_field = value; \ + ::SetSamplerState( g_pD3DDevice, sampler, state_type, value ); \ + } + +#define SETSAMPLESTATEANDMIRROR_FLOAT( sampler, samplerState, state_type, mirror_field, value ) \ + if ( samplerState.mirror_field != value ) \ + { \ + samplerState.mirror_field = value; \ + ::SetSamplerState( g_pD3DDevice, sampler, state_type, *(DWORD*)&value ); \ + } + + +//----------------------------------------------------------------------------- +// Sets state on the board related to the texture state +//----------------------------------------------------------------------------- +void CShaderAPIDx8::SetTextureState( Sampler_t sampler, ShaderAPITextureHandle_t hTexture, bool force ) +{ + // Get the dynamic texture info + SamplerState_t &samplerState = SamplerState( sampler ); + + // Set the texture state, but only if it changes + if ( ( samplerState.m_BoundTexture == hTexture ) && !force ) + return; + + // Disabling texturing + if ( hTexture == INVALID_SHADERAPI_TEXTURE_HANDLE || WouldBeOverTextureLimit( hTexture ) ) + { + Dx9Device()->SetTexture( sampler, 0 ); + return; + } + + samplerState.m_BoundTexture = hTexture; + + // Don't set this if we're disabled + if ( !samplerState.m_TextureEnable ) + return; + + RECORD_COMMAND( DX8_SET_TEXTURE, 3 ); + RECORD_INT( stage ); + RECORD_INT( hTexture ); + RECORD_INT( GetTexture( hTexture).m_CurrentCopy ); + + IDirect3DBaseTexture *pTexture = CShaderAPIDx8::GetD3DTexture( hTexture ); + +#if defined( _X360 ) + DWORD linearFormatBackup = pTexture->Format.dword[0]; + if ( samplerState.m_SRGBReadEnable ) + { + // convert to srgb format for the bind. This effectively emulates the old srgb read sampler state + pTexture->Format.SignX = + pTexture->Format.SignY = + pTexture->Format.SignZ = 3; + } +#endif + + Dx9Device()->SetTexture( sampler, pTexture ); + +#if defined( _X360 ) + // put the format back in linear space + pTexture->Format.dword[0] = linearFormatBackup; +#endif + + Texture_t &tex = GetTexture( hTexture ); + if ( tex.m_LastBoundFrame != m_CurrentFrame ) + { + tex.m_LastBoundFrame = m_CurrentFrame; + tex.m_nTimesBoundThisFrame = 0; + + if ( tex.m_pTextureGroupCounterFrame ) + { + // Update the per-frame texture group counter. + *tex.m_pTextureGroupCounterFrame += tex.GetMemUsage(); + } + + // Track memory usage. + m_nTextureMemoryUsedLastFrame += tex.GetMemUsage(); + } + + if ( !m_bDebugTexturesRendering ) + ++tex.m_nTimesBoundThisFrame; + + tex.m_nTimesBoundMax = MAX( tex.m_nTimesBoundMax, tex.m_nTimesBoundThisFrame ); + + static MaterialSystem_Config_t &materialSystemConfig = ShaderUtil()->GetConfig(); + + D3DTEXTUREFILTERTYPE minFilter = tex.m_MinFilter; + D3DTEXTUREFILTERTYPE magFilter = tex.m_MagFilter; + D3DTEXTUREFILTERTYPE mipFilter = tex.m_MipFilter; + int finestMipmapLevel = tex.m_FinestMipmapLevel; + float lodBias = tex.m_LodBias; + + if ( materialSystemConfig.bMipMapTextures == 0 ) + { + mipFilter = D3DTEXF_NONE; + } + + if ( materialSystemConfig.bFilterTextures == 0 && tex.m_NumLevels > 1 ) + { + minFilter = D3DTEXF_NONE; + magFilter = D3DTEXF_NONE; + mipFilter = D3DTEXF_POINT; + } + + D3DTEXTUREADDRESS uTexWrap = tex.m_UTexWrap; + D3DTEXTUREADDRESS vTexWrap = tex.m_VTexWrap; + D3DTEXTUREADDRESS wTexWrap = tex.m_WTexWrap; + + // For now do this the old way on OSX since the dxabstract layer doesn't support SetSamplerStates + // ###OSX### punting on OSX for now +#if DX_TO_GL_ABSTRACTION && !OSX + if ( ( samplerState.m_MinFilter != minFilter ) || ( samplerState.m_MagFilter != magFilter ) || ( samplerState.m_MipFilter != mipFilter ) || + ( samplerState.m_UTexWrap != uTexWrap ) || ( samplerState.m_VTexWrap != vTexWrap ) || ( samplerState.m_WTexWrap != wTexWrap ) || + ( samplerState.m_FinestMipmapLevel != finestMipmapLevel ) || ( samplerState.m_LodBias != lodBias ) ) + { + samplerState.m_UTexWrap = uTexWrap; + samplerState.m_VTexWrap = vTexWrap; + samplerState.m_WTexWrap = wTexWrap; + samplerState.m_MinFilter = minFilter; + samplerState.m_MagFilter = magFilter; + samplerState.m_MipFilter = mipFilter; + samplerState.m_FinestMipmapLevel = finestMipmapLevel; + samplerState.m_LodBias = lodBias; + Dx9Device()->SetSamplerStates( sampler, uTexWrap, vTexWrap, wTexWrap, minFilter, magFilter, mipFilter, finestMipmapLevel, lodBias ); + } +#else + SETSAMPLESTATEANDMIRROR( sampler, samplerState, D3DSAMP_ADDRESSU, m_UTexWrap, uTexWrap ); + SETSAMPLESTATEANDMIRROR( sampler, samplerState, D3DSAMP_ADDRESSV, m_VTexWrap, vTexWrap ); + SETSAMPLESTATEANDMIRROR( sampler, samplerState, D3DSAMP_ADDRESSW, m_WTexWrap, wTexWrap ); + SETSAMPLESTATEANDMIRROR( sampler, samplerState, D3DSAMP_MINFILTER, m_MinFilter, minFilter ); + SETSAMPLESTATEANDMIRROR( sampler, samplerState, D3DSAMP_MAGFILTER, m_MagFilter, magFilter ); + SETSAMPLESTATEANDMIRROR( sampler, samplerState, D3DSAMP_MIPFILTER, m_MipFilter, mipFilter ); + SETSAMPLESTATEANDMIRROR( sampler, samplerState, D3DSAMP_MAXMIPLEVEL, m_FinestMipmapLevel, finestMipmapLevel ); + SETSAMPLESTATEANDMIRROR_FLOAT( sampler, samplerState, D3DSAMP_MIPMAPLODBIAS, m_LodBias, lodBias ); + +#endif +} + +void CShaderAPIDx8::BindTexture( Sampler_t sampler, ShaderAPITextureHandle_t textureHandle ) +{ + LOCK_SHADERAPI(); + SetTextureState( sampler, textureHandle ); +} + +void CShaderAPIDx8::BindVertexTexture( VertexTextureSampler_t nStage, ShaderAPITextureHandle_t textureHandle ) +{ + Assert( g_pMaterialSystemHardwareConfig->GetVertexTextureCount() != 0 ); + LOCK_SHADERAPI(); + ADD_VERTEX_TEXTURE_FUNC( COMMIT_PER_PASS, COMMIT_VERTEX_SHADER, + CommitVertexTextures, nStage, m_BoundTexture, textureHandle ); +} + + +//----------------------------------------------------------------------------- +// Texture allocation/deallocation +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +// Computes stats info for a texture +//----------------------------------------------------------------------------- +void CShaderAPIDx8::ComputeStatsInfo( ShaderAPITextureHandle_t hTexture, bool isCubeMap, bool isVolumeTexture ) +{ + Texture_t &textureData = GetTexture( hTexture ); + + textureData.m_SizeBytes = 0; + textureData.m_SizeTexels = 0; + textureData.m_LastBoundFrame = -1; + if ( IsX360() ) + { + textureData.m_nTimesBoundThisFrame = 0; + } + + IDirect3DBaseTexture* pD3DTex = CShaderAPIDx8::GetD3DTexture( hTexture ); + + if ( IsPC() || !IsX360() ) + { + if ( isCubeMap ) + { + IDirect3DCubeTexture* pTex = static_cast(pD3DTex); + if ( !pTex ) + { + Assert( 0 ); + return; + } + + int numLevels = pTex->GetLevelCount(); + for (int i = 0; i < numLevels; ++i) + { + D3DSURFACE_DESC desc; + HRESULT hr = pTex->GetLevelDesc( i, &desc ); + Assert( !FAILED(hr) ); + textureData.m_SizeBytes += 6 * ImageLoader::GetMemRequired( desc.Width, desc.Height, 1, textureData.GetImageFormat(), false ); + textureData.m_SizeTexels += 6 * desc.Width * desc.Height; + } + } + else if ( isVolumeTexture ) + { + IDirect3DVolumeTexture9* pTex = static_cast(pD3DTex); + if ( !pTex ) + { + Assert( 0 ); + return; + } + int numLevels = pTex->GetLevelCount(); + for (int i = 0; i < numLevels; ++i) + { + D3DVOLUME_DESC desc; + HRESULT hr = pTex->GetLevelDesc( i, &desc ); + Assert( !FAILED( hr ) ); + textureData.m_SizeBytes += ImageLoader::GetMemRequired( desc.Width, desc.Height, desc.Depth, textureData.GetImageFormat(), false ); + textureData.m_SizeTexels += desc.Width * desc.Height; + } + } + else + { + IDirect3DTexture* pTex = static_cast(pD3DTex); + if ( !pTex ) + { + Assert( 0 ); + return; + } + + int numLevels = pTex->GetLevelCount(); + for (int i = 0; i < numLevels; ++i) + { + D3DSURFACE_DESC desc; + HRESULT hr = pTex->GetLevelDesc( i, &desc ); + Assert( !FAILED( hr ) ); + textureData.m_SizeBytes += ImageLoader::GetMemRequired( desc.Width, desc.Height, 1, textureData.GetImageFormat(), false ); + textureData.m_SizeTexels += desc.Width * desc.Height; + } + } + } + +#if defined( _X360 ) + // 360 uses gpu storage size (which accounts for page alignment bloat), not format size + textureData.m_SizeBytes = g_TextureHeap.GetSize( pD3DTex ); +#endif +} + +static D3DFORMAT ComputeFormat( IDirect3DBaseTexture* pTexture, bool isCubeMap ) +{ + Assert( pTexture ); + D3DSURFACE_DESC desc; + if (isCubeMap) + { + IDirect3DCubeTexture* pTex = static_cast(pTexture); + HRESULT hr = pTex->GetLevelDesc( 0, &desc ); + Assert( !FAILED(hr) ); + } + else + { + IDirect3DTexture* pTex = static_cast(pTexture); + HRESULT hr = pTex->GetLevelDesc( 0, &desc ); + Assert( !FAILED(hr) ); + } + return desc.Format; +} + +ShaderAPITextureHandle_t CShaderAPIDx8::CreateDepthTexture( + ImageFormat renderTargetFormat, + int width, + int height, + const char *pDebugName, + bool bTexture ) +{ + LOCK_SHADERAPI(); + + ShaderAPITextureHandle_t i = CreateTextureHandle(); + Texture_t *pTexture = &GetTexture( i ); + + pTexture->m_Flags = Texture_t::IS_ALLOCATED; + if( bTexture ) + { + pTexture->m_Flags |= Texture_t::IS_DEPTH_STENCIL_TEXTURE; + } + else + { + pTexture->m_Flags |= Texture_t::IS_DEPTH_STENCIL; + } + + pTexture->m_DebugName = pDebugName; + pTexture->m_Width = width; + pTexture->m_Height = height; + pTexture->m_Depth = 1; // fake + pTexture->m_Count = 1; // created single texture + pTexture->m_CountIndex = 0; // created single texture + pTexture->m_CreationFlags = 0; // fake + pTexture->m_NumCopies = 1; + pTexture->m_CurrentCopy = 0; + + ImageFormat renderFormat = FindNearestSupportedFormat( renderTargetFormat, false, true, false ); +#if defined( _X360 ) + D3DFORMAT nDepthFormat = ReverseDepthOnX360() ? D3DFMT_D24FS8 : D3DFMT_D24S8; +#else + D3DFORMAT nDepthFormat = m_bUsingStencil ? D3DFMT_D24S8 : D3DFMT_D24X8; +#endif + D3DFORMAT format = FindNearestSupportedDepthFormat( m_nAdapter, m_AdapterFormat, renderFormat, nDepthFormat ); + D3DMULTISAMPLE_TYPE multisampleType = D3DMULTISAMPLE_NONE; + + pTexture->m_NumLevels = 1; + pTexture->m_SizeTexels = width * height; + pTexture->m_SizeBytes = ImageLoader::GetMemRequired( width, height, 1, renderFormat, false ); + + RECORD_COMMAND( DX8_CREATE_DEPTH_TEXTURE, 5 ); + RECORD_INT( i ); + RECORD_INT( width ); + RECORD_INT( height ); + RECORD_INT( format ); + RECORD_INT( multisampleType ); + + HRESULT hr; + if ( !bTexture ) + { +#if defined( _X360 ) + int backWidth, backHeight; + ShaderAPI()->GetBackBufferDimensions( backWidth, backHeight ); + D3DFORMAT backBufferFormat = ImageLoader::ImageFormatToD3DFormat( g_pShaderDevice->GetBackBufferFormat() ); + // immediately follows back buffer in EDRAM + D3DSURFACE_PARAMETERS surfParameters; + surfParameters.Base = 2*XGSurfaceSize( backWidth, backHeight, backBufferFormat, D3DMULTISAMPLE_NONE ); + surfParameters.ColorExpBias = 0; + surfParameters.HierarchicalZBase = 0; + hr = Dx9Device()->CreateDepthStencilSurface( + width, height, format, multisampleType, 0, TRUE, &pTexture->GetDepthStencilSurface(), &surfParameters ); +#else + hr = Dx9Device()->CreateDepthStencilSurface( + width, height, format, multisampleType, 0, TRUE, &pTexture->GetDepthStencilSurface(), NULL ); +#endif + } + else + { + IDirect3DTexture9 *pTex; + hr = Dx9Device()->CreateTexture( width, height, 1, D3DUSAGE_DEPTHSTENCIL, format, D3DPOOL_DEFAULT, &pTex, NULL ); + pTexture->SetTexture( pTex ); + } + + if ( FAILED( hr ) ) + { + switch( hr ) + { + case D3DERR_INVALIDCALL: + Warning( "ShaderAPIDX8::CreateDepthStencilSurface: D3DERR_INVALIDCALL\n" ); + break; + case D3DERR_OUTOFVIDEOMEMORY: + Warning( "ShaderAPIDX8::CreateDepthStencilSurface: D3DERR_OUTOFVIDEOMEMORY\n" ); + break; + default: + break; + } + Assert( 0 ); + } + +#ifdef _XBOX + D3DSURFACE_DESC desc; + hr = pTexture->GetDepthStencilSurface()->GetDesc( &desc ); + Assert( !FAILED( hr ) ); + + pTexture->m_nTimesBoundThisFrame = 0; + pTexture->m_StaticSizeBytes = desc.Size; + pTexture->m_DynamicSizeBytes = 0; + pTexture->m_DebugName = pDebugName; + pTexture->m_TextureGroupName = TEXTURE_GROUP_RENDER_TARGET; + pTexture->SetImageFormat( IMAGE_FORMAT_UNKNOWN ); +#endif + + return i; +} + + +// FIXME!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +// Could keep a free-list for this instead of linearly searching. We +// don't create textures all the time, so this is probably fine for now. +// FIXME!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +ShaderAPITextureHandle_t CShaderAPIDx8::CreateTextureHandle( void ) +{ + ShaderAPITextureHandle_t handle; + CreateTextureHandles( &handle, 1 ); + return handle; +} + +void CShaderAPIDx8::CreateTextureHandles( ShaderAPITextureHandle_t *handles, int count ) +{ + if ( count <= 0 ) + return; + + MEM_ALLOC_CREDIT(); + + int idxCreating = 0; + ShaderAPITextureHandle_t hTexture; + + { + tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s - Search", __FUNCTION__ ); + + for ( hTexture = m_Textures.Head(); hTexture != m_Textures.InvalidIndex(); hTexture = m_Textures.Next( hTexture ) ) + { + if ( !( m_Textures[hTexture].m_Flags & Texture_t::IS_ALLOCATED ) ) + { + handles[ idxCreating ++ ] = hTexture; + if ( idxCreating >= count ) + return; + } + } + } + + tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s - Add", __FUNCTION__ ); + while ( idxCreating < count ) + handles[ idxCreating ++ ] = m_Textures.AddToTail(); +} + +//----------------------------------------------------------------------------- +// Creates a lovely texture +//----------------------------------------------------------------------------- + +ShaderAPITextureHandle_t CShaderAPIDx8::CreateTexture( + int width, + int height, + int depth, + ImageFormat dstImageFormat, + int numMipLevels, + int numCopies, + int creationFlags, + const char *pDebugName, + const char *pTextureGroupName ) +{ + ShaderAPITextureHandle_t handle = 0; + CreateTextures( &handle, 1, width, height, depth, dstImageFormat, numMipLevels, numCopies, creationFlags, pDebugName, pTextureGroupName ); + return handle; +} + +void CShaderAPIDx8::CreateTextures( + ShaderAPITextureHandle_t *pHandles, + int count, + int width, + int height, + int depth, + ImageFormat dstImageFormat, + int numMipLevels, + int numCopies, + int creationFlags, + const char *pDebugName, + const char *pTextureGroupName ) +{ + LOCK_SHADERAPI(); + + tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s - PostLock", __FUNCTION__ ); + + Assert( this == g_pShaderAPI ); + + if ( depth == 0 ) + { + depth = 1; + } + + bool isCubeMap = (creationFlags & TEXTURE_CREATE_CUBEMAP) != 0; + bool isRenderTarget = (creationFlags & TEXTURE_CREATE_RENDERTARGET) != 0; + bool managed = (creationFlags & TEXTURE_CREATE_MANAGED) != 0; + bool isDepthBuffer = (creationFlags & TEXTURE_CREATE_DEPTHBUFFER) != 0; + bool isDynamic = (creationFlags & TEXTURE_CREATE_DYNAMIC) != 0; + bool isSRGB = (creationFlags & TEXTURE_CREATE_SRGB) != 0; // for Posix/GL only... not used here ? + +#if defined(IS_WINDOWS_PC) && defined(SHADERAPIDX9) + extern bool g_ShaderDeviceUsingD3D9Ex; + if ( g_ShaderDeviceUsingD3D9Ex && managed ) + { + // Managed textures aren't available under D3D9Ex, but we never lose + // texture data, so it's ok to use the default pool. Really. We can't + // lock default-pool textures like we normally would to upload, but we + // have special logic to blit full updates via D3DX helper functions + // in D3D9Ex mode (see texturedx8.cpp) + managed = false; + creationFlags &= ~TEXTURE_CREATE_MANAGED; + } +#endif + + // Can't be both managed + dynamic. Dynamic is an optimization, but + // if it's not managed, then we gotta do special client-specific stuff + // So, managed wins out! + if ( managed ) + { + creationFlags &= ~TEXTURE_CREATE_DYNAMIC; + isDynamic = false; + } + + + + // Create a set of texture handles + CreateTextureHandles( pHandles, count ); + Texture_t **arrTxp = ( Texture_t ** ) stackalloc( count * sizeof( Texture_t * ) ); + + unsigned short usSetFlags = 0; + usSetFlags |= ( IsPosix() || ( creationFlags & (TEXTURE_CREATE_DYNAMIC | TEXTURE_CREATE_MANAGED) ) ) ? Texture_t::IS_LOCKABLE : 0; + usSetFlags |= ( creationFlags & TEXTURE_CREATE_VERTEXTEXTURE) ? Texture_t::IS_VERTEX_TEXTURE : 0; +#if defined( _X360 ) + usSetFlags |= ( creationFlags & TEXTURE_CREATE_RENDERTARGET ) ? Texture_t::IS_RENDER_TARGET : 0; + usSetFlags |= ( creationFlags & TEXTURE_CREATE_CANCONVERTFORMAT ) ? Texture_t::CAN_CONVERT_FORMAT : 0; +#endif + + tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s - CreateFrames", __FUNCTION__ ); + + for ( int idxFrame = 0; idxFrame < count; ++ idxFrame ) + { + arrTxp[ idxFrame ] = &GetTexture( pHandles[ idxFrame ] ); + Texture_t *pTexture = arrTxp[ idxFrame ]; + pTexture->m_Flags = Texture_t::IS_ALLOCATED; + pTexture->m_DebugName = pDebugName; + pTexture->m_Width = width; + pTexture->m_Height = height; + pTexture->m_Depth = depth; + pTexture->m_Count = count; + pTexture->m_CountIndex = idxFrame; + + pTexture->m_CreationFlags = creationFlags; + pTexture->m_Flags |= usSetFlags; + + RECORD_COMMAND( DX8_CREATE_TEXTURE, 12 ); + RECORD_INT( textureHandle ); + RECORD_INT( width ); + RECORD_INT( height ); + RECORD_INT( depth ); // depth for volume textures + RECORD_INT( ImageLoader::ImageFormatToD3DFormat( FindNearestSupportedFormat(dstImageFormat)) ); + RECORD_INT( numMipLevels ); + RECORD_INT( isCubeMap ); + RECORD_INT( numCopies <= 1 ? 1 : numCopies ); + RECORD_INT( isRenderTarget ? 1 : 0 ); + RECORD_INT( managed ); + RECORD_INT( isDepthBuffer ? 1 : 0 ); + RECORD_INT( isDynamic ? 1 : 0 ); + + IDirect3DBaseTexture* pD3DTex; + + // Set the initial texture state + if ( numCopies <= 1 ) + { + tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s - CreateD3DTexture", __FUNCTION__ ); + + pTexture->m_NumCopies = 1; + pD3DTex = CreateD3DTexture( width, height, depth, dstImageFormat, numMipLevels, creationFlags, (char*)pDebugName ); + pTexture->SetTexture( pD3DTex ); + } + else + { + tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s - CreateD3DTexture", __FUNCTION__ ); + + pTexture->m_NumCopies = numCopies; + { +// X360TEMP +// MEM_ALLOC_CREDIT(); + pTexture->GetTextureArray() = new IDirect3DBaseTexture* [numCopies]; + } + for (int k = 0; k < numCopies; ++k) + { + pD3DTex = CreateD3DTexture( width, height, depth, dstImageFormat, numMipLevels, creationFlags, (char*)pDebugName ); + pTexture->SetTexture( k, pD3DTex ); + } + } + pTexture->m_CurrentCopy = 0; + + pD3DTex = CShaderAPIDx8::GetD3DTexture( pHandles[ idxFrame ] ); + +#if defined( _X360 ) + if ( pD3DTex ) + { + D3DSURFACE_DESC desc; + HRESULT hr; + if ( creationFlags & TEXTURE_CREATE_CUBEMAP ) + { + hr = ((IDirect3DCubeTexture *)pD3DTex)->GetLevelDesc( 0, &desc ); + } + else + { + hr = ((IDirect3DTexture *)pD3DTex)->GetLevelDesc( 0, &desc ); + } + Assert( !FAILED( hr ) ); + + // for proper info get the actual format because the input format may have been redirected + dstImageFormat = ImageLoader::D3DFormatToImageFormat( desc.Format ); + Assert( dstImageFormat != IMAGE_FORMAT_UNKNOWN ); + + // track linear or tiled + if ( !XGIsTiledFormat( desc.Format ) ) + { + pTexture->m_Flags |= Texture_t::IS_LINEAR; + } + } +#endif + + pTexture->SetImageFormat( dstImageFormat ); + pTexture->m_UTexWrap = D3DTADDRESS_CLAMP; + pTexture->m_VTexWrap = D3DTADDRESS_CLAMP; + pTexture->m_WTexWrap = D3DTADDRESS_CLAMP; + + if ( isRenderTarget ) + { +#if !defined( _X360 ) + if ( ( dstImageFormat == IMAGE_FORMAT_NV_INTZ ) || ( dstImageFormat == IMAGE_FORMAT_NV_RAWZ ) || + ( dstImageFormat == IMAGE_FORMAT_ATI_DST16 ) || ( dstImageFormat == IMAGE_FORMAT_ATI_DST24 ) ) + { + pTexture->m_MinFilter = pTexture->m_MagFilter = D3DTEXF_POINT; + } + else +#endif + { + pTexture->m_MinFilter = pTexture->m_MagFilter = D3DTEXF_LINEAR; + } + + pTexture->m_NumLevels = 1; + pTexture->m_MipFilter = D3DTEXF_NONE; + } + else + { + pTexture->m_NumLevels = pD3DTex ? pD3DTex->GetLevelCount() : 1; + pTexture->m_MipFilter = (pTexture->m_NumLevels != 1) ? D3DTEXF_LINEAR : D3DTEXF_NONE; + pTexture->m_MinFilter = pTexture->m_MagFilter = D3DTEXF_LINEAR; + } + pTexture->m_SwitchNeeded = false; + + ComputeStatsInfo( pHandles[idxFrame], isCubeMap, (depth > 1) ); + SetupTextureGroup( pHandles[idxFrame], pTextureGroupName ); + } +} + +void CShaderAPIDx8::SetupTextureGroup( ShaderAPITextureHandle_t hTexture, const char *pTextureGroupName ) +{ + Texture_t *pTexture = &GetTexture( hTexture ); + + Assert( !pTexture->m_pTextureGroupCounterGlobal ); + + // Setup the texture group stuff. + if ( pTextureGroupName && pTextureGroupName[0] != 0 ) + { + pTexture->m_TextureGroupName = pTextureGroupName; + } + else + { + pTexture->m_TextureGroupName = TEXTURE_GROUP_UNACCOUNTED; + } + + // 360 cannot vprof due to multicore loading until vprof is reentrant and these counters are real. +#if defined( VPROF_ENABLED ) && !defined( _X360 ) + char counterName[256]; + Q_snprintf( counterName, sizeof( counterName ), "TexGroup_global_%s", pTexture->m_TextureGroupName.String() ); + pTexture->m_pTextureGroupCounterGlobal = g_VProfCurrentProfile.FindOrCreateCounter( counterName, COUNTER_GROUP_TEXTURE_GLOBAL ); + + Q_snprintf( counterName, sizeof( counterName ), "TexGroup_frame_%s", pTexture->m_TextureGroupName.String() ); + pTexture->m_pTextureGroupCounterFrame = g_VProfCurrentProfile.FindOrCreateCounter( counterName, COUNTER_GROUP_TEXTURE_PER_FRAME ); +#else + pTexture->m_pTextureGroupCounterGlobal = NULL; + pTexture->m_pTextureGroupCounterFrame = NULL; +#endif + + if ( pTexture->m_pTextureGroupCounterGlobal ) + { + *pTexture->m_pTextureGroupCounterGlobal += pTexture->GetMemUsage(); + } +} + +//----------------------------------------------------------------------------- +// Deletes a texture... +//----------------------------------------------------------------------------- +void CShaderAPIDx8::DeleteD3DTexture( ShaderAPITextureHandle_t hTexture ) +{ + int numDeallocated = 0; + Texture_t &texture = GetTexture( hTexture ); + + if ( texture.m_Flags & Texture_t::IS_DEPTH_STENCIL ) + { + // garymcthack - need to make sure that playback knows how to deal with these. + RECORD_COMMAND( DX8_DESTROY_DEPTH_TEXTURE, 1 ); + RECORD_INT( hTexture ); + + if ( texture.GetDepthStencilSurface() ) + { + int nRetVal = texture.GetDepthStencilSurface()->Release(); + Assert( nRetVal == 0 ); + texture.GetDepthStencilSurface() = 0; + numDeallocated = 1; + } + else + { + // FIXME: we hit this on shutdown of HLMV on some machines + Assert( 0 ); + } + } + else if ( texture.m_NumCopies == 1 ) + { + if ( texture.GetTexture() ) + { + RECORD_COMMAND( DX8_DESTROY_TEXTURE, 1 ); + RECORD_INT( hTexture ); + + DestroyD3DTexture( texture.GetTexture() ); + texture.SetTexture( 0 ); + numDeallocated = 1; + } + } + else + { + if ( texture.GetTextureArray() ) + { + RECORD_COMMAND( DX8_DESTROY_TEXTURE, 1 ); + RECORD_INT( hTexture ); + + // Multiple copy texture + for (int j = 0; j < texture.m_NumCopies; ++j) + { + if (texture.GetTexture( j )) + { + DestroyD3DTexture( texture.GetTexture( j ) ); + texture.SetTexture( j, 0 ); + ++numDeallocated; + } + } + + delete [] texture.GetTextureArray(); + texture.GetTextureArray() = 0; + } + } + + texture.m_NumCopies = 0; + + // Remove this texture from its global texture group counter. + if ( texture.m_pTextureGroupCounterGlobal ) + { + *texture.m_pTextureGroupCounterGlobal -= texture.GetMemUsage(); + Assert( *texture.m_pTextureGroupCounterGlobal >= 0 ); + texture.m_pTextureGroupCounterGlobal = NULL; + } + + // remove this texture from std textures + for( int i=0 ; i < ARRAYSIZE( m_StdTextureHandles ) ; i++ ) + { + if ( m_StdTextureHandles[i] == hTexture ) + m_StdTextureHandles[i] = INVALID_SHADERAPI_TEXTURE_HANDLE; + } + +} + + +//----------------------------------------------------------------------------- +// Unbinds a texture from all texture stages +//----------------------------------------------------------------------------- +void CShaderAPIDx8::UnbindTexture( ShaderAPITextureHandle_t hTexture ) +{ + // Make sure no texture units are currently bound to it... + for ( int unit = 0; unit < g_pHardwareConfig->GetSamplerCount(); ++unit ) + { + if ( hTexture == SamplerState( unit ).m_BoundTexture ) + { + // Gotta set this here because INVALID_SHADERAPI_TEXTURE_HANDLE means don't actually + // set bound texture (it's used for disabling texturemapping) + SamplerState( unit ).m_BoundTexture = INVALID_SHADERAPI_TEXTURE_HANDLE; + SetTextureState( (Sampler_t)unit, INVALID_SHADERAPI_TEXTURE_HANDLE ); + } + } + + int nVertexTextureCount = g_pHardwareConfig->GetVertexTextureCount(); + for ( int nSampler = 0; nSampler < nVertexTextureCount; ++nSampler ) + { + if ( hTexture == m_DynamicState.m_VertexTextureState[ nSampler ].m_BoundTexture ) + { + // Gotta set this here because INVALID_SHADERAPI_TEXTURE_HANDLE means don't actually + // set bound texture (it's used for disabling texturemapping) + BindVertexTexture( (VertexTextureSampler_t)nSampler, INVALID_SHADERAPI_TEXTURE_HANDLE ); + } + } +} + + +//----------------------------------------------------------------------------- +// Deletes a texture... +//----------------------------------------------------------------------------- +void CShaderAPIDx8::DeleteTexture( ShaderAPITextureHandle_t textureHandle ) +{ + LOCK_SHADERAPI(); + AssertValidTextureHandle( textureHandle ); + + if ( !TextureIsAllocated( textureHandle ) ) + { + // already deallocated + return; + } + + // Unbind it! + UnbindTexture( textureHandle ); + + // Delete it baby + DeleteD3DTexture( textureHandle ); + + // Now remove the texture from the list + // Mark as deallocated so that it can be reused. + GetTexture( textureHandle ).m_Flags = 0; +} + + +void CShaderAPIDx8::WriteTextureToFile( ShaderAPITextureHandle_t hTexture, const char *szFileName ) +{ + Texture_t *pTexInt = &GetTexture( hTexture ); + //Assert( pTexInt->IsCubeMap() == false ); + //Assert( pTexInt->IsVolumeTexture() == false ); + IDirect3DTexture *pD3DTexture = (IDirect3DTexture *)pTexInt->GetTexture(); + + // Get the level of the texture we want to read from + IDirect3DSurface* pTextureLevel; + HRESULT hr = pD3DTexture ->GetSurfaceLevel( 0, &pTextureLevel ); + if ( FAILED( hr ) ) + return; + + D3DSURFACE_DESC surfaceDesc; + pD3DTexture->GetLevelDesc( 0, &surfaceDesc ); + + D3DLOCKED_RECT lockedRect; + + + //if( pTexInt->m_Flags & Texture_t::IS_RENDER_TARGET ) +#if !defined( _X360 ) //TODO: x360 version + { + //render targets can't be locked, luckily we can copy the surface to system memory and lock that. + IDirect3DSurface *pSystemSurface; + + Assert( !IsX360() ); + + hr = Dx9Device()->CreateOffscreenPlainSurface( surfaceDesc.Width, surfaceDesc.Height, surfaceDesc.Format, D3DPOOL_SYSTEMMEM, &pSystemSurface, NULL ); + Assert( SUCCEEDED( hr ) ); + + pSystemSurface->GetDesc( &surfaceDesc ); + + hr = Dx9Device()->GetRenderTargetData( pTextureLevel, pSystemSurface ); + Assert( SUCCEEDED( hr ) ); + + //pretend this is the texture level we originally grabbed with GetSurfaceLevel() and continue on + pTextureLevel->Release(); + pTextureLevel = pSystemSurface; + } +#endif + + // lock the region + if ( FAILED( pTextureLevel->LockRect( &lockedRect, NULL, D3DLOCK_READONLY ) ) ) + { + Assert( 0 ); + pTextureLevel->Release(); + return; + } + + TGAWriter::WriteTGAFile( szFileName, surfaceDesc.Width, surfaceDesc.Height, pTexInt->GetImageFormat(), (const uint8 *)lockedRect.pBits, lockedRect.Pitch ); + + if ( FAILED( pTextureLevel->UnlockRect() ) ) + { + Assert( 0 ); + pTextureLevel->Release(); + return; + } + + pTextureLevel->Release(); +} + + +//----------------------------------------------------------------------------- +// Releases all textures +//----------------------------------------------------------------------------- +void CShaderAPIDx8::ReleaseAllTextures() +{ + ClearStdTextureHandles(); + ShaderAPITextureHandle_t hTexture; + for ( hTexture = m_Textures.Head(); hTexture != m_Textures.InvalidIndex(); hTexture = m_Textures.Next( hTexture ) ) + { + if ( TextureIsAllocated( hTexture ) ) + { + // Delete it baby + DeleteD3DTexture( hTexture ); + } + } + + // Make sure all texture units are pointing to nothing + for (int unit = 0; unit < g_pHardwareConfig->GetSamplerCount(); ++unit ) + { + SamplerState( unit ).m_BoundTexture = INVALID_SHADERAPI_TEXTURE_HANDLE; + SetTextureState( (Sampler_t)unit, INVALID_SHADERAPI_TEXTURE_HANDLE ); + } +} + +void CShaderAPIDx8::DeleteAllTextures() +{ + ReleaseAllTextures(); + m_Textures.Purge(); +} + +bool CShaderAPIDx8::IsTexture( ShaderAPITextureHandle_t textureHandle ) +{ + LOCK_SHADERAPI(); + + if ( !TextureIsAllocated( textureHandle ) ) + { + return false; + } + +#if !defined( _X360 ) + if ( GetTexture( textureHandle ).m_Flags & Texture_t::IS_DEPTH_STENCIL ) + { + return GetTexture( textureHandle ).GetDepthStencilSurface() != 0; + } + else if ( ( GetTexture( textureHandle ).m_NumCopies == 1 && GetTexture( textureHandle ).GetTexture() != 0 ) || + ( GetTexture( textureHandle ).m_NumCopies > 1 && GetTexture( textureHandle ).GetTexture( 0 ) != 0 ) ) + { + return true; + } + else + { + return false; + } +#else + // query is about texture handle validity, not presence + // texture handle is allocated, texture may or may not be present + return true; +#endif +} + + + +//----------------------------------------------------------------------------- +// Gets the surface associated with a texture (refcount of surface is increased) +//----------------------------------------------------------------------------- +IDirect3DSurface* CShaderAPIDx8::GetTextureSurface( ShaderAPITextureHandle_t textureHandle ) +{ + MEM_ALLOC_D3D_CREDIT(); + + IDirect3DSurface* pSurface; + + // We'll be modifying this sucka + AssertValidTextureHandle( textureHandle ); + Texture_t &tex = GetTexture( textureHandle ); + if ( !( tex.m_Flags & Texture_t::IS_ALLOCATED ) ) + { + return NULL; + } + + if ( IsX360() && ( tex.m_Flags & Texture_t::IS_RENDER_TARGET_SURFACE ) ) + { + pSurface = tex.GetRenderTargetSurface( false ); + +#if POSIX + // dxabstract's AddRef/Release have optional args to help track usage + pSurface->AddRef( 0, "CShaderAPIDx8::GetTextureSurface public addref"); +#else + pSurface->AddRef(); +#endif + + return pSurface; + } + + IDirect3DBaseTexture* pD3DTex = CShaderAPIDx8::GetD3DTexture( textureHandle ); + IDirect3DTexture* pTex = static_cast( pD3DTex ); + Assert( pTex ); + if ( !pTex ) + { + return NULL; + } + + HRESULT hr = pTex->GetSurfaceLevel( 0, &pSurface ); + Assert( hr == D3D_OK ); + + return pSurface; +} + +//----------------------------------------------------------------------------- +// Gets the surface associated with a texture (refcount of surface is increased) +//----------------------------------------------------------------------------- +IDirect3DSurface* CShaderAPIDx8::GetDepthTextureSurface( ShaderAPITextureHandle_t textureHandle ) +{ + AssertValidTextureHandle( textureHandle ); + if ( !TextureIsAllocated( textureHandle ) ) + { + return NULL; + } + return GetTexture( textureHandle ).GetDepthStencilSurface(); +} + +//----------------------------------------------------------------------------- +// Changes the render target +//----------------------------------------------------------------------------- +void CShaderAPIDx8::SetRenderTargetEx( int nRenderTargetID, ShaderAPITextureHandle_t colorTextureHandle, ShaderAPITextureHandle_t depthTextureHandle ) +{ + LOCK_SHADERAPI(); + if ( IsDeactivated( ) ) + { + return; + } + + // GR - need to flush batched geometry + FlushBufferedPrimitives(); + +#if defined( PIX_INSTRUMENTATION ) + { + const char *pRT = "Backbuffer"; + const char *pDS = "DefaultDepthStencil"; + + if ( colorTextureHandle == SHADER_RENDERTARGET_NONE ) + { + pRT = "None"; + } + else if ( colorTextureHandle != SHADER_RENDERTARGET_BACKBUFFER ) + { + Texture_t &tex = GetTexture( colorTextureHandle ); + pRT = tex.m_DebugName.String(); + } + + if ( depthTextureHandle == SHADER_RENDERTARGET_NONE ) + { + pDS = "None"; + } + else if ( depthTextureHandle != SHADER_RENDERTARGET_DEPTHBUFFER ) + { + Texture_t &tex = GetTexture( depthTextureHandle ); + pDS = tex.m_DebugName.String(); + } + + char buf[256]; + sprintf( buf, "SRT:%s %s", pRT ? pRT : "?", pDS ? pDS : "?" ); + BeginPIXEvent( 0xFFFFFFFF, buf ); + EndPIXEvent(); + } +#endif + +#if !defined( _X360 ) + RECORD_COMMAND( DX8_TEST_COOPERATIVE_LEVEL, 0 ); + HRESULT hr = Dx9Device()->TestCooperativeLevel(); + if ( hr != D3D_OK ) + { + MarkDeviceLost(); + return; + } +#endif + + IDirect3DSurface* pColorSurface = NULL; + IDirect3DSurface* pZSurface = NULL; + + RECORD_COMMAND( DX8_SET_RENDER_TARGET, 3 ); + RECORD_INT( nRenderTargetID ); + RECORD_INT( colorTextureHandle ); + RECORD_INT( depthTextureHandle ); + + // The 0th render target defines which depth buffer we are using, so + // don't bother if we are another render target + if ( nRenderTargetID > 0 ) + { + depthTextureHandle = SHADER_RENDERTARGET_NONE; + } + + // NOTE!!!! If this code changes, also change Dx8SetRenderTarget in playback.cpp + bool usingTextureTarget = false; + if ( colorTextureHandle == SHADER_RENDERTARGET_BACKBUFFER ) + { + pColorSurface = m_pBackBufferSurface; + +#ifdef ENABLE_NULLREF_DEVICE_SUPPORT + if( pColorSurface ) +#endif + { + // This is just to make the code a little simpler... + // (simplifies the release logic) +#if POSIX + // dxabstract's AddRef/Release have optional args to help track usage + pColorSurface->AddRef( 0, "+C CShaderAPIDx8::SetRenderTargetEx public addref 1"); +#else + pColorSurface->AddRef(); +#endif + } + } + else + { + // get the texture (Refcount increases) + UnbindTexture( colorTextureHandle ); + pColorSurface = GetTextureSurface( colorTextureHandle ); + if ( !pColorSurface ) + { + return; + } + + usingTextureTarget = true; + } + + if ( depthTextureHandle == SHADER_RENDERTARGET_DEPTHBUFFER ) + { + // using the default depth buffer + pZSurface = m_pZBufferSurface; + +#ifdef ENABLE_NULLREF_DEVICE_SUPPORT + if( pZSurface ) +#endif + { + // simplify the prologue logic +#if POSIX + // dxabstract's AddRef/Release have optional args to help track usage + pZSurface->AddRef( 0, "+D CShaderAPIDx8::SetRenderTargetEx public addref 1"); +#else + pZSurface->AddRef(); +#endif + } + } + else if ( depthTextureHandle == SHADER_RENDERTARGET_NONE ) + { + // GR - disable depth buffer + pZSurface = NULL; + } + else + { + UnbindTexture( depthTextureHandle ); + + Texture_t &tex = GetTexture( depthTextureHandle ); + + //Cannot use a depth/stencil surface derived from a texture. + //Asserting helps get the whole call stack instead of letting the 360 report an error with a partial stack + Assert( !( IsX360() && (tex.m_Flags & Texture_t::IS_DEPTH_STENCIL_TEXTURE) ) ); + + if ( tex.m_Flags & Texture_t::IS_DEPTH_STENCIL ) + { + pZSurface = GetDepthTextureSurface( depthTextureHandle ); + if ( pZSurface ) + { +#if POSIX + // dxabstract's AddRef/Release have optional args to help track usage + pZSurface->AddRef( 0, "+D CShaderAPIDx8::SetRenderTargetEx public addref 2"); +#else + pZSurface->AddRef(); +#endif + } + } + else + { + HRESULT hr = ((IDirect3DTexture9*)tex.GetTexture())->GetSurfaceLevel( 0, &pZSurface ); + } + + if ( !pZSurface ) + { + // Refcount of color surface was increased above +#if POSIX + // dxabstract's AddRef/Release have optional args to help track usage + pColorSurface->Release( 0, "-C CShaderAPIDx8::SetRenderTargetEx public release 1" ); +#else + pColorSurface->Release(); +#endif + return; + } + usingTextureTarget = true; + } + +#ifdef _DEBUG + if ( pZSurface ) + { + D3DSURFACE_DESC zSurfaceDesc, colorSurfaceDesc; + pZSurface->GetDesc( &zSurfaceDesc ); + pColorSurface->GetDesc( &colorSurfaceDesc ); + + if ( !HushAsserts() ) + { + Assert( colorSurfaceDesc.Width <= zSurfaceDesc.Width ); + Assert( colorSurfaceDesc.Height <= zSurfaceDesc.Height ); + } + } +#endif + + // we only set this flag for the 0th render target so that NULL + // render targets 1-3 don't mess with the viewport on the main RT + if( nRenderTargetID == 0 ) + m_UsingTextureRenderTarget = usingTextureTarget; + + // NOTE: The documentation says that SetRenderTarget increases the refcount + // but it doesn't appear to in practice. If this somehow changes (perhaps + // in a device-specific manner, we're in trouble). + if ( IsPC() || !IsX360() ) + { + if ( pColorSurface == m_pBackBufferSurface && nRenderTargetID > 0 ) + { + // SetRenderTargetEx is overloaded so that if you pass NULL in for anything that + // isn't the zeroth render target, you effectively disable that MRT index. + // (Passing in NULL for the zeroth render target means that you want to use the backbuffer + // as the render target.) + // hack hack hack!!!!! If the render target id > 0 and the user passed in NULL, disable the render target + Dx9Device()->SetRenderTarget( nRenderTargetID, NULL ); + } + else + { + Dx9Device()->SetRenderTarget( nRenderTargetID, pColorSurface ); + } + } + else + { + Assert( nRenderTargetID == 0 ); + SetRenderTargetInternalXbox( colorTextureHandle ); + } + + // The 0th render target defines which depth buffer we are using, so + // don't bother if we are another render target + if ( nRenderTargetID == 0 ) + { + Dx9Device()->SetDepthStencilSurface( pZSurface ); + } + + // The 0th render target defines the viewport, therefore it also defines + // the viewport limits. + if ( m_UsingTextureRenderTarget && nRenderTargetID == 0 ) + { + D3DSURFACE_DESC desc; + HRESULT hr; + if ( !pZSurface ) + { + hr = pColorSurface->GetDesc( &desc ); + } + else + { + hr = pZSurface->GetDesc( &desc ); + } + Assert( !FAILED(hr) ); + m_ViewportMaxWidth = desc.Width; + m_ViewportMaxHeight = desc.Height; + } + + static bool assert_on_refzero = false; + int ref; + if ( pZSurface ) + { +#if POSIX + ref = pZSurface->Release( 0, "-D CShaderAPIDx8::SetRenderTargetEx public release (z surface)"); +#else + ref = pZSurface->Release(); +#endif + + if(assert_on_refzero) + { + Assert( ref != 0 ); + } + } + +#ifdef ENABLE_NULLREF_DEVICE_SUPPORT + if( pColorSurface ) +#endif + { +#if POSIX + ref = pColorSurface->Release( 0, "-C CShaderAPIDx8::SetRenderTargetEx public release (color surface)"); +#else + ref = pColorSurface->Release(); +#endif + if(assert_on_refzero) + { + Assert( ref != 0 ); + } + } + + // Changing the render target sets a default viewport. Force rewrite to preserve the current desired state. + m_DynamicState.m_Viewport.X = 0; + m_DynamicState.m_Viewport.Y = 0; + m_DynamicState.m_Viewport.Width = (DWORD)-1; + m_DynamicState.m_Viewport.Height = (DWORD)-1; + + ADD_COMMIT_FUNC( COMMIT_PER_DRAW, COMMIT_ALWAYS, CommitSetViewports ); +} + + +//----------------------------------------------------------------------------- +// Changes the render target +//----------------------------------------------------------------------------- +void CShaderAPIDx8::SetRenderTarget( ShaderAPITextureHandle_t colorTextureHandle, ShaderAPITextureHandle_t depthTextureHandle ) +{ + LOCK_SHADERAPI(); + SetRenderTargetEx( 0, colorTextureHandle, depthTextureHandle ); +} + +//----------------------------------------------------------------------------- +// Returns the nearest supported format +//----------------------------------------------------------------------------- +ImageFormat CShaderAPIDx8::GetNearestSupportedFormat( ImageFormat fmt, bool bFilteringRequired /* = true */ ) const +{ + return FindNearestSupportedFormat( fmt, false, false, bFilteringRequired ); +} + + +ImageFormat CShaderAPIDx8::GetNearestRenderTargetFormat( ImageFormat fmt ) const +{ + return FindNearestSupportedFormat( fmt, false, true, false ); +} + + +bool CShaderAPIDx8::DoRenderTargetsNeedSeparateDepthBuffer() const +{ + LOCK_SHADERAPI(); + return m_PresentParameters.MultiSampleType != D3DMULTISAMPLE_NONE; +} + + +//----------------------------------------------------------------------------- +// Indicates we're modifying a texture +//----------------------------------------------------------------------------- +void CShaderAPIDx8::ModifyTexture( ShaderAPITextureHandle_t textureHandle ) +{ + tmZone( TELEMETRY_LEVEL2, TMZF_NONE, "%s", __FUNCTION__ ); + + LOCK_SHADERAPI(); + // Can't do this if we're locked! + Assert( m_ModifyTextureLockedLevel < 0 ); + + AssertValidTextureHandle( textureHandle ); + m_ModifyTextureHandle = textureHandle; + + // If we're got a multi-copy texture, we need to up the current copy count + Texture_t& tex = GetTexture( textureHandle ); + if (tex.m_NumCopies > 1) + { + // Each time we modify a texture, we'll want to switch texture + // as soon as a TexImage2D call is made... + tex.m_SwitchNeeded = true; + } +} + + +//----------------------------------------------------------------------------- +// Advances the current copy of a texture... +//----------------------------------------------------------------------------- +void CShaderAPIDx8::AdvanceCurrentCopy( ShaderAPITextureHandle_t hTexture ) +{ + // May need to switch textures.... + Texture_t& tex = GetTexture( hTexture ); + if (tex.m_NumCopies > 1) + { + if (++tex.m_CurrentCopy >= tex.m_NumCopies) + tex.m_CurrentCopy = 0; + + // When the current copy changes, we need to make sure this texture + // isn't bound to any stages any more; thereby guaranteeing the new + // copy will be re-bound. + UnbindTexture( hTexture ); + } +} + + +//----------------------------------------------------------------------------- +// Locks, unlocks the current texture +//----------------------------------------------------------------------------- + +bool CShaderAPIDx8::TexLock( int level, int cubeFaceID, int xOffset, int yOffset, + int width, int height, CPixelWriter& writer ) +{ + tmZone( TELEMETRY_LEVEL2, TMZF_NONE, "%s", __FUNCTION__ ); + + LOCK_SHADERAPI(); + + Assert( m_ModifyTextureLockedLevel < 0 ); + + ShaderAPITextureHandle_t hTexture = GetModifyTextureHandle(); + if ( !m_Textures.IsValidIndex( hTexture ) ) + return false; + + // Blow off mip levels if we don't support mipmapping + if ( !g_pHardwareConfig->SupportsMipmapping() && ( level > 0 ) ) + return false; + + // This test here just makes sure we don't try to download mipmap levels + // if we weren't able to create them in the first place + Texture_t& tex = GetTexture( hTexture ); + if ( level >= tex.m_NumLevels ) + { + return false; + } + + // May need to switch textures.... + if ( tex.m_SwitchNeeded ) + { + AdvanceCurrentCopy( hTexture ); + tex.m_SwitchNeeded = false; + } + + IDirect3DBaseTexture *pTexture = GetModifyTexture(); +#if defined( _X360 ) + // 360 can't lock a bound texture + if ( pTexture->IsSet( Dx9Device() ) ) + { + UnbindTexture( hTexture ); + } +#endif + + bool bOK = LockTexture( hTexture, tex.m_CurrentCopy, pTexture, + level, (D3DCUBEMAP_FACES)cubeFaceID, xOffset, yOffset, width, height, false, writer ); + if ( bOK ) + { + m_ModifyTextureLockedLevel = level; + m_ModifyTextureLockedFace = cubeFaceID; + } + return bOK; +} + +void CShaderAPIDx8::TexUnlock( ) +{ + tmZone( TELEMETRY_LEVEL2, TMZF_NONE, "%s", __FUNCTION__ ); + + LOCK_SHADERAPI(); + if ( m_ModifyTextureLockedLevel >= 0 ) + { + Texture_t& tex = GetTexture( GetModifyTextureHandle() ); + UnlockTexture( GetModifyTextureHandle(), tex.m_CurrentCopy, GetModifyTexture(), + m_ModifyTextureLockedLevel, (D3DCUBEMAP_FACES)m_ModifyTextureLockedFace ); + + m_ModifyTextureLockedLevel = -1; + } +} + +//----------------------------------------------------------------------------- +// Texture image upload +//----------------------------------------------------------------------------- +void CShaderAPIDx8::TexImage2D( + int level, + int cubeFaceID, + ImageFormat dstFormat, + int z, + int width, + int height, + ImageFormat srcFormat, + bool bSrcIsTiled, + void *pSrcData ) +{ + LOCK_SHADERAPI(); + + Assert( pSrcData ); + AssertValidTextureHandle( GetModifyTextureHandle() ); + + if ( !m_Textures.IsValidIndex( GetModifyTextureHandle() ) ) + { + return; + } + + Assert( (width <= g_pHardwareConfig->Caps().m_MaxTextureWidth) && (height <= g_pHardwareConfig->Caps().m_MaxTextureHeight) ); + + // Blow off mip levels if we don't support mipmapping + if ( !g_pHardwareConfig->SupportsMipmapping() && (level > 0)) + { + return; + } + + // This test here just makes sure we don't try to download mipmap levels + // if we weren't able to create them in the first place + Texture_t& tex = GetTexture( GetModifyTextureHandle() ); + if ( level >= tex.m_NumLevels ) + { + return; + } + + // May need to switch textures.... + if (tex.m_SwitchNeeded) + { + AdvanceCurrentCopy( GetModifyTextureHandle() ); + tex.m_SwitchNeeded = false; + } + + TextureLoadInfo_t info; + info.m_TextureHandle = GetModifyTextureHandle(); + info.m_pTexture = GetModifyTexture(); + info.m_nLevel = level; + info.m_nCopy = tex.m_CurrentCopy; + info.m_CubeFaceID = (D3DCUBEMAP_FACES)cubeFaceID; + info.m_nWidth = width; + info.m_nHeight = height; + info.m_nZOffset = z; + info.m_SrcFormat = srcFormat; + info.m_pSrcData = (unsigned char *)pSrcData; +#if defined( _X360 ) + info.m_bSrcIsTiled = bSrcIsTiled; + info.m_bCanConvertFormat = ( tex.m_Flags & Texture_t::CAN_CONVERT_FORMAT ) != 0; +#else + info.m_bTextureIsLockable = ( tex.m_Flags & Texture_t::IS_LOCKABLE ) != 0; +#endif + LoadTexture( info ); + SetModifyTexture( info.m_pTexture ); +} + +//----------------------------------------------------------------------------- +// Upload to a sub-piece of a texture +//----------------------------------------------------------------------------- +void CShaderAPIDx8::TexSubImage2D( + int level, + int cubeFaceID, + int xOffset, + int yOffset, + int zOffset, + int width, + int height, + ImageFormat srcFormat, + int srcStride, + bool bSrcIsTiled, + void *pSrcData ) +{ + LOCK_SHADERAPI(); + + Assert( pSrcData ); + AssertValidTextureHandle( GetModifyTextureHandle() ); + + if ( !m_Textures.IsValidIndex( GetModifyTextureHandle() ) ) + { + return; + } + + // Blow off mip levels if we don't support mipmapping + if ( !g_pHardwareConfig->SupportsMipmapping() && ( level > 0 ) ) + { + return; + } + + // NOTE: This can only be done with procedural textures if this method is + // being used to download the entire texture, cause last frame's partial update + // may be in a completely different texture! Sadly, I don't have all of the + // information I need, but I can at least check a couple things.... +#ifdef _DEBUG + if ( GetTexture( GetModifyTextureHandle() ).m_NumCopies > 1 ) + { + Assert( (xOffset == 0) && (yOffset == 0) ); + } +#endif + + // This test here just makes sure we don't try to download mipmap levels + // if we weren't able to create them in the first place + Texture_t& tex = GetTexture( GetModifyTextureHandle() ); + if ( level >= tex.m_NumLevels ) + { + return; + } + + // May need to switch textures.... + if ( tex.m_SwitchNeeded ) + { + AdvanceCurrentCopy( GetModifyTextureHandle() ); + tex.m_SwitchNeeded = false; + } + + TextureLoadInfo_t info; + info.m_TextureHandle = GetModifyTextureHandle(); + info.m_pTexture = GetModifyTexture(); + info.m_nLevel = level; + info.m_nCopy = tex.m_CurrentCopy; + info.m_CubeFaceID = (D3DCUBEMAP_FACES)cubeFaceID; + info.m_nWidth = width; + info.m_nHeight = height; + info.m_nZOffset = zOffset; + info.m_SrcFormat = srcFormat; + info.m_pSrcData = (unsigned char *)pSrcData; +#if defined( _X360 ) + info.m_bSrcIsTiled = bSrcIsTiled; + info.m_bCanConvertFormat = ( tex.m_Flags & Texture_t::CAN_CONVERT_FORMAT ) != 0; +#else + info.m_bTextureIsLockable = ( tex.m_Flags & Texture_t::IS_LOCKABLE ) != 0; +#endif + LoadSubTexture( info, xOffset, yOffset, srcStride ); +} + +//----------------------------------------------------------------------------- +// Volume texture upload +//----------------------------------------------------------------------------- +void CShaderAPIDx8::TexImageFromVTF( IVTFTexture *pVTF, int iVTFFrame ) +{ + LOCK_SHADERAPI(); + Assert( pVTF ); + AssertValidTextureHandle( GetModifyTextureHandle() ); + if ( !m_Textures.IsValidIndex( GetModifyTextureHandle() ) ) + { + return; + } + Texture_t& tex = GetTexture( GetModifyTextureHandle() ); + + // May need to switch textures.... + if (tex.m_SwitchNeeded) + { + AdvanceCurrentCopy( GetModifyTextureHandle() ); + tex.m_SwitchNeeded = false; + } + + TextureLoadInfo_t info; + info.m_TextureHandle = GetModifyTextureHandle(); + info.m_pTexture = GetModifyTexture(); + info.m_nLevel = 0; + info.m_nCopy = tex.m_CurrentCopy; + info.m_CubeFaceID = (D3DCUBEMAP_FACES)0; + info.m_nWidth = 0; + info.m_nHeight = 0; + info.m_nZOffset = 0; + info.m_SrcFormat = pVTF->Format(); + info.m_pSrcData = NULL; +#if defined( _X360 ) + info.m_bSrcIsTiled = pVTF->IsPreTiled(); + info.m_bCanConvertFormat = ( tex.m_Flags & Texture_t::CAN_CONVERT_FORMAT ) != 0; +#else + info.m_bTextureIsLockable = ( tex.m_Flags & Texture_t::IS_LOCKABLE ) != 0; +#endif + if ( pVTF->Depth() > 1 ) + { + LoadVolumeTextureFromVTF( info, pVTF, iVTFFrame ); + } + else if ( pVTF->IsCubeMap() ) + { + if ( HardwareConfig()->SupportsCubeMaps() ) + { + LoadCubeTextureFromVTF( info, pVTF, iVTFFrame ); + } + else + { + info.m_CubeFaceID = (D3DCUBEMAP_FACES)6; + LoadTextureFromVTF( info, pVTF, iVTFFrame ); + } + } + else + { + LoadTextureFromVTF( info, pVTF, iVTFFrame ); + } + SetModifyTexture( info.m_pTexture ); +} + + +//----------------------------------------------------------------------------- +// Is the texture resident? +//----------------------------------------------------------------------------- +bool CShaderAPIDx8::IsTextureResident( ShaderAPITextureHandle_t textureHandle ) +{ + return true; +} + + +//----------------------------------------------------------------------------- +// Level of anisotropic filtering +//----------------------------------------------------------------------------- +void CShaderAPIDx8::SetAnisotropicLevel( int nAnisotropyLevel ) +{ + LOCK_SHADERAPI(); + + // NOTE: This must be called before the rest of the code in this function so + // anisotropic can be set per-texture to force it on! This will also avoid + // a possible infinite loop that existed before. + g_pShaderUtil->NoteAnisotropicLevel( nAnisotropyLevel ); + + // Never set this to 1. In the case we want it set to 1, we will use this to override + // aniso per-texture, so set it to something reasonable + if ( nAnisotropyLevel > g_pHardwareConfig->Caps().m_nMaxAnisotropy || nAnisotropyLevel <= 1 ) + { + // Set it to 1/4 the max but between 2-8 + nAnisotropyLevel = max( 2, min( 8, ( g_pHardwareConfig->Caps().m_nMaxAnisotropy / 4 ) ) ); + } + + // Set the D3D max insotropy state for all samplers + for ( int i = 0; i < g_pHardwareConfig->Caps().m_NumSamplers; ++i) + { + SamplerState(i).m_nAnisotropicLevel = nAnisotropyLevel; + SetSamplerState( i, D3DSAMP_MAXANISOTROPY, SamplerState(i).m_nAnisotropicLevel ); + } +} + + +//----------------------------------------------------------------------------- +// Sets the priority +//----------------------------------------------------------------------------- +void CShaderAPIDx8::TexSetPriority( int priority ) +{ +#if !defined( _X360 ) + LOCK_SHADERAPI(); + + // A hint to the cacher... + ShaderAPITextureHandle_t hModifyTexture = GetModifyTextureHandle(); + if ( hModifyTexture == INVALID_SHADERAPI_TEXTURE_HANDLE ) + return; + + Texture_t& tex = GetTexture( hModifyTexture ); + if ( tex.m_NumCopies > 1 ) + { + for (int i = 0; i < tex.m_NumCopies; ++i) + tex.GetTexture( i )->SetPriority( priority ); + } + else + { + tex.GetTexture()->SetPriority( priority ); + } +#endif +} + +void CShaderAPIDx8::TexLodClamp( int finest ) +{ + LOCK_SHADERAPI(); + + ShaderAPITextureHandle_t hModifyTexture = GetModifyTextureHandle(); + if ( hModifyTexture == INVALID_SHADERAPI_TEXTURE_HANDLE ) + return; + + Texture_t& tex = GetTexture( hModifyTexture ); + tex.m_FinestMipmapLevel = finest; +} + +void CShaderAPIDx8::TexLodBias( float bias ) +{ + LOCK_SHADERAPI(); + + ShaderAPITextureHandle_t hModifyTexture = GetModifyTextureHandle(); + if ( hModifyTexture == INVALID_SHADERAPI_TEXTURE_HANDLE ) + return; + + Texture_t& tex = GetTexture( hModifyTexture ); + tex.m_LodBias = bias; +} + + + +//----------------------------------------------------------------------------- +// Texturemapping state +//----------------------------------------------------------------------------- +void CShaderAPIDx8::TexWrap( ShaderTexCoordComponent_t coord, ShaderTexWrapMode_t wrapMode ) +{ + LOCK_SHADERAPI(); + + ShaderAPITextureHandle_t hModifyTexture = GetModifyTextureHandle(); + if ( hModifyTexture == INVALID_SHADERAPI_TEXTURE_HANDLE ) + return; + + D3DTEXTUREADDRESS address; + switch( wrapMode ) + { + case SHADER_TEXWRAPMODE_CLAMP: + address = D3DTADDRESS_CLAMP; + break; + case SHADER_TEXWRAPMODE_REPEAT: + address = D3DTADDRESS_WRAP; + break; + case SHADER_TEXWRAPMODE_BORDER: + address = D3DTADDRESS_BORDER; + break; + default: + address = D3DTADDRESS_CLAMP; + Warning( "CShaderAPIDx8::TexWrap: unknown wrapMode\n" ); + break; + } + + switch( coord ) + { + case SHADER_TEXCOORD_S: + GetTexture( hModifyTexture ).m_UTexWrap = address; + break; + case SHADER_TEXCOORD_T: + GetTexture( hModifyTexture ).m_VTexWrap = address; + break; + case SHADER_TEXCOORD_U: + GetTexture( hModifyTexture ).m_WTexWrap = address; + break; + default: + Warning( "CShaderAPIDx8::TexWrap: unknown coord\n" ); + break; + } +} + +void CShaderAPIDx8::TexMinFilter( ShaderTexFilterMode_t texFilterMode ) +{ + LOCK_SHADERAPI(); + + ShaderAPITextureHandle_t hModifyTexture = GetModifyTextureHandle(); + if ( hModifyTexture == INVALID_SHADERAPI_TEXTURE_HANDLE ) + return; + + switch( texFilterMode ) + { + case SHADER_TEXFILTERMODE_NEAREST: + GetTexture( hModifyTexture ).m_MinFilter = D3DTEXF_POINT; + GetTexture( hModifyTexture ).m_MipFilter = D3DTEXF_NONE; + break; + case SHADER_TEXFILTERMODE_LINEAR: + GetTexture( hModifyTexture ).m_MinFilter = D3DTEXF_LINEAR; + GetTexture( hModifyTexture ).m_MipFilter = D3DTEXF_NONE; + break; + case SHADER_TEXFILTERMODE_NEAREST_MIPMAP_NEAREST: + GetTexture( hModifyTexture ).m_MinFilter = D3DTEXF_POINT; + GetTexture( hModifyTexture ).m_MipFilter = + GetTexture( hModifyTexture ).m_NumLevels != 1 ? D3DTEXF_POINT : D3DTEXF_NONE; + break; + case SHADER_TEXFILTERMODE_LINEAR_MIPMAP_NEAREST: + GetTexture( hModifyTexture ).m_MinFilter = D3DTEXF_LINEAR; + GetTexture( hModifyTexture ).m_MipFilter = + GetTexture( hModifyTexture ).m_NumLevels != 1 ? D3DTEXF_POINT : D3DTEXF_NONE; + break; + case SHADER_TEXFILTERMODE_NEAREST_MIPMAP_LINEAR: + GetTexture( hModifyTexture ).m_MinFilter = D3DTEXF_POINT; + GetTexture( hModifyTexture ).m_MipFilter = + GetTexture( hModifyTexture ).m_NumLevels != 1 ? D3DTEXF_LINEAR : D3DTEXF_NONE; + break; + case SHADER_TEXFILTERMODE_LINEAR_MIPMAP_LINEAR: + GetTexture( hModifyTexture ).m_MinFilter = D3DTEXF_LINEAR; + GetTexture( hModifyTexture ).m_MipFilter = + GetTexture( hModifyTexture ).m_NumLevels != 1 ? D3DTEXF_LINEAR : D3DTEXF_NONE; + break; + case SHADER_TEXFILTERMODE_ANISOTROPIC: + GetTexture( hModifyTexture ).m_MinFilter = D3DTEXF_ANISOTROPIC; + GetTexture( hModifyTexture ).m_MipFilter = + GetTexture( hModifyTexture ).m_NumLevels != 1 ? D3DTEXF_LINEAR : D3DTEXF_NONE; + break; + default: + Warning( "CShaderAPIDx8::TexMinFilter: Unknown texFilterMode\n" ); + break; + } +} + +void CShaderAPIDx8::TexMagFilter( ShaderTexFilterMode_t texFilterMode ) +{ + LOCK_SHADERAPI(); + + ShaderAPITextureHandle_t hModifyTexture = GetModifyTextureHandle(); + if ( hModifyTexture == INVALID_SHADERAPI_TEXTURE_HANDLE ) + return; + + switch( texFilterMode ) + { + case SHADER_TEXFILTERMODE_NEAREST: + GetTexture( hModifyTexture ).m_MagFilter = D3DTEXF_POINT; + break; + case SHADER_TEXFILTERMODE_LINEAR: + GetTexture( hModifyTexture ).m_MagFilter = D3DTEXF_LINEAR; + break; + case SHADER_TEXFILTERMODE_NEAREST_MIPMAP_NEAREST: + Warning( "CShaderAPIDx8::TexMagFilter: SHADER_TEXFILTERMODE_NEAREST_MIPMAP_NEAREST is invalid\n" ); + break; + case SHADER_TEXFILTERMODE_LINEAR_MIPMAP_NEAREST: + Warning( "CShaderAPIDx8::TexMagFilter: SHADER_TEXFILTERMODE_LINEAR_MIPMAP_NEAREST is invalid\n" ); + break; + case SHADER_TEXFILTERMODE_NEAREST_MIPMAP_LINEAR: + Warning( "CShaderAPIDx8::TexMagFilter: SHADER_TEXFILTERMODE_NEAREST_MIPMAP_LINEAR is invalid\n" ); + break; + case SHADER_TEXFILTERMODE_LINEAR_MIPMAP_LINEAR: + Warning( "CShaderAPIDx8::TexMagFilter: SHADER_TEXFILTERMODE_LINEAR_MIPMAP_LINEAR is invalid\n" ); + break; + case SHADER_TEXFILTERMODE_ANISOTROPIC: + GetTexture( hModifyTexture ).m_MagFilter = g_pHardwareConfig->Caps().m_bSupportsMagAnisotropicFiltering ? D3DTEXF_ANISOTROPIC : D3DTEXF_LINEAR; + break; + default: + Warning( "CShaderAPIDx8::TexMAGFilter: Unknown texFilterMode\n" ); + break; + } +} + + +//----------------------------------------------------------------------------- +// Gets the matrix stack from the matrix mode +//----------------------------------------------------------------------------- + +int CShaderAPIDx8::GetMatrixStack( MaterialMatrixMode_t mode ) const +{ + Assert( mode >= 0 && mode < NUM_MATRIX_MODES ); + return mode; +} + +//----------------------------------------------------------------------------- +// Returns true if we're modulating constant color into the vertex color +//----------------------------------------------------------------------------- +bool CShaderAPIDx8::IsModulatingVertexColor() const +{ + return m_TransitionTable.CurrentShadowShaderState()->m_ModulateConstantColor; +} + + +//----------------------------------------------------------------------------- +// Material property (used to deal with overbright for lights) +//----------------------------------------------------------------------------- +void CShaderAPIDx8::SetDefaultMaterial() +{ +#if !defined( _X360 ) + D3DMATERIAL mat; + mat.Diffuse.r = mat.Diffuse.g = mat.Diffuse.b = mat.Diffuse.a = 1.0f; + mat.Ambient.r = mat.Ambient.g = mat.Ambient.b = mat.Ambient.a = 0.0f; + mat.Specular.r = mat.Specular.g = mat.Specular.b = mat.Specular.a = 0.0f; + mat.Emissive.r = mat.Emissive.g = mat.Emissive.b = mat.Emissive.a = 0.0f; + mat.Power = 1.0f; + Dx9Device()->SetMaterial( &mat ); +#endif +} + +//----------------------------------------------------------------------------- +// lighting related methods +//----------------------------------------------------------------------------- + +void CShaderAPIDx8::SetAmbientLight( float r, float g, float b ) +{ + LOCK_SHADERAPI(); + unsigned int ambient = D3DCOLOR_ARGB( 255, (int)(r * 255), + (int)(g * 255), (int)(b * 255) ); + if (ambient != m_DynamicState.m_Ambient) + { + m_DynamicState.m_Ambient = ambient; + SetSupportedRenderState( D3DRS_AMBIENT, ambient ); + } +} + +void CShaderAPIDx8::SetLightingOrigin( Vector vLightingOrigin ) +{ + if ( vLightingOrigin != m_DynamicState.m_vLightingOrigin ) + { + FlushBufferedPrimitives(); + m_DynamicState.m_vLightingOrigin = vLightingOrigin; + } +} + +//#define NO_LOCAL_LIGHTS +void CShaderAPIDx8::SetLight( int lightNum, const LightDesc_t& desc_ ) +{ + LOCK_SHADERAPI(); +#ifdef NO_LOCAL_LIGHTS + LightDesc_t desc = desc_; + desc.m_Type = MATERIAL_LIGHT_DISABLE; +#else + LightDesc_t &desc = const_cast(desc_); // to permit '&' +#endif + Assert( lightNum < g_pHardwareConfig->Caps().m_MaxNumLights && lightNum >= 0 ); + + if( lightNum >= g_pHardwareConfig->Caps().m_MaxNumLights || lightNum < 0 ) + return; + + m_DynamicState.m_LightDescs[lightNum] = desc; + + FlushBufferedPrimitives(); + + if (desc.m_Type == MATERIAL_LIGHT_DISABLE) + { + if (m_DynamicState.m_LightEnable[lightNum]) + { + m_DynamicState.m_LightEnableChanged[lightNum] = STATE_CHANGED; + m_DynamicState.m_LightEnable[lightNum] = false; + } + return; + } + + if (!m_DynamicState.m_LightEnable[lightNum]) + { + m_DynamicState.m_LightEnableChanged[lightNum] = STATE_CHANGED; + m_DynamicState.m_LightEnable[lightNum] = true; + } + + D3DLIGHT light; + switch( desc.m_Type ) + { + case MATERIAL_LIGHT_POINT: + light.Type = D3DLIGHT_POINT; + light.Range = desc.m_Range; + break; + + case MATERIAL_LIGHT_DIRECTIONAL: + light.Type = D3DLIGHT_DIRECTIONAL; + light.Range = 1e12; // This is supposed to be ignored + break; + + case MATERIAL_LIGHT_SPOT: + light.Type = D3DLIGHT_SPOT; + light.Range = desc.m_Range; + break; + + default: + m_DynamicState.m_LightEnable[lightNum] = false; + return; + } + + // This is a D3D limitation + Assert( (light.Range >= 0) && (light.Range <= sqrt(FLT_MAX)) ); + + memcpy( &light.Diffuse, &desc.m_Color[0], 3*sizeof(float) ); + memcpy( &light.Specular, &desc.m_Color[0], 3*sizeof(float) ); + light.Diffuse.a = 1.0f; + light.Specular.a = 1.0f; + light.Ambient.a = light.Ambient.b = light.Ambient.g = light.Ambient.r = 0; + memcpy( &light.Position, &desc.m_Position[0], 3 * sizeof(float) ); + memcpy( &light.Direction, &desc.m_Direction[0], 3 * sizeof(float) ); + light.Falloff = desc.m_Falloff; + light.Attenuation0 = desc.m_Attenuation0; + light.Attenuation1 = desc.m_Attenuation1; + light.Attenuation2 = desc.m_Attenuation2; + + // normalize light color... + light.Theta = desc.m_Theta; + light.Phi = desc.m_Phi; + if (light.Phi > M_PI) + light.Phi = M_PI; + + // This piece of crap line of code is because if theta gets too close to phi, + // we get no light at all. + if (light.Theta - light.Phi > -1e-3) + light.Theta = light.Phi - 1e-3; + + m_DynamicState.m_LightChanged[lightNum] = STATE_CHANGED; + memcpy( &m_DynamicState.m_Lights[lightNum], &light, sizeof(light) ); +} + +void CShaderAPIDx8::DisableAllLocalLights() +{ + LOCK_SHADERAPI(); + bool bFlushed = false; + for ( int lightNum = 0; lightNum < MAX_NUM_LIGHTS; lightNum++ ) + { + if (m_DynamicState.m_LightEnable[lightNum]) + { + if ( !bFlushed ) + { + FlushBufferedPrimitives(); + bFlushed = true; + } + m_DynamicState.m_LightDescs[lightNum].m_Type = MATERIAL_LIGHT_DISABLE; + m_DynamicState.m_LightEnableChanged[lightNum] = STATE_CHANGED; + m_DynamicState.m_LightEnable[lightNum] = false; + } + } +} + +int CShaderAPIDx8::GetMaxLights( void ) const +{ + return g_pHardwareConfig->Caps().m_MaxNumLights; +} + +const LightDesc_t& CShaderAPIDx8::GetLight( int lightNum ) const +{ + Assert( lightNum < g_pHardwareConfig->Caps().m_MaxNumLights && lightNum >= 0 ); + return m_DynamicState.m_LightDescs[lightNum]; +} + +//----------------------------------------------------------------------------- +// Ambient cube +//----------------------------------------------------------------------------- + +//#define NO_AMBIENT_CUBE 1 +void CShaderAPIDx8::SetAmbientLightCube( Vector4D cube[6] ) +{ + LOCK_SHADERAPI(); +/* + int i; + for( i = 0; i < 6; i++ ) + { + ColorClamp( cube[i].AsVector3D() ); +// if( i == 0 ) +// { +// Warning( "%d: %f %f %f\n", i, cube[i][0], cube[i][1], cube[i][2] ); +// } + } +*/ + if (memcmp(&m_DynamicState.m_AmbientLightCube[0][0], cube, 6 * sizeof(Vector4D))) + { + memcpy( &m_DynamicState.m_AmbientLightCube[0][0], cube, 6 * sizeof(Vector4D) ); + +#ifdef NO_AMBIENT_CUBE + memset( &m_DynamicState.m_AmbientLightCube[0][0], 0, 6 * sizeof(Vector4D) ); +#endif + +//#define DEBUG_AMBIENT_CUBE + +#ifdef DEBUG_AMBIENT_CUBE + m_DynamicState.m_AmbientLightCube[0][0] = 1.0f; + m_DynamicState.m_AmbientLightCube[0][1] = 0.0f; + m_DynamicState.m_AmbientLightCube[0][2] = 0.0f; + + m_DynamicState.m_AmbientLightCube[1][0] = 0.0f; + m_DynamicState.m_AmbientLightCube[1][1] = 1.0f; + m_DynamicState.m_AmbientLightCube[1][2] = 0.0f; + + m_DynamicState.m_AmbientLightCube[2][0] = 0.0f; + m_DynamicState.m_AmbientLightCube[2][1] = 0.0f; + m_DynamicState.m_AmbientLightCube[2][2] = 1.0f; + + m_DynamicState.m_AmbientLightCube[3][0] = 1.0f; + m_DynamicState.m_AmbientLightCube[3][1] = 0.0f; + m_DynamicState.m_AmbientLightCube[3][2] = 1.0f; + + m_DynamicState.m_AmbientLightCube[4][0] = 1.0f; + m_DynamicState.m_AmbientLightCube[4][1] = 1.0f; + m_DynamicState.m_AmbientLightCube[4][2] = 0.0f; + + m_DynamicState.m_AmbientLightCube[5][0] = 0.0f; + m_DynamicState.m_AmbientLightCube[5][1] = 1.0f; + m_DynamicState.m_AmbientLightCube[5][2] = 1.0f; +#endif + + m_CachedAmbientLightCube = STATE_CHANGED; + } +} + +void CShaderAPIDx8::SetVertexShaderStateAmbientLightCube() +{ + if (m_CachedAmbientLightCube & STATE_CHANGED_VERTEX_SHADER) + { + SetVertexShaderConstant( VERTEX_SHADER_AMBIENT_LIGHT, m_DynamicState.m_AmbientLightCube[0].Base(), 6 ); + m_CachedAmbientLightCube &= ~STATE_CHANGED_VERTEX_SHADER; + } +} + + +void CShaderAPIDx8::SetPixelShaderStateAmbientLightCube( int pshReg, bool bForceToBlack ) +{ + float *pCubeBase; + Vector4D tempCube[6]; + + if( bForceToBlack ) + { + for ( int i=0; i<6 ; i++ ) + tempCube[i].Init(); + + pCubeBase = tempCube[0].Base(); + } + else + { + pCubeBase = m_DynamicState.m_AmbientLightCube[0].Base(); + } + + SetPixelShaderConstant( pshReg, pCubeBase, 6 ); +} + +float CShaderAPIDx8::GetAmbientLightCubeLuminance( void ) +{ + Vector4D vLuminance( 0.3f, 0.59f, 0.11f, 0.0f ); + float fLuminance = 0.0f; + + for (int i=0; i<6; i++) + { + fLuminance += vLuminance.Dot( m_DynamicState.m_AmbientLightCube[i] ); + } + + return fLuminance / 6.0f; +} + +static inline RECT* RectToRECT( Rect_t *pSrcRect, RECT &dstRect ) +{ + if ( !pSrcRect ) + return NULL; + + dstRect.left = pSrcRect->x; + dstRect.top = pSrcRect->y; + dstRect.right = pSrcRect->x + pSrcRect->width; + dstRect.bottom = pSrcRect->y + pSrcRect->height; + return &dstRect; +} + +void CShaderAPIDx8::CopyRenderTargetToTextureEx( ShaderAPITextureHandle_t textureHandle, int nRenderTargetID, Rect_t *pSrcRect, Rect_t *pDstRect ) +{ + LOCK_SHADERAPI(); + VPROF_BUDGET( "CShaderAPIDx8::CopyRenderTargetToTexture", "Refraction overhead" ); + + if ( !TextureIsAllocated( textureHandle ) ) + return; + +#if defined( PIX_INSTRUMENTATION ) + { + const char *pRT = ( nRenderTargetID < 0 ) ? "DS" : "RT"; + + if ( textureHandle == SHADER_RENDERTARGET_NONE ) + { + pRT = "None"; + } + else if ( textureHandle != SHADER_RENDERTARGET_BACKBUFFER ) + { + Texture_t &tex = GetTexture( textureHandle ); + pRT = tex.m_DebugName.String(); + } + + char buf[256]; + sprintf( buf, "CopyRTToTexture:%s", pRT ? pRT : "?" ); + BeginPIXEvent( 0xFFFFFFFF, buf ); + EndPIXEvent(); + } +#endif + + // Don't flush here!! If you have to flush here, then there is a driver bug. + // FlushHardware( ); + + AssertValidTextureHandle( textureHandle ); + Texture_t *pTexture = &GetTexture( textureHandle ); + Assert( pTexture ); + IDirect3DTexture *pD3DTexture = (IDirect3DTexture *)pTexture->GetTexture(); + Assert( pD3DTexture ); + +#if !defined( _X360 ) + IDirect3DSurface* pRenderTargetSurface; + HRESULT hr = Dx9Device()->GetRenderTarget( nRenderTargetID, &pRenderTargetSurface ); + if ( FAILED( hr ) ) + { + Assert( 0 ); + return; + } + + IDirect3DSurface *pDstSurf; + hr = pD3DTexture->GetSurfaceLevel( 0, &pDstSurf ); + Assert( !FAILED( hr ) ); + if ( FAILED( hr ) ) + { + pRenderTargetSurface->Release(); + return; + } + + bool tryblit = true; + if ( tryblit ) + { + RECORD_COMMAND( DX8_COPY_FRAMEBUFFER_TO_TEXTURE, 1 ); + RECORD_INT( textureHandle ); + + RECT srcRect, dstRect; + hr = Dx9Device()->StretchRect( pRenderTargetSurface, RectToRECT( pSrcRect, srcRect ), + pDstSurf, RectToRECT( pDstRect, dstRect ), D3DTEXF_LINEAR ); + Assert( !FAILED( hr ) ); + } + + pDstSurf->Release(); + pRenderTargetSurface->Release(); +#else + DWORD flags = 0; + switch( nRenderTargetID ) + { + case -1: + flags = D3DRESOLVE_DEPTHSTENCIL | D3DRESOLVE_FRAGMENT0; + break; + case 0: + flags = D3DRESOLVE_RENDERTARGET0; + break; + case 1: + case 2: + case 3: + // not supporting MRT + Assert( 0 ); + return; + NO_DEFAULT + }; + + // not prepared to handle mip mapping yet + Assert( pD3DTexture->GetLevelCount() == 1 ); + + D3DPOINT dstPoint = { 0 }; + if ( pDstRect ) + { + dstPoint.x = pDstRect->x; + dstPoint.y = pDstRect->y; + } + + int destWidth, destHeight; + if( pDstRect ) + { + destWidth = pDstRect->width; + destHeight = pDstRect->height; + + Assert( (destWidth <= pTexture->GetWidth()) && (destHeight <= pTexture->GetHeight()) ); + } + else + { + destWidth = pTexture->GetWidth(); + destHeight = pTexture->GetHeight(); + } + + RECT srcRect; + RECT *pResolveRect = NULL; + int srcWidth, srcHeight; + if ( pSrcRect ) + { + RectToRECT( pSrcRect, srcRect ); + pResolveRect = &srcRect; + + // resolve has no stretching ability, and we can only compensate when doing a resolve to a whole texture larger than the source + Assert( !pDstRect || ( pSrcRect->width <= pDstRect->width && pSrcRect->height <= pDstRect->height ) ); + + srcWidth = pSrcRect->width; + srcHeight = pSrcRect->height; + } + else + { + srcRect.left = srcRect.top = 0; + srcRect.right = m_DynamicState.m_Viewport.Width; + srcRect.bottom = m_DynamicState.m_Viewport.Height; + if( (srcRect.right < 0) || (srcRect.bottom < 0) ) + { + if( m_UsingTextureRenderTarget ) + { + srcRect.right = m_ViewportMaxWidth; + srcRect.bottom = m_ViewportMaxHeight; + } + else + { + int w,h; + GetBackBufferDimensions( w, h ); + srcRect.right = w; + srcRect.bottom = h; + } + } + srcWidth = srcRect.right; + srcHeight = srcRect.bottom; + } + + if( (srcWidth != destWidth) || (srcHeight != destHeight) ) + { + //Not a 1:1 resolve, we should only have gotten this far if we can downsize the target texture to compensate + Assert( (destWidth > srcWidth) && (destHeight > srcHeight) && (dstPoint.x == 0) && (dstPoint.y == 0) ); + + //What we're doing is telling D3D that this texture is smaller than it is so the resolve is 1:1. + //We leave the texture in this state until it resolves from something bigger. + //All outside code still thinks this texture is it's original size. And it still owns enough memory to go back to it's original size. + pD3DTexture->Format.Size.TwoD.Width = srcWidth - 1; + pD3DTexture->Format.Size.TwoD.Height = srcHeight - 1; //no idea why they store it as size-1, but they do + pResolveRect = NULL; + } + else + { + //restore D3D texture to full size in case it was previously downsized + pD3DTexture->Format.Size.TwoD.Width = pTexture->GetWidth() - 1; + pD3DTexture->Format.Size.TwoD.Height = pTexture->GetHeight() - 1; //no idea why they store it as size-1, but they do + } + + // if we convert to srgb format, we need the original format for reverting. We only need the first DWORD of GPUTEXTURE_FETCH_CONSTANT. + DWORD linearFormatBackup = pD3DTexture->Format.dword[0]; + if ( !( flags & D3DRESOLVE_DEPTHSTENCIL ) && ( m_DynamicState.m_bSRGBWritesEnabled ) ) + { + // we need a matched resolve regarding sRGB to get values transfered as-is + // when the surface is sRGB, use the corresponding sRGB texture + pD3DTexture->Format.SignX = pD3DTexture->Format.SignY = pD3DTexture->Format.SignZ = 3; + } + + HRESULT hr = Dx9Device()->Resolve( flags, (D3DRECT*)pResolveRect, pD3DTexture, &dstPoint, 0, 0, NULL, 0, 0, NULL ); + Assert( !FAILED( hr ) ); + + pD3DTexture->Format.dword[0] = linearFormatBackup; +#endif +} + +void CShaderAPIDx8::CopyRenderTargetToScratchTexture( ShaderAPITextureHandle_t srcRt, ShaderAPITextureHandle_t dstTex, Rect_t *pSrcRect, Rect_t *pDstRect ) +{ + LOCK_SHADERAPI(); + + if ( !TextureIsAllocated( srcRt ) || !TextureIsAllocated( dstTex ) ) + { + Assert( !"Fix that render target or dest texture aren't allocated." ); + return; + } + + HRESULT hr = D3D_OK; + + IDirect3DSurface9* srcSurf = NULL; + AssertValidTextureHandle( srcRt ); + Texture_t *pSrcRt = &GetTexture( srcRt ); + Assert( pSrcRt ); + IDirect3DTexture *pD3DSrcRt = ( IDirect3DTexture * ) pSrcRt->GetTexture(); + Assert( pD3DSrcRt ); + hr = pD3DSrcRt->GetSurfaceLevel( 0, &srcSurf ); + Assert( SUCCEEDED( hr ) && srcSurf ); + + IDirect3DSurface9* dstSurf = NULL; + AssertValidTextureHandle( dstTex ); + Texture_t *pDstTex = &GetTexture( dstTex ); + Assert( pDstTex ); + IDirect3DTexture *pD3DDstTex = ( IDirect3DTexture * ) pDstTex->GetTexture(); + Assert( pD3DDstTex ); + hr = pD3DDstTex->GetSurfaceLevel( 0, &dstSurf ); + Assert( SUCCEEDED( hr ) && dstSurf ); + + // This does it. + hr = Dx9Device()->GetRenderTargetData( srcSurf, dstSurf ); + Assert( SUCCEEDED( hr ) ); + + srcSurf->Release(); + dstSurf->Release(); +} + +//------------------------------------------------------------------------- +// Allows locking and unlocking of very specific surface types. pOutBits and pOutPitch will not be touched if +// the lock fails. +//------------------------------------------------------------------------- +void CShaderAPIDx8::LockRect( void** pOutBits, int* pOutPitch, ShaderAPITextureHandle_t texHandle, int mipmap, int x, int y, int w, int h, bool bWrite, bool bRead ) +{ + LOCK_SHADERAPI(); + + Assert( pOutBits ); + Assert( pOutPitch ); + + if ( !TextureIsAllocated( texHandle ) ) + { + Assert( !"Fix that texture isn't allocated." ); + return; + } + + HRESULT hr = D3D_OK; + IDirect3DSurface9* surf = NULL; + AssertValidTextureHandle( texHandle ); + Texture_t *pTex = &GetTexture( texHandle ); + Assert( pTex ); + IDirect3DTexture *pD3DTex = ( IDirect3DTexture * ) pTex->GetTexture(); + Assert( pD3DTex ); + + hr = pD3DTex->GetSurfaceLevel( mipmap, &surf ); + Assert( SUCCEEDED( hr ) && surf ); + + D3DLOCKED_RECT lockRect = { 0 }; + RECT srcRect = { x, y, w, h }; + DWORD flags = 0; + + if ( !bRead && !bWrite ) + { + Assert( !"Asking to neither read nor write? Probably a caller bug." ); + goto cleanup; + } + + if ( bRead && !bWrite ) + flags = D3DLOCK_READONLY; + + hr = surf->LockRect( &lockRect, &srcRect, flags ); + if ( FAILED( hr ) ) + { + Assert( !"Lock failed, look into why." ); + goto cleanup; + } + + (*pOutBits) = lockRect.pBits; + (*pOutPitch) = lockRect.Pitch; + +cleanup: + surf->Release(); +} + +void CShaderAPIDx8::UnlockRect( ShaderAPITextureHandle_t texHandle, int mipmap ) +{ + LOCK_SHADERAPI(); + + if ( !TextureIsAllocated( texHandle ) ) + { + Assert( !"Fix that texture isn't allocated." ); + return; + } + + HRESULT hr = D3D_OK; + IDirect3DSurface9* surf = NULL; + AssertValidTextureHandle( texHandle ); + Texture_t *pTex = &GetTexture( texHandle ); + Assert( pTex ); + IDirect3DTexture *pD3DTex = ( IDirect3DTexture * ) pTex->GetTexture(); + Assert( pD3DTex ); + + hr = pD3DTex->GetSurfaceLevel( mipmap, &surf ); + Assert( SUCCEEDED( hr ) && surf ); + + hr = surf->UnlockRect(); + Assert( SUCCEEDED( hr ) ); + + surf->Release(); +} + +static float GetAspectRatio( const Texture_t* pTex ) +{ + Assert( pTex ); + if ( pTex->m_Height != 0 ) + return float( pTex->m_Width ) / float( pTex->m_Height ); + + Assert( !"Height of texture is 0, that seems like a bug." ); + return 0.0f; +} + +struct TextureExtents_t +{ + int width; + int height; + int depth; + int mipmaps; + + int fine; + int coarse; + + TextureExtents_t() : width( 0 ), height( 0 ), depth( 0 ), mipmaps( 0 ), fine( 0 ), coarse( 0 ) { } +}; + +// Returns positive integer on success, 0 or <0 on failure. +static int FindCommonMipmapRange( int *pOutSrcFine, int *pOutDstFine, Texture_t *pSrcTex, Texture_t *pDstTex ) +{ + Assert( pOutSrcFine && pOutDstFine ); + Assert( pSrcTex && pDstTex ); + + if ( GetAspectRatio( pSrcTex ) != GetAspectRatio( pDstTex ) ) + return 0; + + TextureExtents_t src, + dst; + + // LOD Clamp indicates that there's no actual data in the finer mipmap levels yet, so respect it when determining + // the source and destination levels that could have data. + const int srcLodClamp = pSrcTex->GetLodClamp(); + src.width = Max( 1, pSrcTex->GetWidth() >> srcLodClamp ); + src.height = Max( 1, pSrcTex->GetHeight() >> srcLodClamp ); + src.depth = Max( 1, pSrcTex->GetDepth() >> srcLodClamp ); + src.mipmaps = pSrcTex->m_NumLevels - srcLodClamp; + Assert( src.mipmaps >= 1 ); + + + const int dstLodClamp = pDstTex->GetLodClamp(); + dst.width = Max( 1, pDstTex->GetWidth() >> dstLodClamp ); + dst.height = Max( 1, pDstTex->GetHeight() >> dstLodClamp ); + dst.depth = Max( 1, pDstTex->GetDepth() >> dstLodClamp ); + dst.mipmaps = pDstTex->m_NumLevels - dstLodClamp; + Assert( dst.mipmaps >= 1 ); + + TextureExtents_t *pLarger = NULL, + *pSmaller = NULL; + + if ( src.width >= dst.width && src.height >= dst.height && src.depth >= dst.depth ) + { + pLarger = &src; + pSmaller = &dst; + } + else + { + pLarger = &dst; + pSmaller = &src; + } + + // Since we are same aspect ratio, only need to test one dimension + while ( ( pLarger->width >> pLarger->fine ) > pSmaller->width ) + { + ++pLarger->fine; + --pLarger->mipmaps; + } + + ( *pOutSrcFine ) = src.fine; + ( *pOutDstFine ) = dst.fine; + + return Min( src.mipmaps, dst.mipmaps ); +} + +void CShaderAPIDx8::CopyTextureToTexture( ShaderAPITextureHandle_t srcTex, ShaderAPITextureHandle_t dstTex ) +{ + LOCK_SHADERAPI(); + + AssertValidTextureHandle( srcTex ); + AssertValidTextureHandle( dstTex ); + + Assert( TextureIsAllocated( srcTex ) && TextureIsAllocated( dstTex ) ); + + Texture_t *pSrcTex = &GetTexture( srcTex ); + Texture_t *pDstTex = &GetTexture( dstTex ); + + Assert( pSrcTex && pDstTex ); + + // Must have same image format + Assert( pSrcTex->GetImageFormat() == pDstTex->GetImageFormat() ); + + int srcFine = 0, + dstFine = 0; + + int mipmapCount = FindCommonMipmapRange( &srcFine, &dstFine, pSrcTex, pDstTex ); + + if ( mipmapCount <= 0 ) + { + // This is legit for things that are streamed in that are very small (near the 32x32 cutoff we do at the + // tip of the mipmap pyramid). But leaving it here because it's useful if you're tracking a specific bug. + // Warning( "Attempted to copy textures that had non-overlapping mipmap pyramids. This has failed and no copy has taken place.\n" ); + return; + } + + IDirect3DTexture* pSrcD3DTex = ( IDirect3DTexture * ) pSrcTex->GetTexture(); + IDirect3DTexture* pDstD3DTex = ( IDirect3DTexture * ) pDstTex->GetTexture(); + + HRESULT hr = S_OK; + for ( int i = 0; i < mipmapCount; ++i ) + { + int srcMipmap = srcFine + i; + int dstMipmap = dstFine + i; + + IDirect3DSurface9* pSrcSurf = NULL; + IDirect3DSurface9* pDstSurf = NULL; + + hr = pSrcD3DTex->GetSurfaceLevel( srcMipmap, &pSrcSurf ); + Assert( SUCCEEDED( hr ) && pSrcSurf ); + + hr = pDstD3DTex->GetSurfaceLevel( dstMipmap, &pDstSurf ); + Assert( SUCCEEDED( hr ) && pDstSurf ); + + hr = g_pD3DDevice->StretchRect( pSrcSurf, NULL, pDstSurf, NULL, D3DTEXF_NONE ); + Assert( SUCCEEDED( hr ) ); + + pSrcSurf->Release(); + pDstSurf->Release(); + } +} + + + +void CShaderAPIDx8::CopyRenderTargetToTexture( ShaderAPITextureHandle_t textureHandle ) +{ + LOCK_SHADERAPI(); + CopyRenderTargetToTextureEx( textureHandle, 0 ); +} + + +void CShaderAPIDx8::CopyTextureToRenderTargetEx( int nRenderTargetID, ShaderAPITextureHandle_t textureHandle, Rect_t *pSrcRect, Rect_t *pDstRect ) +{ + LOCK_SHADERAPI(); + VPROF( "CShaderAPIDx8::CopyRenderTargetToTexture" ); + + if ( !TextureIsAllocated( textureHandle ) ) + return; + + // Don't flush here!! If you have to flush here, then there is a driver bug. + // FlushHardware( ); + + AssertValidTextureHandle( textureHandle ); + Texture_t *pTexture = &GetTexture( textureHandle ); + Assert( pTexture ); + IDirect3DTexture *pD3DTexture = (IDirect3DTexture *)pTexture->GetTexture(); + Assert( pD3DTexture ); + +#if !defined( _X360 ) + IDirect3DSurface* pRenderTargetSurface; + HRESULT hr = Dx9Device()->GetRenderTarget( nRenderTargetID, &pRenderTargetSurface ); + if ( FAILED( hr ) ) + { + Assert( 0 ); + return; + } + + IDirect3DSurface *pDstSurf; + hr = pD3DTexture->GetSurfaceLevel( 0, &pDstSurf ); + Assert( !FAILED( hr ) ); + if ( FAILED( hr ) ) + { + pRenderTargetSurface->Release(); + return; + } + + bool tryblit = true; + if ( tryblit ) + { + RECORD_COMMAND( DX8_COPY_FRAMEBUFFER_TO_TEXTURE, 1 ); + RECORD_INT( textureHandle ); + + RECT srcRect, dstRect; + hr = Dx9Device()->StretchRect( pDstSurf, RectToRECT( pSrcRect, srcRect ), + pRenderTargetSurface, RectToRECT( pDstRect, dstRect ), D3DTEXF_LINEAR ); + Assert( !FAILED( hr ) ); + } + + pDstSurf->Release(); + pRenderTargetSurface->Release(); +#else + Assert( 0 ); +#endif +} + + +//----------------------------------------------------------------------------- +// modifies the vertex data when necessary +//----------------------------------------------------------------------------- +void CShaderAPIDx8::ModifyVertexData( ) +{ + // this should be a dead code path + Assert( 0 ); +#if 0 + // We have to modulate the vertex color by the constant color sometimes + if (IsModulatingVertexColor()) + { + m_ModifyBuilder.Reset(); + + float factor[4]; + unsigned char* pColor = (unsigned char*)&m_DynamicState.m_ConstantColor; + factor[0] = pColor[0] / 255.0f; + factor[1] = pColor[1] / 255.0f; + factor[2] = pColor[2] / 255.0f; + factor[3] = pColor[3] / 255.0f; + + for ( int i = 0; i < m_ModifyBuilder.VertexCount(); ++i ) + { + unsigned int color = m_ModifyBuilder.Color(); + unsigned char* pVertexColor = (unsigned char*)&color; + + pVertexColor[0] = (unsigned char)((float)pVertexColor[0] * factor[0]); + pVertexColor[1] = (unsigned char)((float)pVertexColor[1] * factor[1]); + pVertexColor[2] = (unsigned char)((float)pVertexColor[2] * factor[2]); + pVertexColor[3] = (unsigned char)((float)pVertexColor[3] * factor[3]); + m_ModifyBuilder.Color4ubv( pVertexColor ); + + m_ModifyBuilder.AdvanceVertex(); + } + } +#endif +} + +static const char *TextureArgToString( int arg ) +{ + static char buf[128]; + switch( arg & D3DTA_SELECTMASK ) + { + case D3DTA_DIFFUSE: + strcpy( buf, "D3DTA_DIFFUSE" ); + break; + case D3DTA_CURRENT: + strcpy( buf, "D3DTA_CURRENT" ); + break; + case D3DTA_TEXTURE: + strcpy( buf, "D3DTA_TEXTURE" ); + break; + case D3DTA_TFACTOR: + strcpy( buf, "D3DTA_TFACTOR" ); + break; + case D3DTA_SPECULAR: + strcpy( buf, "D3DTA_SPECULAR" ); + break; + case D3DTA_TEMP: + strcpy( buf, "D3DTA_TEMP" ); + break; + default: + strcpy( buf, "" ); + break; + } + + if( arg & D3DTA_COMPLEMENT ) + { + strcat( buf, "|D3DTA_COMPLEMENT" ); + } + if( arg & D3DTA_ALPHAREPLICATE ) + { + strcat( buf, "|D3DTA_ALPHAREPLICATE" ); + } + return buf; +} + +static const char *TextureOpToString( D3DTEXTUREOP op ) +{ + switch( op ) + { + case D3DTOP_DISABLE: + return "D3DTOP_DISABLE"; + case D3DTOP_SELECTARG1: + return "D3DTOP_SELECTARG1"; + case D3DTOP_SELECTARG2: + return "D3DTOP_SELECTARG2"; + case D3DTOP_MODULATE: + return "D3DTOP_MODULATE"; + case D3DTOP_MODULATE2X: + return "D3DTOP_MODULATE2X"; + case D3DTOP_MODULATE4X: + return "D3DTOP_MODULATE4X"; + case D3DTOP_ADD: + return "D3DTOP_ADD"; + case D3DTOP_ADDSIGNED: + return "D3DTOP_ADDSIGNED"; + case D3DTOP_ADDSIGNED2X: + return "D3DTOP_ADDSIGNED2X"; + case D3DTOP_SUBTRACT: + return "D3DTOP_SUBTRACT"; + case D3DTOP_ADDSMOOTH: + return "D3DTOP_ADDSMOOTH"; + case D3DTOP_BLENDDIFFUSEALPHA: + return "D3DTOP_BLENDDIFFUSEALPHA"; + case D3DTOP_BLENDTEXTUREALPHA: + return "D3DTOP_BLENDTEXTUREALPHA"; + case D3DTOP_BLENDFACTORALPHA: + return "D3DTOP_BLENDFACTORALPHA"; + case D3DTOP_BLENDTEXTUREALPHAPM: + return "D3DTOP_BLENDTEXTUREALPHAPM"; + case D3DTOP_BLENDCURRENTALPHA: + return "D3DTOP_BLENDCURRENTALPHA"; + case D3DTOP_PREMODULATE: + return "D3DTOP_PREMODULATE"; + case D3DTOP_MODULATEALPHA_ADDCOLOR: + return "D3DTOP_MODULATEALPHA_ADDCOLOR"; + case D3DTOP_MODULATECOLOR_ADDALPHA: + return "D3DTOP_MODULATECOLOR_ADDALPHA"; + case D3DTOP_MODULATEINVALPHA_ADDCOLOR: + return "D3DTOP_MODULATEINVALPHA_ADDCOLOR"; + case D3DTOP_MODULATEINVCOLOR_ADDALPHA: + return "D3DTOP_MODULATEINVCOLOR_ADDALPHA"; + case D3DTOP_BUMPENVMAP: + return "D3DTOP_BUMPENVMAP"; + case D3DTOP_BUMPENVMAPLUMINANCE: + return "D3DTOP_BUMPENVMAPLUMINANCE"; + case D3DTOP_DOTPRODUCT3: + return "D3DTOP_DOTPRODUCT3"; + case D3DTOP_MULTIPLYADD: + return "D3DTOP_MULTIPLYADD"; + case D3DTOP_LERP: + return "D3DTOP_LERP"; + default: + return ""; + } +} + +static const char *BlendModeToString( int blendMode ) +{ + switch( blendMode ) + { + case D3DBLEND_ZERO: + return "D3DBLEND_ZERO"; + case D3DBLEND_ONE: + return "D3DBLEND_ONE"; + case D3DBLEND_SRCCOLOR: + return "D3DBLEND_SRCCOLOR"; + case D3DBLEND_INVSRCCOLOR: + return "D3DBLEND_INVSRCCOLOR"; + case D3DBLEND_SRCALPHA: + return "D3DBLEND_SRCALPHA"; + case D3DBLEND_INVSRCALPHA: + return "D3DBLEND_INVSRCALPHA"; + case D3DBLEND_DESTALPHA: + return "D3DBLEND_DESTALPHA"; + case D3DBLEND_INVDESTALPHA: + return "D3DBLEND_INVDESTALPHA"; + case D3DBLEND_DESTCOLOR: + return "D3DBLEND_DESTCOLOR"; + case D3DBLEND_INVDESTCOLOR: + return "D3DBLEND_INVDESTCOLOR"; + case D3DBLEND_SRCALPHASAT: + return "D3DBLEND_SRCALPHASAT"; +#if !defined( _X360 ) + case D3DBLEND_BOTHSRCALPHA: + return "D3DBLEND_BOTHSRCALPHA"; + case D3DBLEND_BOTHINVSRCALPHA: + return "D3DBLEND_BOTHINVSRCALPHA"; +#endif + default: + return ""; + } +} + +//----------------------------------------------------------------------------- +// Spew Board State +//----------------------------------------------------------------------------- +void CShaderAPIDx8::SpewBoardState() +{ + // FIXME: This has regressed + return; +#ifdef DEBUG_BOARD_STATE +/* + { + static ID3DXFont* pFont = 0; + if (!pFont) + { + HFONT hFont = CreateFont( 0, 0, 0, 0, FW_DONTCARE, FALSE, FALSE, FALSE, + ANSI_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, + DEFAULT_PITCH | FF_MODERN, 0 ); + Assert( hFont != 0 ); + + HRESULT hr = D3DXCreateFont( Dx9Device(), hFont, &pFont ); + } + + static char buf[1024]; + static RECT r = { 0, 0, 640, 480 }; + + if (m_DynamicState.m_VertexBlend == 0) + return; + +#if 1 + D3DXMATRIX* m = &GetTransform(MATERIAL_MODEL); + D3DXMATRIX* m2 = &GetTransform(MATERIAL_MODEL + 1); + sprintf(buf,"FVF %x\n" + "[%6.3f %6.3f %6.3f %6.3f]\n[%6.3f %6.3f %6.3f %6.3f]\n" + "[%6.3f %6.3f %6.3f %6.3f]\n[%6.3f %6.3f %6.3f %6.3f]\n\n", + "[%6.3f %6.3f %6.3f %6.3f]\n[%6.3f %6.3f %6.3f %6.3f]\n", + "[%6.3f %6.3f %6.3f %6.3f]\n[%6.3f %6.3f %6.3f %6.3f]\n", + ShaderManager()->GetCurrentVertexShader(), + m->m[0][0], m->m[0][1], m->m[0][2], m->m[0][3], + m->m[1][0], m->m[1][1], m->m[1][2], m->m[1][3], + m->m[2][0], m->m[2][1], m->m[2][2], m->m[2][3], + m->m[3][0], m->m[3][1], m->m[3][2], m->m[3][3], + m2->m[0][0], m2->m[0][1], m2->m[0][2], m2->m[0][3], + m2->m[1][0], m2->m[1][1], m2->m[1][2], m2->m[1][3], + m2->m[2][0], m2->m[2][1], m2->m[2][2], m2->m[2][3], + m2->m[3][0], m2->m[3][1], m2->m[3][2], m2->m[3][3] + ); +#else + Vector4D *pVec2 = &m_DynamicState.m_pVectorVertexShaderConstant[VERTEX_SHADER_MODELVIEWPROJ]; + Vector4D *pVec3 = &m_DynamicState.m_pVectorVertexShaderConstant[VERTEX_SHADER_VIEWPROJ]; + Vector4D *pVec4 = &m_DynamicState.m_pVectorVertexShaderConstant[VERTEX_SHADER_MODEL]; + + sprintf(buf,"\n" + "[%6.3f %6.3f %6.3f %6.3f]\n[%6.3f %6.3f %6.3f %6.3f]\n" + "[%6.3f %6.3f %6.3f %6.3f]\n[%6.3f %6.3f %6.3f %6.3f]\n\n" + + "[%6.3f %6.3f %6.3f %6.3f]\n[%6.3f %6.3f %6.3f %6.3f]\n" + "[%6.3f %6.3f %6.3f %6.3f]\n[%6.3f %6.3f %6.3f %6.3f]\n\n" + + "[%6.3f %6.3f %6.3f %6.3f]\n[%6.3f %6.3f %6.3f %6.3f]\n" + "[%6.3f %6.3f %6.3f %6.3f]\n[%6.3f %6.3f %6.3f %6.3f]\n\n" + + "[%6.3f %6.3f %6.3f %6.3f]\n[%6.3f %6.3f %6.3f %6.3f]\n" + "[%6.3f %6.3f %6.3f %6.3f]\n[%6.3f %6.3f %6.3f %6.3f]\n", + + pVec1[0][0], pVec1[0][1], pVec1[0][2], pVec1[0][3], + pVec1[1][0], pVec1[1][1], pVec1[1][2], pVec1[1][3], + pVec1[2][0], pVec1[2][1], pVec1[2][2], pVec1[2][3], + pVec1[3][0], pVec1[3][1], pVec1[3][2], pVec1[3][3], + + pVec2[0][0], pVec2[0][1], pVec2[0][2], pVec2[0][3], + pVec2[1][0], pVec2[1][1], pVec2[1][2], pVec2[1][3], + pVec2[2][0], pVec2[2][1], pVec2[2][2], pVec2[2][3], + pVec2[3][0], pVec2[3][1], pVec2[3][2], pVec2[3][3], + + pVec3[0][0], pVec3[0][1], pVec3[0][2], pVec3[0][3], + pVec3[1][0], pVec3[1][1], pVec3[1][2], pVec3[1][3], + pVec3[2][0], pVec3[2][1], pVec3[2][2], pVec3[2][3], + pVec3[3][0], pVec3[3][1], pVec3[3][2], pVec3[3][3], + + pVec4[0][0], pVec4[0][1], pVec4[0][2], pVec4[0][3], + pVec4[1][0], pVec4[1][1], pVec4[1][2], pVec4[1][3], + pVec4[2][0], pVec4[2][1], pVec4[2][2], pVec4[2][3], + 0, 0, 0, 1 + ); +#endif + pFont->Begin(); + pFont->DrawText( buf, -1, &r, DT_LEFT | DT_TOP, + D3DCOLOR_RGBA( 255, 255, 255, 255 ) ); + pFont->End(); + + return; + } + +#if 0 + Vector4D *pVec2 = &m_DynamicState.m_pVectorVertexShaderConstant[VERTEX_SHADER_MODELVIEWPROJ]; + Vector4D *pVec3 = &m_DynamicState.m_pVectorVertexShaderConstant[VERTEX_SHADER_VIEWPROJ]; + Vector4D *pVec4 = &m_DynamicState.m_pVectorVertexShaderConstant[VERTEX_SHADER_MODEL]; + + static char buf2[1024]; + sprintf(buf2,"\n" + "[%6.3f %6.3f %6.3f %6.3f]\n[%6.3f %6.3f %6.3f %6.3f]\n" + "[%6.3f %6.3f %6.3f %6.3f]\n[%6.3f %6.3f %6.3f %6.3f]\n\n" + + "[%6.3f %6.3f %6.3f %6.3f]\n[%6.3f %6.3f %6.3f %6.3f]\n" + "[%6.3f %6.3f %6.3f %6.3f]\n[%6.3f %6.3f %6.3f %6.3f]\n\n" + + "[%6.3f %6.3f %6.3f %6.3f]\n[%6.3f %6.3f %6.3f %6.3f]\n" + "[%6.3f %6.3f %6.3f %6.3f]\n[%6.3f %6.3f %6.3f %6.3f]\n\n" + + "[%6.3f %6.3f %6.3f %6.3f]\n[%6.3f %6.3f %6.3f %6.3f]\n" + "[%6.3f %6.3f %6.3f %6.3f]\n[%6.3f %6.3f %6.3f %6.3f]\n", + + pVec1[0][0], pVec1[0][1], pVec1[0][2], pVec1[0][3], + pVec1[1][0], pVec1[1][1], pVec1[1][2], pVec1[1][3], + pVec1[2][0], pVec1[2][1], pVec1[2][2], pVec1[2][3], + pVec1[3][0], pVec1[3][1], pVec1[3][2], pVec1[3][3], + + pVec2[0][0], pVec2[0][1], pVec2[0][2], pVec2[0][3], + pVec2[1][0], pVec2[1][1], pVec2[1][2], pVec2[1][3], + pVec2[2][0], pVec2[2][1], pVec2[2][2], pVec2[2][3], + pVec2[3][0], pVec2[3][1], pVec2[3][2], pVec2[3][3], + + pVec3[0][0], pVec3[0][1], pVec3[0][2], pVec3[0][3], + pVec3[1][0], pVec3[1][1], pVec3[1][2], pVec3[1][3], + pVec3[2][0], pVec3[2][1], pVec3[2][2], pVec3[2][3], + pVec3[3][0], pVec3[3][1], pVec3[3][2], pVec3[3][3], + + pVec4[0][0], pVec4[0][1], pVec4[0][2], pVec4[0][3], + pVec4[1][0], pVec4[1][1], pVec4[1][2], pVec4[1][3], + pVec4[2][0], pVec4[2][1], pVec4[2][2], pVec4[2][3], + 0, 0, 0, 1.0f + ); + Plat_DebugString(buf2); + return; +#endif +*/ + + char buf[256]; + sprintf(buf, "\nSnapshot id %d : \n", m_TransitionTable.CurrentSnapshot() ); + Plat_DebugString(buf); + + ShadowState_t &boardState = m_TransitionTable.BoardState(); + ShadowShaderState_t &boardShaderState = m_TransitionTable.BoardShaderState(); + + sprintf(buf,"Depth States: ZFunc %d, ZWrite %d, ZEnable %d, ZBias %d\n", + boardState.m_ZFunc, boardState.m_ZWriteEnable, + boardState.m_ZEnable, boardState.m_ZBias ); + Plat_DebugString(buf); + sprintf(buf,"Cull Enable %d Cull Mode %d Color Write %d Fill %d Const Color Mod %d sRGBWriteEnable %d\n", + boardState.m_CullEnable, m_DynamicState.m_CullMode, boardState.m_ColorWriteEnable, + boardState.m_FillMode, boardShaderState.m_ModulateConstantColor, boardState.m_SRGBWriteEnable ); + Plat_DebugString(buf); + sprintf(buf,"Blend States: Blend Enable %d Test Enable %d Func %d SrcBlend %d (%s) DstBlend %d (%s)\n", + boardState.m_AlphaBlendEnable, boardState.m_AlphaTestEnable, + boardState.m_AlphaFunc, boardState.m_SrcBlend, BlendModeToString( boardState.m_SrcBlend ), + boardState.m_DestBlend, BlendModeToString( boardState.m_DestBlend ) ); + Plat_DebugString(buf); + int len = sprintf(buf,"Alpha Ref %d, Lighting: %d, Ambient Color %x, LightsEnabled ", + boardState.m_AlphaRef, boardState.m_Lighting, m_DynamicState.m_Ambient); + + int i; + for ( i = 0; i < g_pHardwareConfig->Caps().m_MaxNumLights; ++i) + { + len += sprintf(buf+len,"%d ", m_DynamicState.m_LightEnable[i] ); + } + sprintf(buf+len,"\n"); + Plat_DebugString(buf); + sprintf(buf,"Fixed Function: %d, VertexBlend %d\n", + boardState.m_UsingFixedFunction, m_DynamicState.m_VertexBlend ); + Plat_DebugString(buf); + + sprintf(buf,"Pass Vertex Usage: %llx Pixel Shader %p Vertex Shader %p\n", + boardShaderState.m_VertexUsage, ShaderManager()->GetCurrentPixelShader(), + ShaderManager()->GetCurrentVertexShader() ); + Plat_DebugString(buf); + + // REGRESSED!!!! + /* + D3DXMATRIX* m = &GetTransform(MATERIAL_MODEL); + sprintf(buf,"WorldMat [%4.3f %4.3f %4.3f %4.3f %4.3f %4.3f %4.3f %4.3f %4.3f %4.3f %4.3f %4.3f %4.3f %4.3f %4.3f %4.3f]\n", + m->m[0][0], m->m[0][1], m->m[0][2], m->m[0][3], + m->m[1][0], m->m[1][1], m->m[1][2], m->m[1][3], + m->m[2][0], m->m[2][1], m->m[2][2], m->m[2][3], + m->m[3][0], m->m[3][1], m->m[3][2], m->m[3][3] ); + Plat_DebugString(buf); + + m = &GetTransform(MATERIAL_MODEL + 1); + sprintf(buf,"WorldMat2 [%4.3f %4.3f %4.3f %4.3f %4.3f %4.3f %4.3f %4.3f %4.3f %4.3f %4.3f %4.3f %4.3f %4.3f %4.3f %4.3f]\n", + m->m[0][0], m->m[0][1], m->m[0][2], m->m[0][3], + m->m[1][0], m->m[1][1], m->m[1][2], m->m[1][3], + m->m[2][0], m->m[2][1], m->m[2][2], m->m[2][3], + m->m[3][0], m->m[3][1], m->m[3][2], m->m[3][3] ); + Plat_DebugString(buf); + + m = &GetTransform(MATERIAL_VIEW); + sprintf(buf,"ViewMat [%4.3f %4.3f %4.3f %4.3f %4.3f %4.3f %4.3f %4.3f %4.3f %4.3f %4.3f %4.3f]\n", + m->m[0][0], m->m[0][1], m->m[0][2], + m->m[1][0], m->m[1][1], m->m[1][2], + m->m[2][0], m->m[2][1], m->m[2][2], + m->m[3][0], m->m[3][1], m->m[3][2] ); + Plat_DebugString(buf); + + m = &GetTransform(MATERIAL_PROJECTION); + sprintf(buf,"ProjMat [%4.3f %4.3f %4.3f %4.3f %4.3f %4.3f %4.3f %4.3f %4.3f %4.3f %4.3f %4.3f]\n", + m->m[0][0], m->m[0][1], m->m[0][2], + m->m[1][0], m->m[1][1], m->m[1][2], + m->m[2][0], m->m[2][1], m->m[2][2], + m->m[3][0], m->m[3][1], m->m[3][2] ); + Plat_DebugString(buf); + + for (i = 0; i < GetTextureStageCount(); ++i) + { + m = &GetTransform(MATERIAL_TEXTURE0 + i); + sprintf(buf,"TexMat%d [%4.3f %4.3f %4.3f %4.3f %4.3f %4.3f %4.3f %4.3f %4.3f %4.3f %4.3f %4.3f]\n", + i, m->m[0][0], m->m[0][1], m->m[0][2], + m->m[1][0], m->m[1][1], m->m[1][2], + m->m[2][0], m->m[2][1], m->m[2][2], + m->m[3][0], m->m[3][1], m->m[3][2] ); + Plat_DebugString(buf); + } + */ + + sprintf(buf,"Viewport (%d %d) [%d %d] %4.3f %4.3f\n", + m_DynamicState.m_Viewport.X, m_DynamicState.m_Viewport.Y, + m_DynamicState.m_Viewport.Width, m_DynamicState.m_Viewport.Height, + m_DynamicState.m_Viewport.MinZ, m_DynamicState.m_Viewport.MaxZ); + Plat_DebugString(buf); + + for (i = 0; i < MAX_TEXTURE_STAGES; ++i) + { + sprintf(buf,"Stage %d :\n", i); + Plat_DebugString(buf); + sprintf(buf," Color Op: %d (%s) Color Arg1: %d (%s)", + boardState.m_TextureStage[i].m_ColorOp, + TextureOpToString( boardState.m_TextureStage[i].m_ColorOp ), + boardState.m_TextureStage[i].m_ColorArg1, + TextureArgToString( boardState.m_TextureStage[i].m_ColorArg1 ) ); + Plat_DebugString(buf); + sprintf( buf, " Color Arg2: %d (%s)\n", + boardState.m_TextureStage[i].m_ColorArg2, + TextureArgToString( boardState.m_TextureStage[i].m_ColorArg2 ) ); + Plat_DebugString(buf); + sprintf(buf," Alpha Op: %d (%s) Alpha Arg1: %d (%s)", + boardState.m_TextureStage[i].m_AlphaOp, + TextureOpToString( boardState.m_TextureStage[i].m_AlphaOp ), + boardState.m_TextureStage[i].m_AlphaArg1, + TextureArgToString( boardState.m_TextureStage[i].m_AlphaArg1 ) ); + Plat_DebugString(buf); + sprintf(buf," Alpha Arg2: %d (%s)\n", + boardState.m_TextureStage[i].m_AlphaArg2, + TextureArgToString( boardState.m_TextureStage[i].m_AlphaArg2 ) ); + Plat_DebugString(buf); + } + + for ( int i = 0; i < MAX_SAMPLERS; ++i ) + { + sprintf(buf," Texture Enabled: %d Bound Texture: %d UWrap: %d VWrap: %d\n", + SamplerState(i).m_TextureEnable, GetBoundTextureBindId( (Sampler_t)i ), + SamplerState(i).m_UTexWrap, SamplerState(i).m_VTexWrap ); + Plat_DebugString(buf); + sprintf(buf," Mag Filter: %d Min Filter: %d Mip Filter: %d\n", + SamplerState(i).m_MagFilter, SamplerState(i).m_MinFilter, + SamplerState(i).m_MipFilter ); + sprintf(buf," MaxMipLevel: %d\n", SamplerState(i).m_FinestMipmapLevel ); + Plat_DebugString(buf); + } +#else + Plat_DebugString("::SpewBoardState() Not Implemented Yet"); +#endif +} + +//----------------------------------------------------------------------------- +// Begin a render pass +//----------------------------------------------------------------------------- +void CShaderAPIDx8::BeginPass( StateSnapshot_t snapshot ) +{ + LOCK_SHADERAPI(); + VPROF("CShaderAPIDx8::BeginPass"); + if (IsDeactivated()) + return; + + m_nCurrentSnapshot = snapshot; +// Assert( m_pRenderMesh ); + // FIXME: This only does anything with temp meshes, so don't bother yet for the new code. + if( m_pRenderMesh ) + { + m_pRenderMesh->BeginPass( ); + } +} + + +//----------------------------------------------------------------------------- +// Render da polygon! +//----------------------------------------------------------------------------- +void CShaderAPIDx8::RenderPass( int nPass, int nPassCount ) +{ + if ( IsDeactivated() ) + return; + + Assert( m_nCurrentSnapshot != -1 ); +// Assert( m_pRenderMesh ); MESHFIXME + + m_TransitionTable.UseSnapshot( m_nCurrentSnapshot ); + CommitPerPassStateChanges( m_nCurrentSnapshot ); + + // Make sure that we bound a texture for every stage that is enabled + // NOTE: not enabled/finished yet... see comment in CShaderAPIDx8::ApplyTextureEnable +// int nSampler; +// for ( nSampler = 0; nSampler < g_pHardwareConfig->GetSamplerCount(); nSampler++ ) +// { +// if ( SamplerState( nSampler ).m_TextureEnable ) +// { +// } +// } + +#ifdef DEBUG_BOARD_STATE + // Spew out render state... + if ( m_pMaterial->PerformDebugTrace() ) + { + SpewBoardState(); + } +#endif + +#ifdef TEST_CACHE_LOCKS + g_pDataCache->Flush(); +#endif + +// Assert( m_pRenderMesh ); MESHFIXME + if ( m_pRenderMesh ) + { + m_pRenderMesh->RenderPass(); + } + else + { + MeshMgr()->RenderPassWithVertexAndIndexBuffers(); + } + m_nCurrentSnapshot = -1; +} + + +//----------------------------------------------------------------------------- +// Matrix mode +//----------------------------------------------------------------------------- +void CShaderAPIDx8::MatrixMode( MaterialMatrixMode_t matrixMode ) +{ + // NOTE!!!!!! + // The only time that m_MatrixMode is used is for texture matrices. Do not use + // it for anything else unless you change this code! + if ( matrixMode >= MATERIAL_TEXTURE0 && matrixMode <= MATERIAL_TEXTURE7 ) + { + m_MatrixMode = ( D3DTRANSFORMSTATETYPE )( matrixMode - MATERIAL_TEXTURE0 + D3DTS_TEXTURE0 ); + } + else + { + m_MatrixMode = (D3DTRANSFORMSTATETYPE)-1; + } + + m_CurrStack = GetMatrixStack( matrixMode ); +} + +//----------------------------------------------------------------------------- +// the current camera position in world space. +//----------------------------------------------------------------------------- +void CShaderAPIDx8::GetWorldSpaceCameraPosition( float* pPos ) const +{ + memcpy( pPos, m_WorldSpaceCameraPositon.Base(), sizeof( float[3] ) ); +} + +void CShaderAPIDx8::CacheWorldSpaceCameraPosition() +{ + D3DXMATRIX& view = GetTransform(MATERIAL_VIEW); + m_WorldSpaceCameraPositon[0] = + -( view( 3, 0 ) * view( 0, 0 ) + + view( 3, 1 ) * view( 0, 1 ) + + view( 3, 2 ) * view( 0, 2 ) ); + m_WorldSpaceCameraPositon[1] = + -( view( 3, 0 ) * view( 1, 0 ) + + view( 3, 1 ) * view( 1, 1 ) + + view( 3, 2 ) * view( 1, 2 ) ); + m_WorldSpaceCameraPositon[2] = + -( view( 3, 0 ) * view( 2, 0 ) + + view( 3, 1 ) * view( 2, 1 ) + + view( 3, 2 ) * view( 2, 2 ) ); + m_WorldSpaceCameraPositon[3] = 1.0f; + + // Protect against zero, as some pixel shaders will divide by this in CalcWaterFogAlpha() in common_ps_fxc.h + if ( fabs( m_WorldSpaceCameraPositon[2] ) <= 0.00001f ) + { + m_WorldSpaceCameraPositon[2] = 0.01f; + } +} + + +//----------------------------------------------------------------------------- +// Computes a matrix which includes the poly offset given an initial projection matrix +//----------------------------------------------------------------------------- +void CShaderAPIDx8::ComputePolyOffsetMatrix( const D3DXMATRIX& matProjection, D3DXMATRIX &matProjectionOffset ) +{ + // We never need to do this on hardware that can handle zbias + if ( g_pHardwareConfig->Caps().m_ZBiasAndSlopeScaledDepthBiasSupported ) + return; + + float offsetVal = + -1.0f * (m_DesiredState.m_Viewport.MaxZ - m_DesiredState.m_Viewport.MinZ) / + 16384.0f; + + D3DXMATRIX offset; + D3DXMatrixTranslation( &offset, 0.0f, 0.0f, offsetVal ); + D3DXMatrixMultiply( &matProjectionOffset, &matProjection, &offset ); +} + + +//----------------------------------------------------------------------------- +// Caches off the poly-offset projection matrix +//----------------------------------------------------------------------------- +void CShaderAPIDx8::CachePolyOffsetProjectionMatrix() +{ + ComputePolyOffsetMatrix( GetTransform(MATERIAL_PROJECTION), m_CachedPolyOffsetProjectionMatrix ); +} + + +//----------------------------------------------------------------------------- +// Performs a flush on the matrix state if necessary +//----------------------------------------------------------------------------- +bool CShaderAPIDx8::MatrixIsChanging( TransformType_t type ) +{ + if ( IsDeactivated() ) + { + return false; + } + + // early out if the transform is already one of our standard types + if ((type != TRANSFORM_IS_GENERAL) && (type == m_DynamicState.m_TransformType[m_CurrStack])) + return false; + + // Only flush state if we're changing something other than a texture transform + int textureMatrix = m_CurrStack - MATERIAL_TEXTURE0; + if (( textureMatrix < 0 ) || (textureMatrix >= NUM_TEXTURE_TRANSFORMS)) + FlushBufferedPrimitivesInternal(); + + return true; +} + +void CShaderAPIDx8::SetTextureTransformDimension( TextureStage_t textureMatrix, int dimension, bool projected ) +{ + D3DTEXTURETRANSFORMFLAGS textureTransformFlags = ( D3DTEXTURETRANSFORMFLAGS )dimension; + if( projected ) + { + Assert( sizeof( int ) == sizeof( D3DTEXTURETRANSFORMFLAGS ) ); + ( *( int * )&textureTransformFlags ) |= D3DTTFF_PROJECTED; + } + + if (TextureStage(textureMatrix).m_TextureTransformFlags != textureTransformFlags ) + { + SetTextureStageState( textureMatrix, D3DTSS_TEXTURETRANSFORMFLAGS, textureTransformFlags ); + TextureStage(textureMatrix).m_TextureTransformFlags = textureTransformFlags; + } +} + +void CShaderAPIDx8::DisableTextureTransform( TextureStage_t textureMatrix ) +{ + if (TextureStage(textureMatrix).m_TextureTransformFlags != D3DTTFF_DISABLE ) + { + SetTextureStageState( textureMatrix, D3DTSS_TEXTURETRANSFORMFLAGS, D3DTTFF_DISABLE ); + TextureStage(textureMatrix).m_TextureTransformFlags = D3DTTFF_DISABLE; + } +} + +void CShaderAPIDx8::SetBumpEnvMatrix( TextureStage_t textureStage, float m00, float m01, float m10, float m11 ) +{ + TextureStageState_t &textureStageState = TextureStage( textureStage ); + + if( textureStageState.m_BumpEnvMat00 != m00 || + textureStageState.m_BumpEnvMat01 != m01 || + textureStageState.m_BumpEnvMat10 != m10 || + textureStageState.m_BumpEnvMat11 != m11 ) + { + SetTextureStageState( textureStage, D3DTSS_BUMPENVMAT00, *( ( LPDWORD ) (&m00) ) ); + SetTextureStageState( textureStage, D3DTSS_BUMPENVMAT01, *( ( LPDWORD ) (&m01) ) ); + SetTextureStageState( textureStage, D3DTSS_BUMPENVMAT10, *( ( LPDWORD ) (&m10) ) ); + SetTextureStageState( textureStage, D3DTSS_BUMPENVMAT11, *( ( LPDWORD ) (&m11) ) ); + textureStageState.m_BumpEnvMat00 = m00; + textureStageState.m_BumpEnvMat01 = m01; + textureStageState.m_BumpEnvMat10 = m10; + textureStageState.m_BumpEnvMat11 = m11; + } +} + +//----------------------------------------------------------------------------- +// Sets the actual matrix state +//----------------------------------------------------------------------------- +void CShaderAPIDx8::UpdateMatrixTransform( TransformType_t type ) +{ + int textureMatrix = m_CurrStack - MATERIAL_TEXTURE0; + if (( textureMatrix >= 0 ) && (textureMatrix < NUM_TEXTURE_TRANSFORMS)) + { + // NOTE: Flush shouldn't happen here because we + // expect that texture transforms will be set within the shader + + // FIXME: We only want to use D3DTTFF_COUNT3 for cubemaps + // D3DTFF_COUNT2 is used for non-cubemaps. Of course, if there's + // no performance penalty for COUNT3, we should just use that. + D3DTEXTURETRANSFORMFLAGS transformFlags; + transformFlags = (type == TRANSFORM_IS_IDENTITY) ? D3DTTFF_DISABLE : D3DTTFF_COUNT3; + + if (TextureStage(textureMatrix).m_TextureTransformFlags != transformFlags ) + { + SetTextureStageState( textureMatrix, D3DTSS_TEXTURETRANSFORMFLAGS, transformFlags ); + TextureStage(textureMatrix).m_TextureTransformFlags = transformFlags; + } + } + + m_DynamicState.m_TransformType[m_CurrStack] = type; + m_DynamicState.m_TransformChanged[m_CurrStack] = STATE_CHANGED; + +#ifdef _DEBUG + // Store off the board state + D3DXMATRIX *pSrc = &GetTransform(m_CurrStack); + D3DXMATRIX *pDst = &m_DynamicState.m_Transform[m_CurrStack]; +// Assert( *pSrc != *pDst ); + memcpy( pDst, pSrc, sizeof(D3DXMATRIX) ); +#endif + + if ( m_CurrStack == MATERIAL_VIEW ) + { + CacheWorldSpaceCameraPosition(); + } + + if ( !IsX360() && m_CurrStack == MATERIAL_PROJECTION ) + { + CachePolyOffsetProjectionMatrix(); + } + + // Any time the view or projection matrix changes, the user clip planes need recomputing.... + // Assuming we're not overriding the user clip transform + if ( ( m_CurrStack == MATERIAL_PROJECTION ) || + ( ( m_CurrStack == MATERIAL_VIEW ) && ( !m_DynamicState.m_bUserClipTransformOverride ) ) ) + { + MarkAllUserClipPlanesDirty(); + } + + // Set the state if it's a texture transform + if ( (m_CurrStack >= MATERIAL_TEXTURE0) && (m_CurrStack <= MATERIAL_TEXTURE7) ) + { + SetTransform( m_MatrixMode, &GetTransform(m_CurrStack) ); + } +} + + +//-------------------------------------------------------------------------------- +// deformations +//-------------------------------------------------------------------------------- +void CShaderAPIDx8::PushDeformation( DeformationBase_t const *pDef ) +{ + Assert( m_pDeformationStackPtr > m_DeformationStack ); + --m_pDeformationStackPtr; + m_pDeformationStackPtr->m_nDeformationType = pDef->m_eType; + + switch( pDef->m_eType ) + { + case DEFORMATION_CLAMP_TO_BOX_IN_WORLDSPACE: + { + BoxDeformation_t const *pBox = reinterpret_cast< const BoxDeformation_t *>( pDef ); + m_pDeformationStackPtr->m_nNumParameters = 16; + memcpy( m_pDeformationStackPtr->m_flDeformationParameters, &( pBox->m_SourceMins.x ), 16 * sizeof( float ) ); + break; + } + break; + + default: + Assert( 0 ); + } +} + + +void CShaderAPIDx8::PopDeformation( ) +{ + Assert( m_pDeformationStackPtr != m_DeformationStack + DEFORMATION_STACK_DEPTH ); + ++m_pDeformationStackPtr; +} + +int CShaderAPIDx8::GetNumActiveDeformations( void ) const +{ + return ( m_DeformationStack + DEFORMATION_STACK_DEPTH ) - m_pDeformationStackPtr; +} + + +// for shaders to set vertex shader constants. returns a packed state which can be used to set the dynamic combo +int CShaderAPIDx8::GetPackedDeformationInformation( int nMaskOfUnderstoodDeformations, + float *pConstantValuesOut, + int nBufferSize, + int nMaximumDeformations, + int *pDefCombosOut ) const +{ + int nCombosFound = 0; + memset( pDefCombosOut, 0, sizeof( pDefCombosOut[0] ) * nMaximumDeformations ); + size_t nRemainingBufferSize = nBufferSize; + + for( const Deformation_t *i = m_DeformationStack + DEFORMATION_STACK_DEPTH -1; i >= m_pDeformationStackPtr; i-- ) + { + int nFloatsOut = 4 * ( ( i->m_nNumParameters + 3 )>> 2 ); + if ( + ( ( 1 << i->m_nDeformationType ) & nMaskOfUnderstoodDeformations ) && + ( nRemainingBufferSize >= ( nFloatsOut * sizeof( float ) ) ) ) + { + memcpy( pConstantValuesOut, i->m_flDeformationParameters, nFloatsOut * sizeof( float ) ); + pConstantValuesOut += nFloatsOut; + nRemainingBufferSize -= nFloatsOut * sizeof( float ); + ( *pDefCombosOut++ ) = i->m_nDeformationType; + nCombosFound++; + } + } + return nCombosFound; +} + + + + +//----------------------------------------------------------------------------- +// Matrix stack operations +//----------------------------------------------------------------------------- + +void CShaderAPIDx8::PushMatrix() +{ + // NOTE: No matrix transform update needed here. + m_pMatrixStack[m_CurrStack]->Push(); +} + +void CShaderAPIDx8::PopMatrix() +{ + if (MatrixIsChanging()) + { + m_pMatrixStack[m_CurrStack]->Pop(); + UpdateMatrixTransform(); + } +} + +void CShaderAPIDx8::LoadIdentity( ) +{ + if (MatrixIsChanging(TRANSFORM_IS_IDENTITY)) + { + m_pMatrixStack[m_CurrStack]->LoadIdentity( ); + UpdateMatrixTransform( TRANSFORM_IS_IDENTITY ); + } +} + +void CShaderAPIDx8::LoadCameraToWorld( ) +{ + if (MatrixIsChanging(TRANSFORM_IS_CAMERA_TO_WORLD)) + { + // could just use the transpose instead, if we know there's no scale + float det; + D3DXMATRIX inv; + D3DXMatrixInverse( &inv, &det, &GetTransform(MATERIAL_VIEW) ); + + // Kill translation + inv.m[3][0] = inv.m[3][1] = inv.m[3][2] = 0.0f; + + m_pMatrixStack[m_CurrStack]->LoadMatrix( &inv ); + UpdateMatrixTransform( TRANSFORM_IS_CAMERA_TO_WORLD ); + } +} + +void CShaderAPIDx8::LoadMatrix( float *m ) +{ + // Check for identity... + if ( (fabs(m[0] - 1.0f) < 1e-3) && (fabs(m[5] - 1.0f) < 1e-3) && (fabs(m[10] - 1.0f) < 1e-3) && (fabs(m[15] - 1.0f) < 1e-3) && + (fabs(m[1]) < 1e-3) && (fabs(m[2]) < 1e-3) && (fabs(m[3]) < 1e-3) && + (fabs(m[4]) < 1e-3) && (fabs(m[6]) < 1e-3) && (fabs(m[7]) < 1e-3) && + (fabs(m[8]) < 1e-3) && (fabs(m[9]) < 1e-3) && (fabs(m[11]) < 1e-3) && + (fabs(m[12]) < 1e-3) && (fabs(m[13]) < 1e-3) && (fabs(m[14]) < 1e-3) ) + { + LoadIdentity(); + return; + } + + if (MatrixIsChanging()) + { + m_pMatrixStack[m_CurrStack]->LoadMatrix( (D3DXMATRIX*)m ); + UpdateMatrixTransform(); + } +} + +void CShaderAPIDx8::LoadBoneMatrix( int boneIndex, const float *m ) +{ + if ( IsDeactivated() ) + return; + + memcpy( m_boneMatrix[boneIndex].Base(), m, sizeof(float)*12 ); + if ( boneIndex > m_maxBoneLoaded ) + { + m_maxBoneLoaded = boneIndex; + } + if ( boneIndex == 0 ) + { + MatrixMode( MATERIAL_MODEL ); + VMatrix transposeMatrix; + transposeMatrix.Init( *(matrix3x4_t *)m ); + MatrixTranspose( transposeMatrix, transposeMatrix ); + LoadMatrix( (float*)transposeMatrix.m ); + } +} + +//----------------------------------------------------------------------------- +// Commits morph target factors +//----------------------------------------------------------------------------- +static void CommitFlexWeights( IDirect3DDevice9 *pDevice, const DynamicState_t &desiredState, + DynamicState_t ¤tState, bool bForce ) +{ + if ( IsX360() ) + { + // not supporting for 360 + return; + } + + CommitVertexShaderConstantRange( pDevice, desiredState, currentState, bForce, + VERTEX_SHADER_FLEX_WEIGHTS, VERTEX_SHADER_MAX_FLEX_WEIGHT_COUNT ); +} + +void CShaderAPIDx8::SetFlexWeights( int nFirstWeight, int nCount, const MorphWeight_t* pWeights ) +{ + if ( IsX360() ) + { + // not supported for 360 + return; + } + + LOCK_SHADERAPI(); + if ( g_pHardwareConfig->Caps().m_NumVertexShaderConstants < VERTEX_SHADER_FLEX_WEIGHTS + VERTEX_SHADER_MAX_FLEX_WEIGHT_COUNT ) + return; + + if ( nFirstWeight + nCount > VERTEX_SHADER_MAX_FLEX_WEIGHT_COUNT ) + { + Warning( "Attempted to set too many flex weights! Max is %d\n", VERTEX_SHADER_MAX_FLEX_WEIGHT_COUNT ); + nCount = VERTEX_SHADER_MAX_FLEX_WEIGHT_COUNT - nFirstWeight; + } + + if ( nCount <= 0 ) + return; + + float *pDest = m_DesiredState.m_pVectorVertexShaderConstant[ VERTEX_SHADER_FLEX_WEIGHTS + nFirstWeight ].Base(); + memcpy( pDest, pWeights, nCount * sizeof(MorphWeight_t) ); + + ADD_COMMIT_FUNC( COMMIT_PER_DRAW, COMMIT_VERTEX_SHADER, CommitFlexWeights ); +} + +void CShaderAPIDx8::MultMatrix( float *m ) +{ + if (MatrixIsChanging()) + { + m_pMatrixStack[m_CurrStack]->MultMatrix( (D3DXMATRIX*)m ); + UpdateMatrixTransform(); + } +} + +void CShaderAPIDx8::MultMatrixLocal( float *m ) +{ + if (MatrixIsChanging()) + { + m_pMatrixStack[m_CurrStack]->MultMatrixLocal( (D3DXMATRIX*)m ); + UpdateMatrixTransform(); + } +} + +void CShaderAPIDx8::Rotate( float angle, float x, float y, float z ) +{ + if (MatrixIsChanging()) + { + D3DXVECTOR3 axis( x, y, z ); + m_pMatrixStack[m_CurrStack]->RotateAxisLocal( &axis, M_PI * angle / 180.0f ); + UpdateMatrixTransform(); + } +} + +void CShaderAPIDx8::Translate( float x, float y, float z ) +{ + if (MatrixIsChanging()) + { + m_pMatrixStack[m_CurrStack]->TranslateLocal( x, y, z ); + UpdateMatrixTransform(); + } +} + +void CShaderAPIDx8::Scale( float x, float y, float z ) +{ + if (MatrixIsChanging()) + { + m_pMatrixStack[m_CurrStack]->ScaleLocal( x, y, z ); + UpdateMatrixTransform(); + } +} + +void CShaderAPIDx8::ScaleXY( float x, float y ) +{ + if (MatrixIsChanging()) + { + m_pMatrixStack[m_CurrStack]->ScaleLocal( x, y, 1.0f ); + UpdateMatrixTransform(); + } +} + +void CShaderAPIDx8::Ortho( double left, double top, double right, double bottom, double zNear, double zFar ) +{ + if (MatrixIsChanging()) + { + D3DXMATRIX matrix; + + // FIXME: This is being used incorrectly! Should read: + // D3DXMatrixOrthoOffCenterRH( &matrix, left, right, bottom, top, zNear, zFar ); + // Which is certainly why we need these extra -1 scales in y. Bleah + + // NOTE: The camera can be imagined as the following diagram: + // /z + // / + // /____ x Z is going into the screen + // | + // | + // |y + // + // (0,0,z) represents the upper-left corner of the screen. + // Our projection transform needs to transform from this space to a LH coordinate + // system that looks thusly: + // + // y| /z + // | / + // |/____ x Z is going into the screen + // + // Where x,y lies between -1 and 1, and z lies from 0 to 1 + // This is because the viewport transformation from projection space to pixels + // introduces a -1 scale in the y coordinates +// D3DXMatrixOrthoOffCenterLH( &matrix, left, right, bottom, top, zNear, zFar ); + + D3DXMatrixOrthoOffCenterRH( &matrix, left, right, top, bottom, zNear, zFar ); + m_pMatrixStack[m_CurrStack]->MultMatrixLocal(&matrix); + Assert( m_CurrStack == MATERIAL_PROJECTION ); + UpdateMatrixTransform(); + } +} + +void CShaderAPIDx8::PerspectiveX( double fovx, double aspect, double zNear, double zFar ) +{ + if (MatrixIsChanging()) + { + float width = 2 * zNear * tan( fovx * M_PI / 360.0 ); + float height = width / aspect; + Assert( m_CurrStack == MATERIAL_PROJECTION ); + D3DXMATRIX rh; + D3DXMatrixPerspectiveRH( &rh, width, height, zNear, zFar ); + m_pMatrixStack[m_CurrStack]->MultMatrixLocal(&rh); + UpdateMatrixTransform(); + } +} + +void CShaderAPIDx8::PerspectiveOffCenterX( double fovx, double aspect, double zNear, double zFar, double bottom, double top, double left, double right ) +{ + if (MatrixIsChanging()) + { + float width = 2 * zNear * tan( fovx * M_PI / 360.0 ); + float height = width / aspect; + + // bottom, top, left, right are 0..1 so convert to -1..1 + float flFrontPlaneLeft = -(width/2.0f) * (1.0f - left) + left * (width/2.0f); + float flFrontPlaneRight = -(width/2.0f) * (1.0f - right) + right * (width/2.0f); + float flFrontPlaneBottom = -(height/2.0f) * (1.0f - bottom) + bottom * (height/2.0f); + float flFrontPlaneTop = -(height/2.0f) * (1.0f - top) + top * (height/2.0f); + + Assert( m_CurrStack == MATERIAL_PROJECTION ); + D3DXMATRIX rh; + D3DXMatrixPerspectiveOffCenterRH( &rh, flFrontPlaneLeft, flFrontPlaneRight, flFrontPlaneBottom, flFrontPlaneTop, zNear, zFar ); + m_pMatrixStack[m_CurrStack]->MultMatrixLocal(&rh); + UpdateMatrixTransform(); + } +} + +void CShaderAPIDx8::PickMatrix( int x, int y, int width, int height ) +{ + if (MatrixIsChanging()) + { + Assert( m_CurrStack == MATERIAL_PROJECTION ); + + // This is going to create a matrix to append to the standard projection. + // Projection space goes from -1 to 1 in x and y. This matrix we append + // will transform the pick region to -1 to 1 in projection space + ShaderViewport_t viewport; + GetViewports( &viewport, 1 ); + + int vx = viewport.m_nTopLeftX; + int vy = viewport.m_nTopLeftX; + int vwidth = viewport.m_nWidth; + int vheight = viewport.m_nHeight; + + // Compute the location of the pick region in projection space... + float px = 2.0 * (float)(x - vx) / (float)vwidth - 1; + float py = 2.0 * (float)(y - vy)/ (float)vheight - 1; + float pw = 2.0 * (float)width / (float)vwidth; + float ph = 2.0 * (float)height / (float)vheight; + + // we need to translate (px, py) to the origin + // and scale so (pw,ph) -> (2, 2) + D3DXMATRIX matrix; + D3DXMatrixIdentity( &matrix ); + matrix.m[0][0] = 2.0 / pw; + matrix.m[1][1] = 2.0 / ph; + matrix.m[3][0] = -2.0 * px / pw; + matrix.m[3][1] = -2.0 * py / ph; + + m_pMatrixStack[m_CurrStack]->MultMatrixLocal(&matrix); + UpdateMatrixTransform(); + } +} + +void CShaderAPIDx8::GetMatrix( MaterialMatrixMode_t matrixMode, float *dst ) +{ + memcpy( dst, (void*)(FLOAT*)GetTransform(matrixMode), sizeof(D3DXMATRIX) ); +} + + +//----------------------------------------------------------------------------- +// Did a transform change? +//----------------------------------------------------------------------------- +inline bool CShaderAPIDx8::VertexShaderTransformChanged( int i ) +{ + return (m_DynamicState.m_TransformChanged[i] & STATE_CHANGED_VERTEX_SHADER) != 0; +} + +inline bool CShaderAPIDx8::FixedFunctionTransformChanged( int i ) +{ + return (m_DynamicState.m_TransformChanged[i] & STATE_CHANGED_FIXED_FUNCTION) != 0; +} + + +const D3DXMATRIX &CShaderAPIDx8::GetProjectionMatrix( void ) +{ + bool bUsingZBiasProjectionMatrix = + !g_pHardwareConfig->Caps().m_ZBiasAndSlopeScaledDepthBiasSupported && + ( m_TransitionTable.CurrentSnapshot() != -1 ) && + m_TransitionTable.CurrentShadowState() && + m_TransitionTable.CurrentShadowState()->m_ZBias; + + if ( !m_DynamicState.m_FastClipEnabled ) + { + if ( bUsingZBiasProjectionMatrix ) + return m_CachedPolyOffsetProjectionMatrix; + + return GetTransform( MATERIAL_PROJECTION ); + } + + if ( bUsingZBiasProjectionMatrix ) + return m_CachedFastClipPolyOffsetProjectionMatrix; + + return m_CachedFastClipProjectionMatrix; +} + + +//----------------------------------------------------------------------------- +// Workaround hack for visualization of selection mode +//----------------------------------------------------------------------------- +void CShaderAPIDx8::SetupSelectionModeVisualizationState() +{ + Dx9Device()->SetRenderState( D3DRS_CULLMODE, D3DCULL_NONE ); + + D3DXMATRIX ident; + D3DXMatrixIdentity( &ident ); + SetTransform( D3DTS_WORLD, &ident ); + SetTransform( D3DTS_VIEW, &ident ); + SetTransform( D3DTS_PROJECTION, &ident ); + + if ( g_pHardwareConfig->Caps().m_SupportsPixelShaders ) + { + SetVertexShaderConstant( VERTEX_SHADER_VIEWPROJ, ident, 4 ); + SetVertexShaderConstant( VERTEX_SHADER_MODELVIEWPROJ, ident, 4 ); + float *pRowTwo = (float *)ident + 8; + SetVertexShaderConstant( VERTEX_SHADER_MODELVIEWPROJ_THIRD_ROW, pRowTwo, 1 ); // Row two of an identity matrix + SetVertexShaderConstant( VERTEX_SHADER_MODEL, ident, 3 * NUM_MODEL_TRANSFORMS ); + } +} + + +//----------------------------------------------------------------------------- +// Set view transforms +//----------------------------------------------------------------------------- + +static void printmat4x4( char *label, float *m00 ) +{ + // print label.. + // fetch 4 from row, print as a row + // fetch 4 from column, print as a row + +#ifdef DX_TO_GL_ABSTRACTION + float row[4]; + float col[4]; + + GLMPRINTF(("-M- -- %s --", label )); + for( int n=0; n<4; n++ ) + { + // extract row and column floats + for( int i=0; i<4;i++) + { + row[i] = m00[(n*4)+i]; + col[i] = m00[(i*4)+n]; + } + GLMPRINTF(( "-M- [ %7.4f %7.4f %7.4f %7.4f ] T=> [ %7.4f %7.4f %7.4f %7.4f ]", + row[0],row[1],row[2],row[3], + col[0],col[1],col[2],col[3] + )); + } + GLMPRINTF(("-M-")); +#endif +} + +void CShaderAPIDx8::SetVertexShaderViewProj() +{ + //GLM_FUNC; + //GLMPRINTF(( ">-M- SetVertexShaderViewProj" )); + + if (g_pHardwareConfig->Caps().m_SupportsPixelShaders) + { + D3DXMATRIX transpose; + if(0) + { + transpose = GetTransform(MATERIAL_VIEW) * GetProjectionMatrix(); + D3DXMatrixTranspose( &transpose, &transpose ); + } + else + { + // show work + D3DXMATRIX matView,matProj; + + matView = GetTransform(MATERIAL_VIEW); + matProj = GetProjectionMatrix(); + transpose = matView * matProj; + + //printmat4x4( "matView", (float*)&matView ); + //printmat4x4( "matProj", (float*)&matProj ); + //printmat4x4( "result (view * proj) pre-transpose", (float*)&transpose ); + + D3DXMatrixTranspose( &transpose, &transpose ); + + #if 0 // turned off while we try to do fixup-Y in shader translate + if (IsPosix()) // flip all shader projection matrices for Y on GL since you can't have an upside-down viewport specification + { + // flip Y + transpose._21 *= -1.0f; + transpose._22 *= -1.0f; + transpose._23 *= -1.0f; + transpose._24 *= -1.0f; + } + #endif + + //printmat4x4( "result (view * proj) post-transpose", (float*)&transpose ); + } + + + SetVertexShaderConstant( VERTEX_SHADER_VIEWPROJ, transpose, 4 ); + + // If we're doing FastClip, the above viewproj matrix won't work well for + // vertex shaders which compute projPos.z, hence we'll compute a more useful + // viewproj and put the third row of it in another constant + transpose = GetTransform( MATERIAL_VIEW ) * GetTransform( MATERIAL_PROJECTION ); // Get the non-FastClip projection matrix + D3DXMatrixTranspose( &transpose, &transpose ); + + float *pRowTwo = (float *)transpose + 8; + SetVertexShaderConstant( VERTEX_SHADER_VIEWPROJ_THIRD_ROW, pRowTwo, 1 ); + } + //GLMPRINTF(( "<-M- SetVertexShaderViewProj" )); +} + +void CShaderAPIDx8::SetVertexShaderModelViewProjAndModelView( void ) +{ + //GLM_FUNC; + //GLMPRINTF(( ">-M- SetVertexShaderModelViewProjAndModelView" )); + + if (g_pHardwareConfig->Caps().m_SupportsPixelShaders) + { + D3DXMATRIX modelView, transpose; + + if (0) + { + D3DXMatrixMultiply( &modelView, &GetTransform(MATERIAL_MODEL), &GetTransform(MATERIAL_VIEW) ); + D3DXMatrixMultiply( &transpose, &modelView, &GetProjectionMatrix() ); + } + else + { + // show work + D3DXMATRIX matView,matProj,matModel; + + matModel = GetTransform(MATERIAL_MODEL); + matView = GetTransform(MATERIAL_VIEW); + matProj = GetProjectionMatrix(); + + D3DXMatrixMultiply( &modelView, &matModel, &matView ); + D3DXMatrixMultiply( &transpose, &modelView, &matProj ); + + //printmat4x4( "matModel", (float*)&matModel ); + //printmat4x4( "matView", (float*)&matView ); + //printmat4x4( "matProj", (float*)&matProj ); + //printmat4x4( "result (model * view * proj) pre-transpose", (float*)&transpose ); + } + + D3DXMatrixTranspose( &transpose, &transpose ); + + #if 0 // turned off while we try to do fixup-Y in shader translate + if (IsPosix()) // flip all shader projection matrices for Y on GL since you can't have an upside-down viewport specification + { + // flip Y + transpose._21 *= -1.0f; + transpose._22 *= -1.0f; + transpose._23 *= -1.0f; + transpose._24 *= -1.0f; + } + #endif + + SetVertexShaderConstant( VERTEX_SHADER_MODELVIEWPROJ, transpose, 4 ); + + // If we're doing FastClip, the above modelviewproj matrix won't work well for + // vertex shaders which compute projPos.z, hence we'll compute a more useful + // modelviewproj and put the third row of it in another constant + D3DXMatrixMultiply( &transpose, &modelView, &GetTransform( MATERIAL_PROJECTION ) ); // Get the non-FastClip projection matrix + D3DXMatrixTranspose( &transpose, &transpose ); + + float *pRowTwo = (float *)transpose + 8; + SetVertexShaderConstant( VERTEX_SHADER_MODELVIEWPROJ_THIRD_ROW, pRowTwo, 1 ); + } + + //GLMPRINTF(( "<-M- SetVertexShaderModelViewProjAndModelView" )); +} + +void CShaderAPIDx8::UpdateVertexShaderMatrix( int iMatrix ) +{ + //GLM_FUNC; + if ( iMatrix == 0 ) + { + int matrix = MATERIAL_MODEL; + if (VertexShaderTransformChanged(matrix)) + { + int vertexShaderConstant = VERTEX_SHADER_MODEL + iMatrix * 3; + + // Put the transform into the vertex shader constants... + D3DXMATRIX transpose; + D3DXMatrixTranspose( &transpose, &GetTransform(matrix) ); + SetVertexShaderConstant( vertexShaderConstant, transpose, 3 ); + + // clear the change flag + m_DynamicState.m_TransformChanged[matrix] &= ~STATE_CHANGED_VERTEX_SHADER; + } + } + else + { + SetVertexShaderConstant( VERTEX_SHADER_MODEL + iMatrix, m_boneMatrix[iMatrix].Base(), 3 ); + } +} + + +void CShaderAPIDx8::SetVertexShaderStateSkinningMatrices() +{ + //GLM_FUNC; + // casting from 4x3 matrices to a 4x4 D3DXMATRIX, need 4 floats of overflow + float results[12+4]; + + // get the first one from the MATERIAL_MODEL matrix stack + D3DXMatrixTranspose( (D3DXMATRIX *)&results[0], &GetTransform( MATERIAL_MODEL ) ); + memcpy( m_boneMatrix[0].Base(), results, 12 * sizeof(float) ); + + m_maxBoneLoaded++; + int matricesLoaded = max( 1, m_maxBoneLoaded ); + m_maxBoneLoaded = 0; + + m_DynamicState.m_TransformChanged[MATERIAL_MODEL] &= ~STATE_CHANGED_VERTEX_SHADER; + SetVertexShaderConstant( VERTEX_SHADER_MODEL, m_boneMatrix[0].Base(), matricesLoaded * 3, true ); + + // ###OSX### punting on OSX for now +#if defined( DX_TO_GL_ABSTRACTION ) && !defined( OSX ) + Dx9Device()->SetMaxUsedVertexShaderConstantsHint( VERTEX_SHADER_MODEL + ( matricesLoaded * 3 ) ); +#endif + +} + +//----------------------------------------------------------------------------- +// Commits vertex shader transforms that can change on a per pass basis +//----------------------------------------------------------------------------- +void CShaderAPIDx8::CommitPerPassVertexShaderTransforms() +{ + //GLMPRINTF(( ">-M- CommitPerPassVertexShaderTransforms" )); + Assert( g_pHardwareConfig->Caps().m_SupportsPixelShaders ); + + bool projChanged = VertexShaderTransformChanged( MATERIAL_PROJECTION ); + //projChanged = true; //only for debug + if ( projChanged ) + { + //GLMPRINTF(( "-M- projChanged=true in CommitPerPassVertexShaderTransforms" )); + SetVertexShaderViewProj(); + SetVertexShaderModelViewProjAndModelView(); + + // Clear change flags + m_DynamicState.m_TransformChanged[MATERIAL_PROJECTION] &= ~STATE_CHANGED_VERTEX_SHADER; + } + else + { + //GLMPRINTF(( "-M- projChanged=false in CommitPerPassVertexShaderTransforms" )); + } + + //GLMPRINTF(( "<-M- CommitPerPassVertexShaderTransforms" )); +} + +//----------------------------------------------------------------------------- +// Commits vertex shader transforms +//----------------------------------------------------------------------------- +void CShaderAPIDx8::CommitVertexShaderTransforms() +{ + //GLMPRINTF(( ">-M- CommitVertexShaderTransforms" )); + + Assert( g_pHardwareConfig->Caps().m_SupportsPixelShaders ); + + bool viewChanged = VertexShaderTransformChanged(MATERIAL_VIEW); + bool projChanged = VertexShaderTransformChanged(MATERIAL_PROJECTION); + bool modelChanged = VertexShaderTransformChanged(MATERIAL_MODEL) && (m_DynamicState.m_NumBones < 1); + + //GLMPRINTF(( "-M- viewChanged=%s projChanged=%s modelChanged = %s in CommitVertexShaderTransforms", viewChanged?"Y":"N",projChanged?"Y":"N",modelChanged?"Y":"N" )); + if (viewChanged) + { + //GLMPRINTF(( "-M- viewChanged --> UpdateVertexShaderFogParams" )); + UpdateVertexShaderFogParams(); + } + + if( viewChanged || projChanged ) + { + // NOTE: We have to deal with fast-clip *before* + //GLMPRINTF(( "-M- viewChanged||projChanged --> SetVertexShaderViewProj" )); + SetVertexShaderViewProj(); + } + + if( viewChanged || modelChanged || projChanged ) + { + //GLMPRINTF(( "-M- viewChanged||projChanged||modelChanged --> SetVertexShaderModelViewProjAndModelView" )); + SetVertexShaderModelViewProjAndModelView(); + } + + if( modelChanged && m_DynamicState.m_NumBones < 1 ) + { + UpdateVertexShaderMatrix( 0 ); + } + + // Clear change flags + m_DynamicState.m_TransformChanged[MATERIAL_MODEL] &= ~STATE_CHANGED_VERTEX_SHADER; + m_DynamicState.m_TransformChanged[MATERIAL_VIEW] &= ~STATE_CHANGED_VERTEX_SHADER; + m_DynamicState.m_TransformChanged[MATERIAL_PROJECTION] &= ~STATE_CHANGED_VERTEX_SHADER; + + //GLMPRINTF(( "<-M- CommitVertexShaderTransforms" )); +} + + +void CShaderAPIDx8::UpdateFixedFunctionMatrix( int iMatrix ) +{ + if ( IsX360() ) + return; + + int matrix = MATERIAL_MODEL + iMatrix; + if ( FixedFunctionTransformChanged( matrix ) ) + { + SetTransform( D3DTS_WORLDMATRIX(iMatrix), &GetTransform(matrix) ); + + // clear the change flag + m_DynamicState.m_TransformChanged[matrix] &= ~STATE_CHANGED_FIXED_FUNCTION; + } +} + + +void CShaderAPIDx8::SetFixedFunctionStateSkinningMatrices() +{ + if ( IsX360() ) + return; + + for( int i=1; i < g_pHardwareConfig->MaxBlendMatrices(); i++ ) + { + UpdateFixedFunctionMatrix( i ); + } +} + +//----------------------------------------------------------------------------- +// Commits transforms for the fixed function pipeline that can happen on a per pass basis +//----------------------------------------------------------------------------- +void CShaderAPIDx8::CommitPerPassFixedFunctionTransforms() +{ + if ( IsX360() ) + return; + + // Update projection + if ( FixedFunctionTransformChanged( MATERIAL_PROJECTION ) ) + { + D3DTRANSFORMSTATETYPE matrix = D3DTS_PROJECTION; + + SetTransform( matrix, &GetProjectionMatrix() ); + // clear the change flag + m_DynamicState.m_TransformChanged[MATERIAL_PROJECTION] &= ~STATE_CHANGED_FIXED_FUNCTION; + } +} + + +//----------------------------------------------------------------------------- +// Commits transforms for the fixed function pipeline +//----------------------------------------------------------------------------- + +void CShaderAPIDx8::CommitFixedFunctionTransforms() +{ + if ( IsX360() ) + return; + + // Update view + projection + int i; + for ( i = MATERIAL_VIEW; i <= MATERIAL_PROJECTION; ++i) + { + if (FixedFunctionTransformChanged( i )) + { + D3DTRANSFORMSTATETYPE matrix = (i == MATERIAL_VIEW) ? D3DTS_VIEW : D3DTS_PROJECTION; + if ( i == MATERIAL_PROJECTION ) + { + SetTransform( matrix, &GetProjectionMatrix() ); + } + else + { + SetTransform( matrix, &GetTransform(i) ); + } + + // clear the change flag + m_DynamicState.m_TransformChanged[i] &= ~STATE_CHANGED_FIXED_FUNCTION; + } + } + + UpdateFixedFunctionMatrix( 0 ); +} + + +void CShaderAPIDx8::SetSkinningMatrices() +{ + LOCK_SHADERAPI(); + Assert( m_pMaterial ); + + if ( m_DynamicState.m_NumBones == 0 ) + { + // ###OSX### punting on OSX for now +#if defined( DX_TO_GL_ABSTRACTION ) && !defined( OSX) + Dx9Device()->SetMaxUsedVertexShaderConstantsHint( VERTEX_SHADER_BONE_TRANSFORM( 0 ) + 3 ); +#endif + return; + } + + uint nMaxVertexConstantIndex = 0; + + if ( IsX360() || UsesVertexShader(m_pMaterial->GetVertexFormat()) ) + { + SetVertexShaderStateSkinningMatrices(); + } + else if ( IsPC() ) + { +#if defined( DX_TO_GL_ABSTRACTION ) && !defined( OSX) + Assert( 0 ); +#else + SetFixedFunctionStateSkinningMatrices(); +#endif + } + else + { + Assert( 0 ); + } +} + + + +//----------------------------------------------------------------------------- +// Commits vertex shader lighting +//----------------------------------------------------------------------------- + +inline bool CShaderAPIDx8::VertexShaderLightingChanged( int i ) +{ + return (m_DynamicState.m_LightChanged[i] & STATE_CHANGED_VERTEX_SHADER) != 0; +} + +inline bool CShaderAPIDx8::VertexShaderLightingEnableChanged( int i ) +{ + return (m_DynamicState.m_LightEnableChanged[i] & STATE_CHANGED_VERTEX_SHADER) != 0; +} + +inline bool CShaderAPIDx8::FixedFunctionLightingChanged( int i ) +{ + return (m_DynamicState.m_LightChanged[i] & STATE_CHANGED_FIXED_FUNCTION) != 0; +} + +inline bool CShaderAPIDx8::FixedFunctionLightingEnableChanged( int i ) +{ + return (m_DynamicState.m_LightEnableChanged[i] & STATE_CHANGED_FIXED_FUNCTION) != 0; +} + + +//----------------------------------------------------------------------------- +// Computes the light type +//----------------------------------------------------------------------------- + +VertexShaderLightTypes_t CShaderAPIDx8::ComputeLightType( int i ) const +{ + if (!m_DynamicState.m_LightEnable[i]) + return LIGHT_NONE; + + switch( m_DynamicState.m_Lights[i].Type ) + { + case D3DLIGHT_POINT: + return LIGHT_POINT; + + case D3DLIGHT_DIRECTIONAL: + return LIGHT_DIRECTIONAL; + + case D3DLIGHT_SPOT: + return LIGHT_SPOT; + } + + Assert(0); + return LIGHT_NONE; +} + + +//----------------------------------------------------------------------------- +// Sort the lights by type +//----------------------------------------------------------------------------- + +void CShaderAPIDx8::SortLights( int* index ) +{ + m_DynamicState.m_NumLights = 0; + + for (int i = 0; i < MAX_NUM_LIGHTS; ++i) + { + VertexShaderLightTypes_t type = ComputeLightType(i); // returns LIGHT_NONE if the light is disabled + int j = m_DynamicState.m_NumLights; + if (type != LIGHT_NONE) + { + while ( --j >= 0 ) + { + if (m_DynamicState.m_LightType[j] <= type) + break; + + // shift... + m_DynamicState.m_LightType[j+1] = m_DynamicState.m_LightType[j]; + index[j+1] = index[j]; + } + ++j; + + m_DynamicState.m_LightType[j] = type; + index[j] = i; + + ++m_DynamicState.m_NumLights; + } + } +} + +//----------------------------------------------------------------------------- +// Vertex Shader lighting +//----------------------------------------------------------------------------- +void CShaderAPIDx8::CommitVertexShaderLighting() +{ + // If nothing changed, then don't bother. Otherwise, reload... + int i; + for ( i = 0; i < MAX_NUM_LIGHTS; ++i ) + { + if (VertexShaderLightingChanged(i) || VertexShaderLightingEnableChanged(i)) + break; + } + + // Yeah baby + if ( i == MAX_NUM_LIGHTS ) + return; + + // First, gotta sort the lights by their type + int lightIndex[MAX_NUM_LIGHTS]; + memset( lightIndex, 0, sizeof( lightIndex ) ); + SortLights( lightIndex ); + + // Clear the lighting enable flags + for ( i = 0; i < MAX_NUM_LIGHTS; ++i ) + { + m_DynamicState.m_LightEnableChanged[i] &= ~STATE_CHANGED_VERTEX_SHADER; + m_DynamicState.m_LightChanged[i] &= ~STATE_CHANGED_VERTEX_SHADER; + } + + bool bAtLeastDX90 = g_pHardwareConfig->GetDXSupportLevel() >= 90; + + // Set the lighting state + for ( i = 0; i < m_DynamicState.m_NumLights; ++i ) + { + D3DLIGHT& light = m_DynamicState.m_Lights[lightIndex[i]]; + + Vector4D lightState[5]; + + // The first one is the light color (and light type code on DX9) + float w = (light.Type == D3DLIGHT_DIRECTIONAL) && bAtLeastDX90 ? 1.0f : 0.0f; + lightState[0].Init( light.Diffuse.r, light.Diffuse.g, light.Diffuse.b, w); + + // The next constant holds the light direction (and light type code on DX9) + w = (light.Type == D3DLIGHT_SPOT) && bAtLeastDX90 ? 1.0f : 0.0f; + lightState[1].Init( light.Direction.x, light.Direction.y, light.Direction.z, w ); + + // The next constant holds the light position + lightState[2].Init( light.Position.x, light.Position.y, light.Position.z, 1.0f ); + + // The next constant holds exponent, stopdot, stopdot2, 1 / (stopdot - stopdot2) + if (light.Type == D3DLIGHT_SPOT) + { + float stopdot = cos( light.Theta * 0.5f ); + float stopdot2 = cos( light.Phi * 0.5f ); + float oodot = (stopdot > stopdot2) ? 1.0f / (stopdot - stopdot2) : 0.0f; + lightState[3].Init( light.Falloff, stopdot, stopdot2, oodot ); + } + else + { + lightState[3].Init( 0, 1, 1, 1 ); + } + + // The last constant holds attenuation0, attenuation1, attenuation2 + lightState[4].Init( light.Attenuation0, light.Attenuation1, light.Attenuation2, 0.0f ); + + // Set the state + SetVertexShaderConstant( VERTEX_SHADER_LIGHTS + i * 5, lightState[0].Base(), 5 ); + } + + if ( g_pHardwareConfig->NumIntegerVertexShaderConstants() > 0 && g_pHardwareConfig->NumBooleanVertexShaderConstants() > 0 ) + { + // Vertex Shader loop counter for number of lights (Only the .x component is used by our shaders) + // .x is the iteration count, .y is the initial value and .z is the increment step + int nLoopControl[4] = {m_DynamicState.m_NumLights, 0, 1, 0}; + SetIntegerVertexShaderConstant( 0, nLoopControl, 1 ); + + // Enable lights using vertex shader static flow control + int nLightEnable[VERTEX_SHADER_LIGHT_ENABLE_BOOL_CONST_COUNT] = {0, 0, 0, 0}; + for ( i = 0; i < m_DynamicState.m_NumLights; ++i ) + { + nLightEnable[i] = 1; + } + + SetBooleanVertexShaderConstant( VERTEX_SHADER_LIGHT_ENABLE_BOOL_CONST, nLightEnable, VERTEX_SHADER_LIGHT_ENABLE_BOOL_CONST_COUNT ); + } +} + +//----------------------------------------------------------------------------- +// Set the pixel shader constants for lights +//----------------------------------------------------------------------------- +void CShaderAPIDx8::CommitPixelShaderLighting( int pshReg ) +{ +#ifndef NDEBUG + char const *materialName = m_pMaterial->GetName(); +#endif + + // First, gotta sort the lights by their type + int lightIndex[MAX_NUM_LIGHTS]; + SortLights( lightIndex ); + + // Offset to create a point light from directional + const float fFarAway = 10000.0f; + + // Total pixel shader lighting state for four lights + Vector4D lightState[6]; + for ( int i = 0; i < 6; i++ ) + lightState[i].Init(); + + int nNumLights = m_DynamicState.m_NumLights; + if ( nNumLights > 0 ) + { + D3DLIGHT *light = &m_DynamicState.m_Lights[lightIndex[0]]; + lightState[0].Init( light->Diffuse.r, light->Diffuse.g, light->Diffuse.b, 0.0f ); + + if ( light->Type == D3DLIGHT_DIRECTIONAL ) + { + Vector vDir(light->Direction.x, light->Direction.y, light->Direction.z ); + Vector vPos = m_DynamicState.m_vLightingOrigin - vDir * fFarAway; + lightState[1].Init( vPos.x, vPos.y, vPos.z, 0.0f ); + } + else + { + lightState[1].Init( light->Position.x, light->Position.y, light->Position.z, 0.0f ); + } + + if ( nNumLights > 1 ) // At least two lights + { + light = &m_DynamicState.m_Lights[lightIndex[1]]; + lightState[2].Init( light->Diffuse.r, light->Diffuse.g, light->Diffuse.b, 0.0f ); + + if ( light->Type == D3DLIGHT_DIRECTIONAL ) + { + Vector vDir(light->Direction.x, light->Direction.y, light->Direction.z ); + Vector vPos = m_DynamicState.m_vLightingOrigin - vDir * fFarAway; + lightState[3].Init( vPos.x, vPos.y, vPos.z, 0.0f ); + } + else + { + lightState[3].Init( light->Position.x, light->Position.y, light->Position.z, 0.0f ); + } + + if ( nNumLights > 2 ) // At least three lights + { + light = &m_DynamicState.m_Lights[lightIndex[2]]; + lightState[4].Init( light->Diffuse.r, light->Diffuse.g, light->Diffuse.b, 0.0f ); + + if ( light->Type == D3DLIGHT_DIRECTIONAL ) + { + Vector vDir(light->Direction.x, light->Direction.y, light->Direction.z ); + Vector vPos = m_DynamicState.m_vLightingOrigin - vDir * fFarAway; + lightState[5].Init( vPos.x, vPos.y, vPos.z, 0.0f ); + } + else + { + lightState[5].Init( light->Position.x, light->Position.y, light->Position.z, 0.0f ); + } + + if ( nNumLights > 3 ) // At least four lights (our current max) + { + light = &m_DynamicState.m_Lights[lightIndex[3]]; // Spread 4th light's constants across w components + lightState[0].w = light->Diffuse.r; + lightState[1].w = light->Diffuse.g; + lightState[2].w = light->Diffuse.b; + + if ( light->Type == D3DLIGHT_DIRECTIONAL ) + { + Vector vDir(light->Direction.x, light->Direction.y, light->Direction.z ); + Vector vPos = m_DynamicState.m_vLightingOrigin - vDir * fFarAway; + lightState[3].w = vPos.x; + lightState[4].w = vPos.y; + lightState[5].w = vPos.z; + } + else + { + lightState[3].w = light->Position.x; + lightState[4].w = light->Position.y; + lightState[5].w = light->Position.z; + } + } + } + } + } + SetPixelShaderConstant( pshReg, lightState[0].Base(), 6 ); +} + +//----------------------------------------------------------------------------- +// Fixed function lighting +//----------------------------------------------------------------------------- +void CShaderAPIDx8::CommitFixedFunctionLighting() +{ + if ( IsX360() ) + { + return; + } + + // Commit each light + for (int i = 0; i < g_pHardwareConfig->MaxNumLights(); ++i) + { + // Change light enable + if ( FixedFunctionLightingEnableChanged( i ) ) + { + LightEnable( i, m_DynamicState.m_LightEnable[i] ); + + // Clear change flag + m_DynamicState.m_LightEnableChanged[i] &= ~STATE_CHANGED_FIXED_FUNCTION; + } + + // Change lighting state... + if ( m_DynamicState.m_LightEnable[i] ) + { + if ( FixedFunctionLightingChanged( i ) ) + { + // Store off the "correct" falloff... + D3DLIGHT& light = m_DynamicState.m_Lights[i]; + + float falloff = light.Falloff; + + SetLight( i, &light ); + + // Clear change flag + m_DynamicState.m_LightChanged[i] &= ~STATE_CHANGED_FIXED_FUNCTION; + + // restore the correct falloff + light.Falloff = falloff; + } + } + } +} + + +//----------------------------------------------------------------------------- +// Commits user clip planes +//----------------------------------------------------------------------------- +D3DXMATRIX& CShaderAPIDx8::GetUserClipTransform( ) +{ + if ( !m_DynamicState.m_bUserClipTransformOverride ) + return GetTransform(MATERIAL_VIEW); + + return m_DynamicState.m_UserClipTransform; +} + + +//----------------------------------------------------------------------------- +// Commits user clip planes +//----------------------------------------------------------------------------- +void CShaderAPIDx8::CommitUserClipPlanes( bool bUsingFixedFunction ) +{ + // We need to transform the clip planes, specified in world space, + // to be in projection space.. To transform the plane, we must transform + // the intercept and then transform the normal. + + if( bUsingFixedFunction != m_DynamicState.m_UserClipLastUpdatedUsingFixedFunction ) + { + //fixed function clip planes are in world space, vertex shader clip planes are in clip space, so we need to update every clip plane whenever there's a flip + m_DynamicState.m_UserClipPlaneChanged = (1 << g_pHardwareConfig->MaxUserClipPlanes()) - 1; + m_DynamicState.m_UserClipLastUpdatedUsingFixedFunction = bUsingFixedFunction; + } + + D3DXMATRIX worldToProjectionInvTrans; +#ifndef _DEBUG + if( m_DynamicState.m_UserClipPlaneChanged & m_DynamicState.m_UserClipPlaneEnabled & ((1 << g_pHardwareConfig->MaxUserClipPlanes()) - 1) ) +#endif + { + //we're going to need the transformation matrix at least once this call + if( bUsingFixedFunction ) + { + if( m_DynamicState.m_bUserClipTransformOverride ) + { + //D3DXMatrixIdentity( &worldToProjectionInvTrans ); //TODO: Test user clip transforms with this + //Since GetUserClipTransform() returns the view matrix if a user supplied transform doesn't exist, the general solution to this should be to transform the user transform by the inverse view matrix + //Since we don't know if the user clip is invertable, we'll premultiply by inverse view and cross our fingers that it's right more often than wrong + D3DXMATRIX viewInverse = GetTransform( MATERIAL_VIEW ); + D3DXMatrixInverse(&viewInverse, NULL, &viewInverse); + worldToProjectionInvTrans = viewInverse * GetUserClipTransform(); //taking a cue from the multiplication below, multiplication goes left into right + + D3DXMatrixInverse(&worldToProjectionInvTrans, NULL, &worldToProjectionInvTrans); + D3DXMatrixTranspose(&worldToProjectionInvTrans, &worldToProjectionInvTrans); + } + else + { + D3DXMatrixIdentity( &worldToProjectionInvTrans ); + } + } + else + { + worldToProjectionInvTrans = GetUserClipTransform( ) * GetTransform( MATERIAL_PROJECTION ); + D3DXMatrixInverse(&worldToProjectionInvTrans, NULL, &worldToProjectionInvTrans); + D3DXMatrixTranspose(&worldToProjectionInvTrans, &worldToProjectionInvTrans); + } + } + + for (int i = 0; i < g_pHardwareConfig->MaxUserClipPlanes(); ++i) + { + // Don't bother with the plane if it's not enabled + if ( (m_DynamicState.m_UserClipPlaneEnabled & (1 << i)) == 0 ) + continue; + + // Don't bother if it didn't change... + if ( (m_DynamicState.m_UserClipPlaneChanged & (1 << i)) == 0 ) + { +#ifdef _DEBUG + //verify that the plane has not actually changed + D3DXPLANE clipPlaneProj; + D3DXPlaneTransform( &clipPlaneProj, &m_DynamicState.m_UserClipPlaneWorld[i], &worldToProjectionInvTrans ); + Assert ( clipPlaneProj == m_DynamicState.m_UserClipPlaneProj[i] ); +#endif + continue; + } + + m_DynamicState.m_UserClipPlaneChanged &= ~(1 << i); + + D3DXPLANE clipPlaneProj; + D3DXPlaneTransform( &clipPlaneProj, &m_DynamicState.m_UserClipPlaneWorld[i], &worldToProjectionInvTrans ); + + if ( clipPlaneProj != m_DynamicState.m_UserClipPlaneProj[i] ) + { + Dx9Device()->SetClipPlane( i, (float*)clipPlaneProj ); + m_DynamicState.m_UserClipPlaneProj[i] = clipPlaneProj; + } + } +} + + +//----------------------------------------------------------------------------- +// Need to handle fog mode on a per-pass basis +//----------------------------------------------------------------------------- +void CShaderAPIDx8::CommitPerPassFogMode( bool bUsingVertexAndPixelShaders ) +{ + if ( IsX360() ) + { + // FF fog not applicable on 360 + return; + } + + D3DFOGMODE dxFogMode = D3DFOG_NONE; + if ( m_DynamicState.m_FogEnable ) + { + dxFogMode = bUsingVertexAndPixelShaders ? D3DFOG_NONE : D3DFOG_LINEAR; + } + + // Set fog mode if it's different than before. + if( m_DynamicState.m_FogMode != dxFogMode ) + { + SetRenderStateConstMacro( this, D3DRS_FOGVERTEXMODE, dxFogMode ); + m_DynamicState.m_FogMode = dxFogMode; + } +} + +//----------------------------------------------------------------------------- +// Handle Xbox GPU/DX API fixups necessary before actual draw. +//----------------------------------------------------------------------------- +void CShaderAPIDx8::CommitPerPassXboxFixups() +{ +#if defined( _X360 ) + // send updated shader constants to gpu + WriteShaderConstantsToGPU(); + + // sRGB write state may have changed after RT set, have to re-set correct RT + SetRenderTargetInternalXbox( m_hCachedRenderTarget ); +#endif +} + +//----------------------------------------------------------------------------- +// These states can change between each pass +//----------------------------------------------------------------------------- +void CShaderAPIDx8::CommitPerPassStateChanges( StateSnapshot_t id ) +{ + if ( IsX360() || UsesVertexAndPixelShaders(id) ) + { + CommitPerPassVertexShaderTransforms(); + CommitPerPassFogMode( true ); + CommitPerPassXboxFixups(); + CallCommitFuncs( COMMIT_PER_PASS, false ); + } + else if ( IsPC() ) + { + CommitPerPassFixedFunctionTransforms(); + CommitPerPassFogMode( false ); + CallCommitFuncs( COMMIT_PER_PASS, true ); + } + else + { + Assert( 0 ); + } +} + + +//----------------------------------------------------------------------------- +// Commits transforms and lighting +//----------------------------------------------------------------------------- +void CShaderAPIDx8::CommitStateChanges() +{ + VPROF("CShaderAPIDx8::CommitStateChanges"); + CommitFastClipPlane(); + + bool bUsingFixedFunction = !IsX360() && m_pMaterial && !UsesVertexShader( m_pMaterial->GetVertexFormat() ); + + // xboxissue - cannot support ff pipeline + Assert ( IsPC() || ( IsX360() && !bUsingFixedFunction ) ); + + if ( IsX360() || !bUsingFixedFunction ) + { + CommitVertexShaderTransforms(); + + if ( m_pMaterial && m_pMaterial->IsVertexLit() ) + { + CommitVertexShaderLighting(); + } + } + else if ( IsPC() ) + { + CommitFixedFunctionTransforms(); + + if ( m_pMaterial && ( m_pMaterial->IsVertexLit() || m_pMaterial->NeedsFixedFunctionFlashlight() ) ) + { + CommitFixedFunctionLighting(); + } + } + else + { + Assert( 0 ); + } + + if ( m_DynamicState.m_UserClipPlaneEnabled ) + { + CommitUserClipPlanes( bUsingFixedFunction ); + } + + CallCommitFuncs( COMMIT_PER_DRAW, bUsingFixedFunction ); +} + +//----------------------------------------------------------------------------- +// Commits viewports +//----------------------------------------------------------------------------- +static void CommitSetViewports( IDirect3DDevice9 *pDevice, const DynamicState_t &desiredState, DynamicState_t ¤tState, bool bForce ) +{ + bool bChanged = bForce || memcmp( &desiredState.m_Viewport, ¤tState.m_Viewport, sizeof(D3DVIEWPORT9) ); + + // The width + height can be zero at startup sometimes. + if ( bChanged && ( desiredState.m_Viewport.Width != 0 ) && ( desiredState.m_Viewport.Height != 0 ) ) + { + if( ReverseDepthOnX360() ) //reverse depth on 360 for better perf through hierarchical z + { + D3DVIEWPORT9 reverseDepthViewport; + reverseDepthViewport = desiredState.m_Viewport; + reverseDepthViewport.MinZ = 1.0f - desiredState.m_Viewport.MinZ; + reverseDepthViewport.MaxZ = 1.0f - desiredState.m_Viewport.MaxZ; + Dx9Device()->SetViewport( &reverseDepthViewport ); + } + else + { + Dx9Device()->SetViewport( &desiredState.m_Viewport ); + } + memcpy( ¤tState.m_Viewport, &desiredState.m_Viewport, sizeof( D3DVIEWPORT9 ) ); + } +} + + +void CShaderAPIDx8::SetViewports( int nCount, const ShaderViewport_t* pViewports ) +{ + Assert( nCount == 1 && pViewports[0].m_nVersion == SHADER_VIEWPORT_VERSION ); + if ( nCount != 1 ) + return; + + LOCK_SHADERAPI(); + + D3DVIEWPORT9 viewport; + viewport.X = pViewports[0].m_nTopLeftX; + viewport.Y = pViewports[0].m_nTopLeftY; + viewport.Width = pViewports[0].m_nWidth; + viewport.Height = pViewports[0].m_nHeight; + viewport.MinZ = pViewports[0].m_flMinZ; + viewport.MaxZ = pViewports[0].m_flMaxZ; + + // Clamp the viewport to the current render target... + if ( !m_UsingTextureRenderTarget ) + { + // Clamp to both the back buffer and the window, if it is resizing + int nMaxWidth = 0, nMaxHeight = 0; + GetBackBufferDimensions( nMaxWidth, nMaxHeight ); + if ( IsPC() && m_IsResizing ) + { + RECT viewRect; +#if !defined( DX_TO_GL_ABSTRACTION ) + GetClientRect( ( HWND )m_ViewHWnd, &viewRect ); +#else + toglGetClientRect( (VD3DHWND)m_ViewHWnd, &viewRect ); +#endif + m_nWindowWidth = viewRect.right - viewRect.left; + m_nWindowHeight = viewRect.bottom - viewRect.top; + nMaxWidth = min( m_nWindowWidth, nMaxWidth ); + nMaxHeight = min( m_nWindowHeight, nMaxHeight ); + } + + // Dimensions can freak out on app exit, so at least make sure the viewport is positive + if ( (viewport.Width > (unsigned int)nMaxWidth ) && (nMaxWidth > 0) ) + { + viewport.Width = nMaxWidth; + } + + // Dimensions can freak out on app exit, so at least make sure the viewport is positive + if ( ( viewport.Height > (unsigned int)nMaxHeight ) && (nMaxHeight > 0) ) + { + viewport.Height = nMaxHeight; + } + } + else + { + if ( viewport.Width > (unsigned int)m_ViewportMaxWidth ) + { + viewport.Width = m_ViewportMaxWidth; + } + if ( viewport.Height > (unsigned int)m_ViewportMaxHeight ) + { + viewport.Height = m_ViewportMaxHeight; + } + } + + // FIXME: Once we extract buffered primitives out, we can directly fill in desired state + // and avoid the memcmp and copy + if ( memcmp( &m_DesiredState.m_Viewport, &viewport, sizeof(D3DVIEWPORT9) ) ) + { + if ( !IsDeactivated() ) + { + // State changed... need to flush the dynamic buffer + FlushBufferedPrimitives(); + } + + memcpy( &m_DesiredState.m_Viewport, &viewport, sizeof(D3DVIEWPORT9) ); + } + + ADD_COMMIT_FUNC( COMMIT_PER_DRAW, COMMIT_ALWAYS, CommitSetViewports ); +} + + +//----------------------------------------------------------------------------- +// Gets the current viewport size +//----------------------------------------------------------------------------- +int CShaderAPIDx8::GetViewports( ShaderViewport_t* pViewports, int nMax ) const +{ + if ( !pViewports || nMax == 0 ) + return 1; + + LOCK_SHADERAPI(); + + pViewports[0].m_nTopLeftX = m_DesiredState.m_Viewport.X; + pViewports[0].m_nTopLeftY = m_DesiredState.m_Viewport.Y; + pViewports[0].m_nWidth = m_DesiredState.m_Viewport.Width; + pViewports[0].m_nHeight = m_DesiredState.m_Viewport.Height; + pViewports[0].m_flMinZ = m_DesiredState.m_Viewport.MinZ; + pViewports[0].m_flMaxZ = m_DesiredState.m_Viewport.MaxZ; + return 1; +} + + +//----------------------------------------------------------------------------- +// Flushes buffered primitives +//----------------------------------------------------------------------------- +void CShaderAPIDx8::FlushBufferedPrimitives( ) +{ + if ( ShaderUtil() ) + { + if ( !ShaderUtil()->OnFlushBufferedPrimitives() ) + { + return; + } + } + FlushBufferedPrimitivesInternal(); +} + +void CShaderAPIDx8::FlushBufferedPrimitivesInternal( ) +{ + LOCK_SHADERAPI(); + // This shouldn't happen in the inner rendering loop! + Assert( m_pRenderMesh == 0 ); + + // NOTE: We've gotta store off the matrix mode because + // it'll get reset by the default state application caused by the flush + int tempStack = m_CurrStack; + D3DTRANSFORMSTATETYPE tempMatrixMode = m_MatrixMode; + + MeshMgr()->Flush(); + + m_CurrStack = tempStack; + m_MatrixMode = tempMatrixMode; +} + + +//----------------------------------------------------------------------------- +// Flush the hardware +//----------------------------------------------------------------------------- +void CShaderAPIDx8::FlushHardware( ) +{ + LOCK_SHADERAPI(); + FlushBufferedPrimitives(); + + Dx9Device()->EndScene(); + + DiscardVertexBuffers(); + + Dx9Device()->BeginScene(); + + ForceHardwareSync(); +} + + + +//----------------------------------------------------------------------------- +// Deal with device lost (alt-tab) +//----------------------------------------------------------------------------- +void CShaderAPIDx8::HandleDeviceLost() +{ + if ( IsX360() ) + { + return; + } + + LOCK_SHADERAPI(); + + if ( !IsActive() ) + return; + + // need to flush the dynamic buffer + FlushBufferedPrimitives(); + + if ( !IsDeactivated() ) + { + Dx9Device()->EndScene(); + } + + CheckDeviceLost( m_bOtherAppInitializing ); + + if ( !IsDeactivated() ) + { + Dx9Device()->BeginScene(); + } +} + +//----------------------------------------------------------------------------- +// Buffer clear color +//----------------------------------------------------------------------------- +void CShaderAPIDx8::ClearColor3ub( unsigned char r, unsigned char g, unsigned char b ) +{ + LOCK_SHADERAPI(); + float a = 255;//(r * 0.30f + g * 0.59f + b * 0.11f) / MAX_HDR_OVERBRIGHT; + + // GR - need to force alpha to black for HDR + m_DynamicState.m_ClearColor = D3DCOLOR_ARGB((unsigned char)a,r,g,b); +} + +void CShaderAPIDx8::ClearColor4ub( unsigned char r, unsigned char g, unsigned char b, unsigned char a ) +{ + LOCK_SHADERAPI(); + m_DynamicState.m_ClearColor = D3DCOLOR_ARGB(a,r,g,b); +} + +// Converts the clear color to be appropriate for HDR +D3DCOLOR CShaderAPIDx8::GetActualClearColor( D3DCOLOR clearColor ) +{ + bool bConvert = !IsX360() && m_TransitionTable.CurrentState().m_bLinearColorSpaceFrameBufferEnable; + +#if defined( _X360 ) + // The PC disables SRGBWrite when clearing so that the clear color won't get gamma converted + // The 360 cannot disable that state, and thus compensates for the sRGB conversion + // the desired result is the clear color written to the RT as-is + if ( clearColor & D3DCOLOR_ARGB( 0, 255, 255, 255 ) ) + { + IDirect3DSurface *pRTSurface = NULL; + Dx9Device()->GetRenderTarget( 0, &pRTSurface ); + if ( pRTSurface ) + { + D3DSURFACE_DESC desc; + HRESULT hr = pRTSurface->GetDesc( &desc ); + if ( !FAILED( hr ) && IS_D3DFORMAT_SRGB( desc.Format ) ) + { + bConvert = true; + } + pRTSurface->Release(); + } + } +#endif + + if ( bConvert ) + { + // HDRFIXME: need to make sure this works this way. + // HDRFIXME: Is there a helper function that'll do this easier? + // convert clearColor from gamma to linear since our frame buffer is linear. + Vector vecGammaColor; + vecGammaColor.x = ( 1.0f / 255.0f ) * ( ( clearColor >> 16 ) & 0xff ); + vecGammaColor.y = ( 1.0f / 255.0f ) * ( ( clearColor >> 8 ) & 0xff ); + vecGammaColor.z = ( 1.0f / 255.0f ) * ( clearColor & 0xff ); + Vector vecLinearColor; + vecLinearColor.x = GammaToLinear( vecGammaColor.x ); + vecLinearColor.y = GammaToLinear( vecGammaColor.y ); + vecLinearColor.z = GammaToLinear( vecGammaColor.z ); + clearColor &= D3DCOLOR_RGBA( 0, 0, 0, 255 ); + clearColor |= D3DCOLOR_COLORVALUE( vecLinearColor.x, vecLinearColor.y, vecLinearColor.z, 0.0f ); + } + + return clearColor; +} + + +//----------------------------------------------------------------------------- +// Clear buffers while obeying stencil +//----------------------------------------------------------------------------- +void CShaderAPIDx8::ClearBuffersObeyStencil( bool bClearColor, bool bClearDepth ) +{ + //copy the clear color bool into the clear alpha bool + ClearBuffersObeyStencilEx( bClearColor, bClearColor, bClearDepth ); +} + +void CShaderAPIDx8::ClearBuffersObeyStencilEx( bool bClearColor, bool bClearAlpha, bool bClearDepth ) +{ + LOCK_SHADERAPI(); + + if ( !bClearColor && !bClearAlpha && !bClearDepth ) + return; + + FlushBufferedPrimitives(); + + // Before clearing can happen, user clip planes must be disabled + SetRenderState( D3DRS_CLIPPLANEENABLE, 0 ); + + D3DCOLOR clearColor = GetActualClearColor( m_DynamicState.m_ClearColor ); + + unsigned char r, g, b, a; + b = clearColor& 0xFF; + g = ( clearColor >> 8 ) & 0xFF; + r = ( clearColor >> 16 ) & 0xFF; + a = ( clearColor >> 24 ) & 0xFF; + + ShaderUtil()->DrawClearBufferQuad( r, g, b, a, bClearColor, bClearAlpha, bClearDepth ); + + // Reset user clip plane state + FlushBufferedPrimitives(); + SetRenderState( D3DRS_CLIPPLANEENABLE, m_DynamicState.m_UserClipPlaneEnabled ); +} + +//------------------------------------------------------------------------- +//Perform stencil operations to every pixel on the screen +//------------------------------------------------------------------------- +void CShaderAPIDx8::PerformFullScreenStencilOperation( void ) +{ + LOCK_SHADERAPI(); + + FlushBufferedPrimitives(); + + // We'll be drawing a large quad in altered worldspace, user clip planes must be disabled + SetRenderStateConstMacro( this, D3DRS_CLIPPLANEENABLE, 0 ); + + ShaderUtil()->DrawClearBufferQuad( 0, 0, 0, 0, false, false, false ); + + // Reset user clip plane state + FlushBufferedPrimitives(); + SetRenderStateConstMacro( this, D3DRS_CLIPPLANEENABLE, m_DynamicState.m_UserClipPlaneEnabled ); +} + + +//----------------------------------------------------------------------------- +// Buffer clear +//----------------------------------------------------------------------------- +void CShaderAPIDx8::ClearBuffers( bool bClearColor, bool bClearDepth, bool bClearStencil, int renderTargetWidth, int renderTargetHeight ) +{ + LOCK_SHADERAPI(); + if ( ShaderUtil()->GetConfig().m_bSuppressRendering ) + return; + + if ( IsDeactivated() ) + return; + + // State changed... need to flush the dynamic buffer + FlushBufferedPrimitives(); + CallCommitFuncs( COMMIT_PER_DRAW, true ); + + float depth = (ShaderUtil()->GetConfig().bReverseDepth ^ ReverseDepthOnX360()) ? 0.0f : 1.0f; + DWORD mask = 0; + + if ( bClearColor ) + { + mask |= D3DCLEAR_TARGET; + } + + if ( bClearDepth ) + { + mask |= D3DCLEAR_ZBUFFER; + } + + if ( bClearStencil && m_bUsingStencil ) + { + mask |= D3DCLEAR_STENCIL; + } + + // Only clear the current view... right!??! + D3DRECT clear; + clear.x1 = m_DesiredState.m_Viewport.X; + clear.y1 = m_DesiredState.m_Viewport.Y; + clear.x2 = clear.x1 + m_DesiredState.m_Viewport.Width; + clear.y2 = clear.y1 + m_DesiredState.m_Viewport.Height; + + // SRGBWrite is disabled when clearing so that the clear color won't get gamma converted + bool bSRGBWriteEnable = false; + if ( !IsX360() && bClearColor && m_TransitionTable.CurrentShadowState() ) + { + bSRGBWriteEnable = m_TransitionTable.CurrentShadowState()->m_SRGBWriteEnable; + } + +#if !defined( _X360 ) + if ( bSRGBWriteEnable ) + { + // This path used to be !IsPosix(), but this makes no sense and causes the clear color to differ in D3D9 vs. GL. + Dx9Device()->SetRenderState( D3DRS_SRGBWRITEENABLE, 0 ); + } +#endif + + D3DCOLOR clearColor = GetActualClearColor( m_DynamicState.m_ClearColor ); + + if ( mask != 0 ) + { + bool bRenderTargetMatchesViewport = + ( renderTargetWidth == -1 && renderTargetHeight == -1 ) || + ( m_DesiredState.m_Viewport.Width == -1 && m_DesiredState.m_Viewport.Height == -1 ) || + ( renderTargetWidth == ( int )m_DesiredState.m_Viewport.Width && + renderTargetHeight == ( int )m_DesiredState.m_Viewport.Height ); + + if ( bRenderTargetMatchesViewport ) + { + RECORD_COMMAND( DX8_CLEAR, 6 ); + RECORD_INT( 0 ); + RECORD_STRUCT( &clear, sizeof(clear) ); + RECORD_INT( mask ); + RECORD_INT( clearColor ); + RECORD_FLOAT( depth ); + RECORD_INT( 0 ); + + Dx9Device()->Clear( 0, NULL, mask, clearColor, depth, 0L ); + } + else + { + RECORD_COMMAND( DX8_CLEAR, 6 ); + RECORD_INT( 0 ); + RECORD_STRUCT( &clear, sizeof(clear) ); + RECORD_INT( mask ); + RECORD_INT( clearColor ); + RECORD_FLOAT( depth ); + RECORD_INT( 0 ); + + Dx9Device()->Clear( 1, &clear, mask, clearColor, depth, 0L ); + } + } + + // Restore state + if ( bSRGBWriteEnable ) + { + // sRGBWriteEnable shouldn't be true if we have no shadow state. . . Assert just in case. + Assert( m_TransitionTable.CurrentShadowState() ); + m_TransitionTable.ApplySRGBWriteEnable( *m_TransitionTable.CurrentShadowState() ); + } +} + + +//----------------------------------------------------------------------------- +// Bind +//----------------------------------------------------------------------------- +void CShaderAPIDx8::BindVertexShader( VertexShaderHandle_t hVertexShader ) +{ + ShaderManager()->BindVertexShader( hVertexShader ); +} + +void CShaderAPIDx8::BindGeometryShader( GeometryShaderHandle_t hGeometryShader ) +{ + Assert( hGeometryShader == GEOMETRY_SHADER_HANDLE_INVALID ); +} + +void CShaderAPIDx8::BindPixelShader( PixelShaderHandle_t hPixelShader ) +{ + ShaderManager()->BindPixelShader( hPixelShader ); +} + + +//----------------------------------------------------------------------------- +// Returns a copy of the front buffer +//----------------------------------------------------------------------------- +IDirect3DSurface* CShaderAPIDx8::GetFrontBufferImage( ImageFormat& format ) +{ +#if !defined( _X360 ) + // need to flush the dynamic buffer and make sure the entire image is there + FlushBufferedPrimitives(); + + int w, h; + GetBackBufferDimensions( w, h ); + + HRESULT hr; + IDirect3DSurface *pFullScreenSurfaceBits = 0; + hr = Dx9Device()->CreateOffscreenPlainSurface( w, h, + D3DFMT_A8R8G8B8, D3DPOOL_SCRATCH, &pFullScreenSurfaceBits, NULL ); + if (FAILED(hr)) + return 0; + + hr = Dx9Device()->GetFrontBufferData( 0, pFullScreenSurfaceBits ); + if (FAILED(hr)) + return 0; + + int windowWidth, windowHeight; + GetWindowSize( windowWidth, windowHeight ); + + IDirect3DSurface *pSurfaceBits = 0; + hr = Dx9Device()->CreateOffscreenPlainSurface( windowWidth, windowHeight, + D3DFMT_A8R8G8B8, D3DPOOL_SCRATCH, &pSurfaceBits, NULL ); + Assert( hr == D3D_OK ); + + POINT pnt; + pnt.x = pnt.y = 0; +#ifdef _WIN32 + BOOL result = ClientToScreen( ( HWND )m_hWnd, &pnt ); +#else + BOOL result = ClientToScreen( (VD3DHWND)m_hWnd, &pnt ); +#endif + Assert( result ); + + RECT srcRect; + srcRect.left = pnt.x; + srcRect.top = pnt.y; + srcRect.right = pnt.x + windowWidth; + srcRect.bottom = pnt.y + windowHeight; + + POINT dstPnt; + dstPnt.x = dstPnt.y = 0; + + D3DLOCKED_RECT lockedSrcRect; + hr = pFullScreenSurfaceBits->LockRect( &lockedSrcRect, &srcRect, D3DLOCK_READONLY ); + Assert( hr == D3D_OK ); + + D3DLOCKED_RECT lockedDstRect; + hr = pSurfaceBits->LockRect( &lockedDstRect, NULL, 0 ); + Assert( hr == D3D_OK ); + + int i; + for( i = 0; i < windowHeight; i++ ) + { + memcpy( ( unsigned char * )lockedDstRect.pBits + ( i * lockedDstRect.Pitch ), + ( unsigned char * )lockedSrcRect.pBits + ( i * lockedSrcRect.Pitch ), + windowWidth * 4 ); // hack . . what if this is a different format? + } + hr = pSurfaceBits->UnlockRect(); + Assert( hr == D3D_OK ); + hr = pFullScreenSurfaceBits->UnlockRect(); + Assert( hr == D3D_OK ); + + pFullScreenSurfaceBits->Release(); + + format = ImageLoader::D3DFormatToImageFormat( D3DFMT_A8R8G8B8 ); + return pSurfaceBits; +#else + Assert( 0 ); + return NULL; +#endif +} + + +//----------------------------------------------------------------------------- +// Lets the shader know about the full-screen texture so it can +//----------------------------------------------------------------------------- +void CShaderAPIDx8::SetFullScreenTextureHandle( ShaderAPITextureHandle_t h ) +{ + LOCK_SHADERAPI(); + m_hFullScreenTexture = h; +} + + +//----------------------------------------------------------------------------- +// Lets the shader know about the full-screen texture so it can +//----------------------------------------------------------------------------- +void CShaderAPIDx8::SetLinearToGammaConversionTextures( ShaderAPITextureHandle_t hSRGBWriteEnabledTexture, ShaderAPITextureHandle_t hIdentityTexture ) +{ + LOCK_SHADERAPI(); + + m_hLinearToGammaTableTexture = hSRGBWriteEnabledTexture; + m_hLinearToGammaTableIdentityTexture = hIdentityTexture; +} + +//----------------------------------------------------------------------------- +// Returns a copy of the back buffer +//----------------------------------------------------------------------------- +IDirect3DSurface* CShaderAPIDx8::GetBackBufferImageHDR( Rect_t *pSrcRect, Rect_t *pDstRect, ImageFormat& format ) +{ +#if !defined( _X360 ) + HRESULT hr; + IDirect3DSurface *pSurfaceBits = 0; + IDirect3DSurface *pTmpSurface = NULL; + + // Get the back buffer + IDirect3DSurface* pBackBuffer; + hr = Dx9Device()->GetRenderTarget( 0, &pBackBuffer ); + if (FAILED(hr)) + return 0; + + // Find about its size and format + D3DSURFACE_DESC desc; + D3DTEXTUREFILTERTYPE filter; + + hr = pBackBuffer->GetDesc( &desc ); + if (FAILED(hr)) + goto CleanUp; + + filter = ((pDstRect->width != pSrcRect->width) || (pDstRect->height != pSrcRect->height)) ? D3DTEXF_LINEAR : D3DTEXF_NONE; + + if ( ( pDstRect->x + pDstRect->width <= SMALL_BACK_BUFFER_SURFACE_WIDTH ) && + ( pDstRect->y + pDstRect->height <= SMALL_BACK_BUFFER_SURFACE_HEIGHT ) ) + { + if (!m_pSmallBackBufferFP16TempSurface) + { + hr = Dx9Device()->CreateRenderTarget( + SMALL_BACK_BUFFER_SURFACE_WIDTH, SMALL_BACK_BUFFER_SURFACE_HEIGHT, + desc.Format, D3DMULTISAMPLE_NONE, 0, TRUE, &m_pSmallBackBufferFP16TempSurface, + NULL ); + } + pTmpSurface = m_pSmallBackBufferFP16TempSurface; +#if POSIX + pTmpSurface->AddRef( 0, "CShaderAPIDx8::GetBackBufferImageHDR public addref"); +#else + pTmpSurface->AddRef(); +#endif + + desc.Width = SMALL_BACK_BUFFER_SURFACE_WIDTH; + desc.Height = SMALL_BACK_BUFFER_SURFACE_HEIGHT; + + RECT srcRect, destRect; + RectToRECT( pSrcRect, srcRect ); + RectToRECT( pDstRect, destRect ); + hr = Dx9Device()->StretchRect( pBackBuffer, &srcRect, pTmpSurface, &destRect, filter ); + if ( FAILED(hr) ) + goto CleanUp; + } + else + { + // Normally we would only have to create a separate render target here and StretchBlt to it first + // if AA was enabled, but certain machines/drivers get reboots if we do GetRenderTargetData + // straight off the backbuffer. + hr = Dx9Device()->CreateRenderTarget( desc.Width, desc.Height, desc.Format, + D3DMULTISAMPLE_NONE, 0, TRUE, &pTmpSurface, NULL ); + if ( FAILED(hr) ) + goto CleanUp; + + hr = Dx9Device()->StretchRect( pBackBuffer, NULL, pTmpSurface, NULL, filter ); + if ( FAILED(hr) ) + goto CleanUp; + } + + // Create a buffer the same size and format + hr = Dx9Device()->CreateOffscreenPlainSurface( desc.Width, desc.Height, + desc.Format, D3DPOOL_SYSTEMMEM, &pSurfaceBits, NULL ); + if (FAILED(hr)) + goto CleanUp; + + // Blit from the back buffer to our scratch buffer + hr = Dx9Device()->GetRenderTargetData( pTmpSurface ? pTmpSurface : pBackBuffer, pSurfaceBits ); + if (FAILED(hr)) + goto CleanUp2; + + format = ImageLoader::D3DFormatToImageFormat(desc.Format); + if ( pTmpSurface ) + { + pTmpSurface->Release(); + } + pBackBuffer->Release(); + return pSurfaceBits; + +CleanUp2: + pSurfaceBits->Release(); + +CleanUp: + if ( pTmpSurface ) + { + pTmpSurface->Release(); + } + + pBackBuffer->Release(); +#else + Assert( 0 ); +#endif + return 0; +} + + +//----------------------------------------------------------------------------- +// Returns a copy of the back buffer +//----------------------------------------------------------------------------- +IDirect3DSurface* CShaderAPIDx8::GetBackBufferImage( Rect_t *pSrcRect, Rect_t *pDstRect, ImageFormat& format ) +{ +#if !defined( _X360 ) + if ( !m_pBackBufferSurface || ( m_hFullScreenTexture == INVALID_SHADERAPI_TEXTURE_HANDLE ) ) + return NULL; + + HRESULT hr; + D3DSURFACE_DESC desc; + + FlushBufferedPrimitives(); + + // Get the current render target + IDirect3DSurface* pRenderTarget; + hr = Dx9Device()->GetRenderTarget( 0, &pRenderTarget ); + if (FAILED(hr)) + return 0; + + // Find about its size and format + hr = pRenderTarget->GetDesc( &desc ); + + if ( desc.Format == D3DFMT_A16B16G16R16F || desc.Format == D3DFMT_A32B32G32R32F ) + return GetBackBufferImageHDR( pSrcRect, pDstRect, format ); + + IDirect3DSurface *pSurfaceBits = NULL; + IDirect3DSurface *pTmpSurface = NULL; + int nRenderTargetRefCount; + nRenderTargetRefCount = 0; + + if ( (desc.MultiSampleType == D3DMULTISAMPLE_NONE) && (pRenderTarget != m_pBackBufferSurface) && + (pSrcRect->width == pDstRect->width) && (pSrcRect->height == pDstRect->height) ) + { + // Don't bother to blit through the full-screen texture if we don't + // have to stretch, we're not coming from the backbuffer, and we don't have to do AA resolve + pTmpSurface = pRenderTarget; +#if POSIX + pTmpSurface->AddRef( 0, "CShaderAPIDx8::GetBackBufferImage public addref"); +#else + pTmpSurface->AddRef(); +#endif + } + else + { + Texture_t *pTex = &GetTexture( m_hFullScreenTexture ); + IDirect3DTexture* pFullScreenTexture = (IDirect3DTexture*)pTex->GetTexture(); + + D3DTEXTUREFILTERTYPE filter = ((pDstRect->width != pSrcRect->width) || (pDstRect->height != pSrcRect->height)) ? D3DTEXF_LINEAR : D3DTEXF_NONE; + + hr = pFullScreenTexture->GetSurfaceLevel( 0, &pTmpSurface ); + if ( FAILED(hr) ) + goto CleanUp; + + if ( pTmpSurface == pRenderTarget ) + { + Warning( "Can't blit from full-sized offscreen buffer!\n" ); + goto CleanUp; + } + + RECT srcRect, destRect; + srcRect.left = pSrcRect->x; srcRect.right = pSrcRect->x + pSrcRect->width; + srcRect.top = pSrcRect->y; srcRect.bottom = pSrcRect->y + pSrcRect->height; + srcRect.left = clamp( srcRect.left, 0, (int)desc.Width ); + srcRect.right = clamp( srcRect.right, 0, (int)desc.Width ); + srcRect.top = clamp( srcRect.top, 0, (int)desc.Height ); + srcRect.bottom = clamp( srcRect.bottom, 0, (int)desc.Height ); + + destRect.left = pDstRect->x ; destRect.right = pDstRect->x + pDstRect->width; + destRect.top = pDstRect->y; destRect.bottom = pDstRect->y + pDstRect->height; + destRect.left = clamp( destRect.left, 0, (int)desc.Width ); + destRect.right = clamp( destRect.right, 0, (int)desc.Width ); + destRect.top = clamp( destRect.top, 0, (int)desc.Height ); + destRect.bottom = clamp( destRect.bottom, 0, (int)desc.Height ); + + hr = Dx9Device()->StretchRect( pRenderTarget, &srcRect, pTmpSurface, &destRect, filter ); + if ( FAILED(hr) ) + { + AssertOnce( "Error resizing pixels!\n" ); + goto CleanUp; + } + } + + D3DSURFACE_DESC tmpDesc; + hr = pTmpSurface->GetDesc( &tmpDesc ); + Assert( !FAILED(hr) ); + + // Create a buffer the same size and format + hr = Dx9Device()->CreateOffscreenPlainSurface( tmpDesc.Width, tmpDesc.Height, + desc.Format, D3DPOOL_SYSTEMMEM, &pSurfaceBits, NULL ); + if ( FAILED(hr) ) + { + AssertOnce( "Error creating offscreen surface!\n" ); + goto CleanUp; + } + + // Blit from the back buffer to our scratch buffer + hr = Dx9Device()->GetRenderTargetData( pTmpSurface, pSurfaceBits ); + if ( FAILED(hr) ) + { + AssertOnce( "Error copying bits into the offscreen surface!\n" ); + goto CleanUp; + } + + format = ImageLoader::D3DFormatToImageFormat( desc.Format ); + + pTmpSurface->Release(); +#ifdef _DEBUG + nRenderTargetRefCount = +#endif + pRenderTarget->Release(); + AssertOnce( nRenderTargetRefCount == 1 ); + return pSurfaceBits; + +CleanUp: + if ( pSurfaceBits ) + { + pSurfaceBits->Release(); + } + + if ( pTmpSurface ) + { + pTmpSurface->Release(); + } +#else + Assert( 0 ); +#endif + + return 0; +} + + +//----------------------------------------------------------------------------- +// Copy bits from a host-memory surface +//----------------------------------------------------------------------------- +void CShaderAPIDx8::CopyBitsFromHostSurface( IDirect3DSurface* pSurfaceBits, + const Rect_t &dstRect, unsigned char *pData, ImageFormat srcFormat, ImageFormat dstFormat, int nDstStride ) +{ + // Copy out the bits... + RECT rect; + rect.left = dstRect.x; + rect.right = dstRect.x + dstRect.width; + rect.top = dstRect.y; + rect.bottom = dstRect.y + dstRect.height; + + D3DLOCKED_RECT lockedRect; + HRESULT hr; + int flags = D3DLOCK_READONLY | D3DLOCK_NOSYSLOCK; + + tmZone( TELEMETRY_LEVEL1, TMZF_NONE, "%s", __FUNCTION__ ); + + hr = pSurfaceBits->LockRect( &lockedRect, &rect, flags ); + if ( !FAILED( hr ) ) + { + unsigned char *pImage = (unsigned char *)lockedRect.pBits; + ShaderUtil()->ConvertImageFormat( (unsigned char *)pImage, srcFormat, + pData, dstFormat, dstRect.width, dstRect.height, lockedRect.Pitch, nDstStride ); + + hr = pSurfaceBits->UnlockRect( ); + } +} + + +//----------------------------------------------------------------------------- +// Reads from the current read buffer + stretches +//----------------------------------------------------------------------------- +void CShaderAPIDx8::ReadPixels( Rect_t *pSrcRect, Rect_t *pDstRect, unsigned char *pData, ImageFormat dstFormat, int nDstStride ) +{ + LOCK_SHADERAPI(); + Assert( pDstRect ); + + if ( IsPC() || !IsX360() ) + { + Rect_t srcRect; + if ( !pSrcRect ) + { + srcRect.x = srcRect.y = 0; + srcRect.width = m_nWindowWidth; + srcRect.height = m_nWindowHeight; + pSrcRect = &srcRect; + } + + ImageFormat format; + IDirect3DSurface* pSurfaceBits = GetBackBufferImage( pSrcRect, pDstRect, format ); + if ( pSurfaceBits ) + { + CopyBitsFromHostSurface( pSurfaceBits, *pDstRect, pData, format, dstFormat, nDstStride ); + + // Release the temporary surface + pSurfaceBits->Release(); + } + } + else + { +#if defined( _X360 ) + // 360 requires material system to handle due to RT complexities + ShaderUtil()->ReadBackBuffer( pSrcRect, pDstRect, pData, dstFormat, nDstStride ); +#endif + } +} + + +//----------------------------------------------------------------------------- +// Reads from the current read buffer +//----------------------------------------------------------------------------- +void CShaderAPIDx8::ReadPixels( int x, int y, int width, int height, unsigned char *pData, ImageFormat dstFormat ) +{ + Rect_t rect; + rect.x = x; + rect.y = y; + rect.width = width; + rect.height = height; + + if ( IsPC() || !IsX360() ) + { + ImageFormat format; + IDirect3DSurface* pSurfaceBits = GetBackBufferImage( &rect, &rect, format ); + if (pSurfaceBits) + { + CopyBitsFromHostSurface( pSurfaceBits, rect, pData, format, dstFormat, 0 ); + + // Release the temporary surface + pSurfaceBits->Release(); + } + } + else + { +#if defined( _X360 ) + // 360 requires material system to handle due to RT complexities + ShaderUtil()->ReadBackBuffer( &rect, &rect, pData, dstFormat, 0 ); +#endif + } +} + + +//----------------------------------------------------------------------------- +// Binds a particular material to render with +//----------------------------------------------------------------------------- +void CShaderAPIDx8::Bind( IMaterial* pMaterial ) +{ + LOCK_SHADERAPI(); + IMaterialInternal* pMatInt = static_cast( pMaterial ); + + bool bMaterialChanged; + if ( m_pMaterial && pMatInt && m_pMaterial->InMaterialPage() && pMatInt->InMaterialPage() ) + { + bMaterialChanged = ( m_pMaterial->GetMaterialPage() != pMatInt->GetMaterialPage() ); + } + else + { + bMaterialChanged = ( m_pMaterial != pMatInt ) || ( m_pMaterial && m_pMaterial->InMaterialPage() ) || ( pMatInt && pMatInt->InMaterialPage() ); + } + + if ( bMaterialChanged ) + { + FlushBufferedPrimitives(); +#ifdef RECORDING + RECORD_DEBUG_STRING( ( char * )pMaterial->GetName() ); + IShader *pShader = pMatInt->GetShader(); + if( pShader && pShader->GetName() ) + { + RECORD_DEBUG_STRING( pShader->GetName() ); + } + else + { + RECORD_DEBUG_STRING( "" ); + } +#endif + m_pMaterial = pMatInt; + +#if ( defined( PIX_INSTRUMENTATION ) || defined( NVPERFHUD ) ) + PIXifyName( s_pPIXMaterialName, sizeof( s_pPIXMaterialName ), m_pMaterial->GetName() ); +#endif + } +} + +// Get the currently bound material +IMaterialInternal* CShaderAPIDx8::GetBoundMaterial() +{ + return m_pMaterial; +} + + +//----------------------------------------------------------------------------- +// Binds a standard texture +//----------------------------------------------------------------------------- +void CShaderAPIDx8::BindStandardTexture( Sampler_t sampler, StandardTextureId_t id ) +{ + if ( m_StdTextureHandles[id] != INVALID_SHADERAPI_TEXTURE_HANDLE ) + { + BindTexture( sampler, m_StdTextureHandles[id] ); + } + else + { + ShaderUtil()->BindStandardTexture( sampler, id ); + } +} + +void CShaderAPIDx8::BindStandardVertexTexture( VertexTextureSampler_t sampler, StandardTextureId_t id ) +{ + ShaderUtil()->BindStandardVertexTexture( sampler, id ); +} + +void CShaderAPIDx8::GetStandardTextureDimensions( int *pWidth, int *pHeight, StandardTextureId_t id ) +{ + ShaderUtil()->GetStandardTextureDimensions( pWidth, pHeight, id ); +} + + +//----------------------------------------------------------------------------- +// Gets the lightmap dimensions +//----------------------------------------------------------------------------- +void CShaderAPIDx8::GetLightmapDimensions( int *w, int *h ) +{ + ShaderUtil()->GetLightmapDimensions( w, h ); +} + + +//----------------------------------------------------------------------------- +// Selection mode methods +//----------------------------------------------------------------------------- +int CShaderAPIDx8::SelectionMode( bool selectionMode ) +{ + LOCK_SHADERAPI(); + int numHits = m_NumHits; + if (m_InSelectionMode) + { + WriteHitRecord(); + } + m_InSelectionMode = selectionMode; + m_pCurrSelectionRecord = m_pSelectionBuffer; + m_NumHits = 0; + return numHits; +} + +bool CShaderAPIDx8::IsInSelectionMode() const +{ + return m_InSelectionMode; +} + +void CShaderAPIDx8::SelectionBuffer( unsigned int* pBuffer, int size ) +{ + LOCK_SHADERAPI(); + Assert( !m_InSelectionMode ); + Assert( pBuffer && size ); + m_pSelectionBufferEnd = pBuffer + size; + m_pSelectionBuffer = pBuffer; + m_pCurrSelectionRecord = pBuffer; +} + +void CShaderAPIDx8::ClearSelectionNames( ) +{ + LOCK_SHADERAPI(); + if (m_InSelectionMode) + { + WriteHitRecord(); + } + m_SelectionNames.Clear(); +} + +void CShaderAPIDx8::LoadSelectionName( int name ) +{ + LOCK_SHADERAPI(); + if (m_InSelectionMode) + { + WriteHitRecord(); + Assert( m_SelectionNames.Count() > 0 ); + m_SelectionNames.Top() = name; + } +} + +void CShaderAPIDx8::PushSelectionName( int name ) +{ + LOCK_SHADERAPI(); + if (m_InSelectionMode) + { + WriteHitRecord(); + m_SelectionNames.Push(name); + } +} + +void CShaderAPIDx8::PopSelectionName() +{ + LOCK_SHADERAPI(); + if (m_InSelectionMode) + { + WriteHitRecord(); + m_SelectionNames.Pop(); + } +} + +void CShaderAPIDx8::WriteHitRecord( ) +{ + FlushBufferedPrimitives(); + + if (m_SelectionNames.Count() && (m_SelectionMinZ != FLT_MAX)) + { + Assert( m_pCurrSelectionRecord + m_SelectionNames.Count() + 3 < m_pSelectionBufferEnd ); + *m_pCurrSelectionRecord++ = m_SelectionNames.Count(); + // NOTE: because of rounding, "(uint32)(float)UINT32_MAX" yields zero(!), hence the use of doubles. + // [ ALSO: As of Nov 2011, VS2010 exhibits a debug build code-gen bug if we cast the result to int32 instead of uint32 ] + *m_pCurrSelectionRecord++ = (uint32)( 0.5 + m_SelectionMinZ*(double)((uint32)~0) ); + *m_pCurrSelectionRecord++ = (uint32)( 0.5 + m_SelectionMaxZ*(double)((uint32)~0) ); + for (int i = 0; i < m_SelectionNames.Count(); ++i) + { + *m_pCurrSelectionRecord++ = m_SelectionNames[i]; + } + + ++m_NumHits; + } + + m_SelectionMinZ = FLT_MAX; + m_SelectionMaxZ = FLT_MIN; +} + +// We hit somefin in selection mode +void CShaderAPIDx8::RegisterSelectionHit( float minz, float maxz ) +{ + if (minz < 0) + minz = 0; + if (maxz > 1) + maxz = 1; + if (m_SelectionMinZ > minz) + m_SelectionMinZ = minz; + if (m_SelectionMaxZ < maxz) + m_SelectionMaxZ = maxz; +} + +int CShaderAPIDx8::GetCurrentNumBones( void ) const +{ + return m_DynamicState.m_NumBones; +} + +bool CShaderAPIDx8::IsHWMorphingEnabled( ) const +{ + return m_DynamicState.m_bHWMorphingEnabled; +} + + +//----------------------------------------------------------------------------- +// Inserts the lighting block into the code +//----------------------------------------------------------------------------- + +// If you change the number of lighting combinations, change this enum +enum +{ + DX8_LIGHTING_COMBINATION_COUNT = 22, + DX9_LIGHTING_COMBINATION_COUNT = 35 +}; + +#define MAX_LIGHTS 4 + + +// NOTE: These should match g_lightType* in vsh_prep.pl! +static int g_DX8LightCombinations[][4] = +{ + // static ambient local1 local2 + + // This is a special case for no lighting at all. + { LIGHT_NONE, LIGHT_NONE, LIGHT_NONE, LIGHT_NONE }, + + // This is a special case so that we don't have to do the ambient cube + // when we only have static lighting + { LIGHT_STATIC, LIGHT_NONE, LIGHT_NONE, LIGHT_NONE }, + + { LIGHT_NONE, LIGHT_AMBIENTCUBE, LIGHT_NONE, LIGHT_NONE }, + { LIGHT_NONE, LIGHT_AMBIENTCUBE, LIGHT_SPOT, LIGHT_NONE }, + { LIGHT_NONE, LIGHT_AMBIENTCUBE, LIGHT_POINT, LIGHT_NONE }, + { LIGHT_NONE, LIGHT_AMBIENTCUBE, LIGHT_DIRECTIONAL, LIGHT_NONE }, + { LIGHT_NONE, LIGHT_AMBIENTCUBE, LIGHT_SPOT, LIGHT_SPOT }, + { LIGHT_NONE, LIGHT_AMBIENTCUBE, LIGHT_SPOT, LIGHT_POINT, }, + { LIGHT_NONE, LIGHT_AMBIENTCUBE, LIGHT_SPOT, LIGHT_DIRECTIONAL, }, + { LIGHT_NONE, LIGHT_AMBIENTCUBE, LIGHT_POINT, LIGHT_POINT, }, + { LIGHT_NONE, LIGHT_AMBIENTCUBE, LIGHT_POINT, LIGHT_DIRECTIONAL, }, + { LIGHT_NONE, LIGHT_AMBIENTCUBE, LIGHT_DIRECTIONAL, LIGHT_DIRECTIONAL, }, + + { LIGHT_STATIC, LIGHT_AMBIENTCUBE, LIGHT_NONE, LIGHT_NONE }, + { LIGHT_STATIC, LIGHT_AMBIENTCUBE, LIGHT_SPOT, LIGHT_NONE }, + { LIGHT_STATIC, LIGHT_AMBIENTCUBE, LIGHT_POINT, LIGHT_NONE }, + { LIGHT_STATIC, LIGHT_AMBIENTCUBE, LIGHT_DIRECTIONAL, LIGHT_NONE }, + { LIGHT_STATIC, LIGHT_AMBIENTCUBE, LIGHT_SPOT, LIGHT_SPOT }, + { LIGHT_STATIC, LIGHT_AMBIENTCUBE, LIGHT_SPOT, LIGHT_POINT, }, + { LIGHT_STATIC, LIGHT_AMBIENTCUBE, LIGHT_SPOT, LIGHT_DIRECTIONAL, }, + { LIGHT_STATIC, LIGHT_AMBIENTCUBE, LIGHT_POINT, LIGHT_POINT, }, + { LIGHT_STATIC, LIGHT_AMBIENTCUBE, LIGHT_POINT, LIGHT_DIRECTIONAL, }, + { LIGHT_STATIC, LIGHT_AMBIENTCUBE, LIGHT_DIRECTIONAL, LIGHT_DIRECTIONAL, } +}; + +// NOTE: These should match g_lightType* in vsh_prep.pl! +// They also correspond to the parallel g_LocalLightTypeXArray[] arrays in common_vs_fxc.h +static int g_DX9LightCombinations[][MAX_LIGHTS] = +{ + // local0 local1 local2 local3 + { LIGHT_NONE, LIGHT_NONE, LIGHT_NONE, LIGHT_NONE }, // Zero lights [ Combo 0] + + { LIGHT_SPOT, LIGHT_NONE, LIGHT_NONE, LIGHT_NONE }, // One light [ Combo 1] + { LIGHT_POINT, LIGHT_NONE, LIGHT_NONE, LIGHT_NONE }, + { LIGHT_DIRECTIONAL, LIGHT_NONE, LIGHT_NONE, LIGHT_NONE }, + + { LIGHT_SPOT, LIGHT_SPOT, LIGHT_NONE, LIGHT_NONE }, // Two lights [ Combo 4] + { LIGHT_SPOT, LIGHT_POINT, LIGHT_NONE, LIGHT_NONE }, + { LIGHT_SPOT, LIGHT_DIRECTIONAL, LIGHT_NONE, LIGHT_NONE }, + { LIGHT_POINT, LIGHT_POINT, LIGHT_NONE, LIGHT_NONE }, + { LIGHT_POINT, LIGHT_DIRECTIONAL, LIGHT_NONE, LIGHT_NONE }, + { LIGHT_DIRECTIONAL, LIGHT_DIRECTIONAL, LIGHT_NONE, LIGHT_NONE }, + + { LIGHT_SPOT, LIGHT_SPOT, LIGHT_SPOT, LIGHT_NONE }, // Three lights [ Combo 10] + { LIGHT_SPOT, LIGHT_SPOT, LIGHT_POINT, LIGHT_NONE }, + { LIGHT_SPOT, LIGHT_SPOT, LIGHT_DIRECTIONAL, LIGHT_NONE }, + { LIGHT_SPOT, LIGHT_POINT, LIGHT_POINT, LIGHT_NONE }, + { LIGHT_SPOT, LIGHT_POINT, LIGHT_DIRECTIONAL, LIGHT_NONE }, + { LIGHT_SPOT, LIGHT_DIRECTIONAL, LIGHT_DIRECTIONAL, LIGHT_NONE }, + { LIGHT_POINT, LIGHT_POINT, LIGHT_POINT, LIGHT_NONE }, + { LIGHT_POINT, LIGHT_POINT, LIGHT_DIRECTIONAL, LIGHT_NONE }, + { LIGHT_POINT, LIGHT_DIRECTIONAL, LIGHT_DIRECTIONAL, LIGHT_NONE }, + { LIGHT_DIRECTIONAL, LIGHT_DIRECTIONAL, LIGHT_DIRECTIONAL, LIGHT_NONE }, + + { LIGHT_SPOT, LIGHT_SPOT, LIGHT_SPOT, LIGHT_SPOT }, // Four lights [ Combo 20] + { LIGHT_SPOT, LIGHT_SPOT, LIGHT_SPOT, LIGHT_POINT }, + { LIGHT_SPOT, LIGHT_SPOT, LIGHT_SPOT, LIGHT_DIRECTIONAL }, + { LIGHT_SPOT, LIGHT_SPOT, LIGHT_POINT, LIGHT_POINT }, + { LIGHT_SPOT, LIGHT_SPOT, LIGHT_POINT, LIGHT_DIRECTIONAL }, + { LIGHT_SPOT, LIGHT_SPOT, LIGHT_DIRECTIONAL, LIGHT_DIRECTIONAL }, + { LIGHT_SPOT, LIGHT_POINT, LIGHT_POINT, LIGHT_POINT }, + { LIGHT_SPOT, LIGHT_POINT, LIGHT_POINT, LIGHT_DIRECTIONAL }, + { LIGHT_SPOT, LIGHT_POINT, LIGHT_DIRECTIONAL, LIGHT_DIRECTIONAL }, + { LIGHT_SPOT, LIGHT_DIRECTIONAL, LIGHT_DIRECTIONAL, LIGHT_DIRECTIONAL }, + { LIGHT_POINT, LIGHT_POINT, LIGHT_POINT, LIGHT_POINT }, + { LIGHT_POINT, LIGHT_POINT, LIGHT_POINT, LIGHT_DIRECTIONAL }, + { LIGHT_POINT, LIGHT_POINT, LIGHT_DIRECTIONAL, LIGHT_DIRECTIONAL }, + { LIGHT_POINT, LIGHT_DIRECTIONAL, LIGHT_DIRECTIONAL, LIGHT_DIRECTIONAL }, + { LIGHT_DIRECTIONAL, LIGHT_DIRECTIONAL, LIGHT_DIRECTIONAL, LIGHT_DIRECTIONAL } +}; + +// This is just for getting light combos for DX8 +// For DX9, use GetDX9LightState() +// It is up to the shader cpp files to use the right method +int CShaderAPIDx8::GetCurrentLightCombo( void ) const +{ + Assert( g_pHardwareConfig->Caps().m_nDXSupportLevel <= 81 ); + + Assert( m_DynamicState.m_NumLights <= 2 ); + + COMPILE_TIME_ASSERT( DX8_LIGHTING_COMBINATION_COUNT == + sizeof( g_DX8LightCombinations ) / sizeof( g_DX8LightCombinations[0] ) ); + + // hack . . do this a cheaper way. + bool bUseAmbientCube; + if( m_DynamicState.m_AmbientLightCube[0][0] == 0.0f && + m_DynamicState.m_AmbientLightCube[0][1] == 0.0f && + m_DynamicState.m_AmbientLightCube[0][2] == 0.0f && + m_DynamicState.m_AmbientLightCube[1][0] == 0.0f && + m_DynamicState.m_AmbientLightCube[1][1] == 0.0f && + m_DynamicState.m_AmbientLightCube[1][2] == 0.0f && + m_DynamicState.m_AmbientLightCube[2][0] == 0.0f && + m_DynamicState.m_AmbientLightCube[2][1] == 0.0f && + m_DynamicState.m_AmbientLightCube[2][2] == 0.0f && + m_DynamicState.m_AmbientLightCube[3][0] == 0.0f && + m_DynamicState.m_AmbientLightCube[3][1] == 0.0f && + m_DynamicState.m_AmbientLightCube[3][2] == 0.0f && + m_DynamicState.m_AmbientLightCube[4][0] == 0.0f && + m_DynamicState.m_AmbientLightCube[4][1] == 0.0f && + m_DynamicState.m_AmbientLightCube[4][2] == 0.0f && + m_DynamicState.m_AmbientLightCube[5][0] == 0.0f && + m_DynamicState.m_AmbientLightCube[5][1] == 0.0f && + m_DynamicState.m_AmbientLightCube[5][2] == 0.0f ) + { + bUseAmbientCube = false; + } + else + { + bUseAmbientCube = true; + } + + Assert( m_pRenderMesh ); + + const VertexShaderLightTypes_t *pLightType = m_DynamicState.m_LightType; + + if( m_DynamicState.m_NumLights == 0 && !bUseAmbientCube ) + { + if( m_pRenderMesh->HasColorMesh() ) + return 1; // special case for static lighting only + else + return 0; // special case for no lighting at all. + } + + int i; + // hack - skip the first two for now since we don't know if the ambient cube is needed or not. + for( i = 2; i < DX9_LIGHTING_COMBINATION_COUNT; ++i ) + { + int j; + for( j = 0; j < m_DynamicState.m_NumLights; ++j ) + { + if( pLightType[j] != g_DX8LightCombinations[i][j+2] ) + break; + } + if( j == m_DynamicState.m_NumLights ) + { + while( j < 2 ) + { + if (g_DX8LightCombinations[i][j+2] != LIGHT_NONE) + break; + ++j; + } + if( j == 2 ) + { + if( m_pRenderMesh->HasColorMesh() ) + { + return i + 10; + } + else + { + return i; + } + } + } + } + + // should never get here! + Assert( 0 ); + return 0; +} + +void CShaderAPIDx8::GetDX9LightState( LightState_t *state ) const +{ + // hack . . do this a cheaper way. + if( m_DynamicState.m_AmbientLightCube[0][0] == 0.0f && + m_DynamicState.m_AmbientLightCube[0][1] == 0.0f && + m_DynamicState.m_AmbientLightCube[0][2] == 0.0f && + m_DynamicState.m_AmbientLightCube[1][0] == 0.0f && + m_DynamicState.m_AmbientLightCube[1][1] == 0.0f && + m_DynamicState.m_AmbientLightCube[1][2] == 0.0f && + m_DynamicState.m_AmbientLightCube[2][0] == 0.0f && + m_DynamicState.m_AmbientLightCube[2][1] == 0.0f && + m_DynamicState.m_AmbientLightCube[2][2] == 0.0f && + m_DynamicState.m_AmbientLightCube[3][0] == 0.0f && + m_DynamicState.m_AmbientLightCube[3][1] == 0.0f && + m_DynamicState.m_AmbientLightCube[3][2] == 0.0f && + m_DynamicState.m_AmbientLightCube[4][0] == 0.0f && + m_DynamicState.m_AmbientLightCube[4][1] == 0.0f && + m_DynamicState.m_AmbientLightCube[4][2] == 0.0f && + m_DynamicState.m_AmbientLightCube[5][0] == 0.0f && + m_DynamicState.m_AmbientLightCube[5][1] == 0.0f && + m_DynamicState.m_AmbientLightCube[5][2] == 0.0f ) + { + state->m_bAmbientLight = false; + } + else + { + state->m_bAmbientLight = true; + } + + Assert( m_pRenderMesh ); + Assert( m_DynamicState.m_NumLights <= 4 ); + + if ( g_pHardwareConfig->SupportsPixelShaders_2_b() ) + { + Assert( m_DynamicState.m_NumLights <= MAX_LIGHTS ); // 2b hardware gets four lights + } + else + { + Assert( m_DynamicState.m_NumLights <= (MAX_LIGHTS-2) ); // 2.0 hardware gets two less + } + +#ifdef OSX + state->m_nNumLights = MIN(MAX_NUM_LIGHTS,m_DynamicState.m_NumLights); +#else + state->m_nNumLights = m_DynamicState.m_NumLights; +#endif + + state->m_nNumLights = m_DynamicState.m_NumLights; + state->m_bStaticLightVertex = m_pRenderMesh->HasColorMesh(); + state->m_bStaticLightTexel = false; // For now +} + +MaterialFogMode_t CShaderAPIDx8::GetCurrentFogType( void ) const +{ + return m_DynamicState.m_SceneFog; +} + +void CShaderAPIDx8::RecordString( const char *pStr ) +{ + RECORD_STRING( pStr ); +} + +void CShaderAPIDx8::EvictManagedResourcesInternal() +{ + if ( IsX360() ) + return; + + if ( !ThreadOwnsDevice() || !ThreadInMainThread() ) + { + ShaderUtil()->OnThreadEvent( SHADER_THREAD_EVICT_RESOURCES ); + return; + } + if ( mat_debugalttab.GetBool() ) + { + Warning( "mat_debugalttab: CShaderAPIDx8::EvictManagedResourcesInternal\n" ); + } + +#if !defined( _X360 ) + if ( Dx9Device() ) + { + Dx9Device()->EvictManagedResources(); + } +#endif +} + +void CShaderAPIDx8::EvictManagedResources( void ) +{ + if ( IsX360() ) + { + return; + } + + LOCK_SHADERAPI(); + Assert(ThreadOwnsDevice()); + // Tell other material system applications to release resources + SendIPCMessage( EVICT_MESSAGE ); + EvictManagedResourcesInternal(); +} + +bool CShaderAPIDx8::IsDebugTextureListFresh( int numFramesAllowed /* = 1 */ ) +{ + return ( m_nDebugDataExportFrame <= m_CurrentFrame ) && ( m_nDebugDataExportFrame >= m_CurrentFrame - numFramesAllowed ); +} + +bool CShaderAPIDx8::SetDebugTextureRendering( bool bEnable ) +{ + bool bVal = m_bDebugTexturesRendering; + m_bDebugTexturesRendering = bEnable; + return bVal; +} + +void CShaderAPIDx8::EnableDebugTextureList( bool bEnable ) +{ + m_bEnableDebugTextureList = bEnable; +} + +void CShaderAPIDx8::EnableGetAllTextures( bool bEnable ) +{ + m_bDebugGetAllTextures = bEnable; +} + +KeyValues* CShaderAPIDx8::GetDebugTextureList() +{ + return m_pDebugTextureList; +} + +int CShaderAPIDx8::GetTextureMemoryUsed( TextureMemoryType eTextureMemory ) +{ + switch ( eTextureMemory ) + { + case MEMORY_BOUND_LAST_FRAME: + return m_nTextureMemoryUsedLastFrame; + case MEMORY_TOTAL_LOADED: + return m_nTextureMemoryUsedTotal; + case MEMORY_ESTIMATE_PICMIP_1: + return m_nTextureMemoryUsedPicMip1; + case MEMORY_ESTIMATE_PICMIP_2: + return m_nTextureMemoryUsedPicMip2; + default: + return 0; + } +} + + +// Allocate and delete query objects. +ShaderAPIOcclusionQuery_t CShaderAPIDx8::CreateOcclusionQueryObject( void ) +{ + // don't allow this on <80 because it falls back to wireframe in that case + if( m_DeviceSupportsCreateQuery == 0 || g_pHardwareConfig->Caps().m_nDXSupportLevel < 80 ) + return INVALID_SHADERAPI_OCCLUSION_QUERY_HANDLE; + + // While we're deactivated, m_OcclusionQueryObjects just holds NULL pointers. + // Create a dummy one here and let ReacquireResources create the actual D3D object. + if ( IsDeactivated() ) + return INVALID_SHADERAPI_OCCLUSION_QUERY_HANDLE; + + IDirect3DQuery9 *pQuery = NULL; + HRESULT hr = Dx9Device()->CreateQuery( D3DQUERYTYPE_OCCLUSION, &pQuery ); + return ( hr == D3D_OK ) ? (ShaderAPIOcclusionQuery_t)pQuery : INVALID_SHADERAPI_OCCLUSION_QUERY_HANDLE; +} + +void CShaderAPIDx8::DestroyOcclusionQueryObject( ShaderAPIOcclusionQuery_t handle ) +{ + IDirect3DQuery9 *pQuery = (IDirect3DQuery9 *)handle; + + int nRetVal = pQuery->Release(); + Assert( nRetVal == 0 ); +} + +// Bracket drawing with begin and end so that we can get counts next frame. +void CShaderAPIDx8::BeginOcclusionQueryDrawing( ShaderAPIOcclusionQuery_t handle ) +{ + IDirect3DQuery9 *pQuery = (IDirect3DQuery9 *)handle; + + HRESULT hResult = pQuery->Issue( D3DISSUE_BEGIN ); + Assert( hResult == D3D_OK ); +} + +void CShaderAPIDx8::EndOcclusionQueryDrawing( ShaderAPIOcclusionQuery_t handle ) +{ + IDirect3DQuery9 *pQuery = (IDirect3DQuery9 *)handle; + + HRESULT hResult = pQuery->Issue( D3DISSUE_END ); + Assert( hResult == D3D_OK ); +} + +// Get the number of pixels rendered between begin and end on an earlier frame. +// Calling this in the same frame is a huge perf hit! +int CShaderAPIDx8::OcclusionQuery_GetNumPixelsRendered( ShaderAPIOcclusionQuery_t handle, bool bFlush ) +{ + LOCK_SHADERAPI(); + IDirect3DQuery9 *pQuery = (IDirect3DQuery9 *)handle; + + tmZone( TELEMETRY_LEVEL1, TMZF_NONE, "%s", __FUNCTION__ ); + + DWORD nPixels; + HRESULT hResult = pQuery->GetData( &nPixels, sizeof( nPixels ), bFlush ? D3DGETDATA_FLUSH : 0 ); + + // This means that the query will not finish and resulted in an error, game should use + // the previous query's results and not reissue the query + if ( ( hResult == D3DERR_DEVICELOST ) || ( hResult == D3DERR_NOTAVAILABLE ) ) + return OCCLUSION_QUERY_RESULT_ERROR; + + // This means the query isn't finished yet, game will have to use the previous query's + // results and not reissue the query; wait for query to finish later. + if ( hResult == S_FALSE ) + return OCCLUSION_QUERY_RESULT_PENDING; + + // NOTE: This appears to work around a driver bug for ATI on Vista + if ( nPixels & 0x80000000 ) + { + nPixels = 0; + } + return nPixels; +} + + +void CShaderAPIDx8::SetPixelShaderFogParams( int reg, ShaderFogMode_t fogMode ) +{ + m_DelayedShaderConstants.iPixelShaderFogParams = reg; //save it off in case the ShaderFogMode_t disables fog. We only find out later. + float fogParams[4]; + + if( (GetPixelFogMode() != MATERIAL_FOG_NONE) && (fogMode != SHADER_FOGMODE_DISABLED) ) + { + float ooFogRange = 1.0f; + + float fStart = m_VertexShaderFogParams[0]; + float fEnd = m_VertexShaderFogParams[1]; + + // Check for divide by zero + if ( fEnd != fStart ) + { + ooFogRange = 1.0f / ( fEnd - fStart ); + } + + fogParams[0] = fStart * ooFogRange; // fogStart / ( fogEnd - fogStart ) + fogParams[1] = m_DynamicState.m_FogZ; // water height + fogParams[2] = clamp( m_flFogMaxDensity, 0.0f, 1.0f ); // Max fog density + fogParams[3] = ooFogRange; // 1 / ( fogEnd - fogStart ); + + if (GetPixelFogMode() == MATERIAL_FOG_LINEAR_BELOW_FOG_Z) + { + // terms are unused for height fog, forcing 1.0 allows unified PS math + fogParams[0] = 0.0f; + fogParams[2] = 1.0f; + } + } + else + { + //emulating MATERIAL_FOG_NONE by setting the parameters so that CalcRangeFog() always returns 0. Gets rid of a dynamic combo across the ps2x set. + fogParams[0] = 0.0f; + fogParams[1] = m_DynamicState.m_FogZ; // water height + fogParams[2] = 1.0f; // Max fog density + fogParams[3] = 0.0f; + } + + // cFogEndOverFogRange, cFogOne, unused, cOOFogRange + SetPixelShaderConstant( reg, fogParams, 1 ); +} + +void CShaderAPIDx8::SetPixelShaderFogParams( int reg ) +{ + SetPixelShaderFogParams( reg, m_TransitionTable.CurrentShadowState()->m_FogMode ); +} + +void CShaderAPIDx8::SetFlashlightState( const FlashlightState_t &state, const VMatrix &worldToTexture ) +{ + LOCK_SHADERAPI(); + SetFlashlightStateEx( state, worldToTexture, NULL ); +} + +void CShaderAPIDx8::SetFlashlightStateEx( const FlashlightState_t &state, const VMatrix &worldToTexture, ITexture *pFlashlightDepthTexture ) +{ + LOCK_SHADERAPI(); + // fixme: do a test here. + FlushBufferedPrimitives(); + m_FlashlightState = state; + m_FlashlightWorldToTexture = worldToTexture; + m_pFlashlightDepthTexture = pFlashlightDepthTexture; + + if ( !g_pHardwareConfig->SupportsPixelShaders_2_b() ) + { + m_FlashlightState.m_bEnableShadows = false; + m_pFlashlightDepthTexture = NULL; + } +} + +const FlashlightState_t &CShaderAPIDx8::GetFlashlightState( VMatrix &worldToTexture ) const +{ + worldToTexture = m_FlashlightWorldToTexture; + return m_FlashlightState; +} + +const FlashlightState_t &CShaderAPIDx8::GetFlashlightStateEx( VMatrix &worldToTexture, ITexture **ppFlashlightDepthTexture ) const +{ + worldToTexture = m_FlashlightWorldToTexture; + *ppFlashlightDepthTexture = m_pFlashlightDepthTexture; + return m_FlashlightState; +} + +bool CShaderAPIDx8::SupportsMSAAMode( int nMSAAMode ) +{ + if ( IsX360() ) + { + return false; + } + + return ( D3D_OK == D3D()->CheckDeviceMultiSampleType( m_DisplayAdapter, m_DeviceType, + m_PresentParameters.BackBufferFormat, + m_PresentParameters.Windowed, + ComputeMultisampleType( nMSAAMode ), NULL ) ); +} + +bool CShaderAPIDx8::SupportsCSAAMode( int nNumSamples, int nQualityLevel ) +{ +#ifdef DX_TO_GL_ABSTRACTION + // GL_NV_framebuffer_multisample_coverage + return false; +#endif + + // Only nVidia does this kind of AA + if ( g_pHardwareConfig->Caps().m_VendorID != VENDORID_NVIDIA ) + return false; + + DWORD dwQualityLevels = 0; + HRESULT hr = D3D()->CheckDeviceMultiSampleType( m_DisplayAdapter, m_DeviceType, + m_PresentParameters.BackBufferFormat, + m_PresentParameters.Windowed, + ComputeMultisampleType( nNumSamples ), &dwQualityLevels ); + + return ( ( D3D_OK == hr ) && ( (int) dwQualityLevels >= nQualityLevel ) ); +} + +bool CShaderAPIDx8::SupportsShadowDepthTextures( void ) +{ + return g_pHardwareConfig->Caps().m_bSupportsShadowDepthTextures; +} + +bool CShaderAPIDx8::SupportsNormalMapCompression( void ) const +{ + return g_pHardwareConfig->Caps().m_bSupportsNormalMapCompression; +} + +bool CShaderAPIDx8::SupportsBorderColor( void ) const +{ + return g_pHardwareConfig->Caps().m_bSupportsBorderColor; +} + +bool CShaderAPIDx8::SupportsFetch4( void ) +{ + return IsPC() && g_pHardwareConfig->Caps().m_bSupportsFetch4; +} + +ImageFormat CShaderAPIDx8::GetShadowDepthTextureFormat( void ) +{ + return g_pHardwareConfig->Caps().m_ShadowDepthTextureFormat; +} + +ImageFormat CShaderAPIDx8::GetNullTextureFormat( void ) +{ + return g_pHardwareConfig->Caps().m_NullTextureFormat; +} + +void CShaderAPIDx8::SetShadowDepthBiasFactors( float fShadowSlopeScaleDepthBias, float fShadowDepthBias ) +{ + m_fShadowSlopeScaleDepthBias = fShadowSlopeScaleDepthBias; + m_fShadowDepthBias = fShadowDepthBias; +} + +void CShaderAPIDx8::ClearVertexAndPixelShaderRefCounts() +{ + LOCK_SHADERAPI(); + ShaderManager()->ClearVertexAndPixelShaderRefCounts(); +} + +void CShaderAPIDx8::PurgeUnusedVertexAndPixelShaders() +{ + LOCK_SHADERAPI(); + ShaderManager()->PurgeUnusedVertexAndPixelShaders(); +} + +bool CShaderAPIDx8::UsingSoftwareVertexProcessing() const +{ + return g_pHardwareConfig->Caps().m_bSoftwareVertexProcessing; +} + +ITexture *CShaderAPIDx8::GetRenderTargetEx( int nRenderTargetID ) +{ + return ShaderUtil()->GetRenderTargetEx( nRenderTargetID ); +} + +float CShaderAPIDx8::GetLightMapScaleFactor( void ) const +{ + switch( HardwareConfig()->GetHDRType() ) + { + + case HDR_TYPE_FLOAT: + return 1.0; + break; + + case HDR_TYPE_INTEGER: + return 16.0; + + case HDR_TYPE_NONE: + default: + return GammaToLinearFullRange( 2.0 ); // light map scale + } +} + +void CShaderAPIDx8::SetToneMappingScaleLinear( const Vector &scale ) +{ + if ( g_pHardwareConfig->SupportsPixelShaders_2_0() ) + { + // Flush buffered primitives before changing the tone map scalar! + FlushBufferedPrimitives(); + + Vector scale_to_use = scale; + m_ToneMappingScale.AsVector3D() = scale_to_use; + + bool mode_uses_srgb=false; + + switch( HardwareConfig()->GetHDRType() ) + { + case HDR_TYPE_NONE: + m_ToneMappingScale.x = 1.0; // output scale + m_ToneMappingScale.z = 1.0; // reflection map scale + break; + + case HDR_TYPE_FLOAT: + m_ToneMappingScale.x = scale_to_use.x; // output scale + m_ToneMappingScale.z = 1.0; // reflection map scale + break; + + case HDR_TYPE_INTEGER: + mode_uses_srgb = true; + m_ToneMappingScale.x = scale_to_use.x; // output scale + m_ToneMappingScale.z = 16.0; // reflection map scale + break; + } + + m_ToneMappingScale.y = GetLightMapScaleFactor(); // light map scale + + // w component gets gamma scale + m_ToneMappingScale.w = LinearToGammaFullRange( m_ToneMappingScale.x ); + SetPixelShaderConstant( TONE_MAPPING_SCALE_PSH_CONSTANT, m_ToneMappingScale.Base() ); + + // We have to change the fog color since we tone map directly in the shaders in integer HDR mode. + if ( HardwareConfig()->GetHDRType() == HDR_TYPE_INTEGER && m_TransitionTable.CurrentShadowState() ) + { + // Get the shadow state in sync since it depends on SetToneMappingScaleLinear. + ApplyFogMode( m_TransitionTable.CurrentShadowState()->m_FogMode, mode_uses_srgb, m_TransitionTable.CurrentShadowState()->m_bDisableFogGammaCorrection ); + } + } +} + +const Vector & CShaderAPIDx8::GetToneMappingScaleLinear( void ) const +{ + return m_ToneMappingScale.AsVector3D(); +} + +void CShaderAPIDx8::EnableLinearColorSpaceFrameBuffer( bool bEnable ) +{ + LOCK_SHADERAPI(); + m_TransitionTable.EnableLinearColorSpaceFrameBuffer( bEnable ); +} + + +void CShaderAPIDx8::SetPSNearAndFarZ( int pshReg ) +{ + VMatrix m; + GetMatrix( MATERIAL_PROJECTION, m.m[0] ); + + // m[2][2] = F/(N-F) (flip sign if RH) + // m[3][2] = NF/(N-F) + + float vNearFar[4]; + + float N = m[3][2] / m[2][2]; + float F = (m[3][2]*N) / (N + m[3][2]); + + vNearFar[0] = N; + vNearFar[1] = F; + + + SetPixelShaderConstant( pshReg, vNearFar, 1 ); +} + + +void CShaderAPIDx8::SetFloatRenderingParameter( int parm_number, float value ) +{ + LOCK_SHADERAPI(); + if ( parm_number < ARRAYSIZE( FloatRenderingParameters )) + FloatRenderingParameters[parm_number] = value; +} + +void CShaderAPIDx8::SetIntRenderingParameter( int parm_number, int value ) +{ + LOCK_SHADERAPI(); + if ( parm_number < ARRAYSIZE( IntRenderingParameters )) + IntRenderingParameters[parm_number] = value; +} + +void CShaderAPIDx8::SetVectorRenderingParameter( int parm_number, Vector const & value ) +{ + LOCK_SHADERAPI(); + if ( parm_number < ARRAYSIZE( VectorRenderingParameters )) + VectorRenderingParameters[parm_number] = value; +} + +float CShaderAPIDx8::GetFloatRenderingParameter( int parm_number ) const +{ + LOCK_SHADERAPI(); + if ( parm_number < ARRAYSIZE( FloatRenderingParameters )) + return FloatRenderingParameters[parm_number]; + else + return 0.0; +} + +int CShaderAPIDx8::GetIntRenderingParameter( int parm_number ) const +{ + LOCK_SHADERAPI(); + if ( parm_number < ARRAYSIZE( IntRenderingParameters )) + return IntRenderingParameters[parm_number]; + else + return 0; +} + +Vector CShaderAPIDx8::GetVectorRenderingParameter( int parm_number ) const +{ + LOCK_SHADERAPI(); + if ( parm_number < ARRAYSIZE( VectorRenderingParameters )) + return VectorRenderingParameters[parm_number]; + else + return Vector( 0, 0, 0 ); +} + +// stencil entry points +void CShaderAPIDx8::SetStencilEnable( bool onoff ) +{ + LOCK_SHADERAPI(); + SetRenderState( D3DRS_STENCILENABLE, onoff?TRUE:FALSE, true ); +} + +void CShaderAPIDx8::SetStencilFailOperation( StencilOperation_t op ) +{ + LOCK_SHADERAPI(); + SetRenderState( D3DRS_STENCILFAIL, op, true ); +} + +void CShaderAPIDx8::SetStencilZFailOperation( StencilOperation_t op ) +{ + LOCK_SHADERAPI(); + SetRenderState( D3DRS_STENCILZFAIL, op, true ); +} + +void CShaderAPIDx8::SetStencilPassOperation( StencilOperation_t op ) +{ + LOCK_SHADERAPI(); + SetRenderState( D3DRS_STENCILPASS, op, true ); +} + +void CShaderAPIDx8::SetStencilCompareFunction( StencilComparisonFunction_t cmpfn ) +{ + LOCK_SHADERAPI(); + SetRenderState( D3DRS_STENCILFUNC, cmpfn, true ); +} + +void CShaderAPIDx8::SetStencilReferenceValue( int ref ) +{ + LOCK_SHADERAPI(); + SetRenderState( D3DRS_STENCILREF, ref, true ); +} + +void CShaderAPIDx8::SetStencilTestMask( uint32 msk ) +{ + LOCK_SHADERAPI(); + SetRenderState( D3DRS_STENCILMASK, msk, true ); +} + +void CShaderAPIDx8::SetStencilWriteMask( uint32 msk ) +{ + LOCK_SHADERAPI(); + SetRenderState( D3DRS_STENCILWRITEMASK, msk, true ); +} + +void CShaderAPIDx8::ClearStencilBufferRectangle( + int xmin, int ymin, int xmax, int ymax,int value) +{ + LOCK_SHADERAPI(); + D3DRECT clear; + clear.x1 = xmin; + clear.y1 = ymin; + clear.x2 = xmax; + clear.y2 = ymax; + + Dx9Device()->Clear( + 1, &clear, D3DCLEAR_STENCIL, 0, 0, value ); +} + +int CShaderAPIDx8::CompareSnapshots( StateSnapshot_t snapshot0, StateSnapshot_t snapshot1 ) +{ + LOCK_SHADERAPI(); + const ShadowState_t &shadow0 = m_TransitionTable.GetSnapshot(snapshot0); + const ShadowState_t &shadow1 = m_TransitionTable.GetSnapshot(snapshot1); + const ShadowShaderState_t &shader0 = m_TransitionTable.GetSnapshotShader(snapshot0); + const ShadowShaderState_t &shader1 = m_TransitionTable.GetSnapshotShader(snapshot1); + + int dVertex = shader0.m_VertexShader - shader1.m_VertexShader; + if ( dVertex ) + return dVertex; + int dVCombo = shader0.m_nStaticVshIndex - shader1.m_nStaticVshIndex; + if ( dVCombo) + return dVCombo; + + int dPixel = shader0.m_PixelShader - shader1.m_PixelShader; + if ( dPixel ) + return dPixel; + int dPCombo = shader0.m_nStaticPshIndex - shader1.m_nStaticPshIndex; + if ( dPCombo) + return dPCombo; + + return snapshot0 - snapshot1; +} + +//----------------------------------------------------------------------------- +// X360 TTF support requires XUI state manipulation of d3d. +// Font support lives inside the shaderapi in order to maintain privacy of d3d. +//----------------------------------------------------------------------------- +#if defined( _X360 ) +HXUIFONT CShaderAPIDx8::OpenTrueTypeFont( const char *pFontname, int tall, int style ) +{ + LOCK_SHADERAPI(); + + struct fontTable_t + { + const char *pFontName; + const char *pPath; + }; + + // explicit mapping now required, dvd searching to expensive + static fontTable_t fontToFilename[] = + { + {"tf2", "tf/resource/tf2.ttf"}, + {"tf2 build", "tf/resource/tf2build.ttf"}, + {"tf2 professor", "tf/resource/tf2professor.ttf"}, + {"tf2 secondary", "tf/resource/tf2secondary.ttf"}, + {"team fortress", "tf/resource/tf.ttf"}, + {"tfd", "tf/resource/tfd.ttf"}, + {"tflogo", "tf/resource/tflogo.ttf"}, + {"hl2ep2", "ep2/resource/hl2ep2.ttf"}, + {"hl2ep1", "episodic/resource/hl2ep1.ttf"}, + {"halflife2", "hl2/resource/halflife2.ttf"}, + {"hl2cross", "hl2/resource/HL2Crosshairs.ttf"}, + {"courier new", "platform/vgui/fonts/cour.ttf"}, + {"times new roman", "platform/vgui/fonts/times.ttf"}, + {"trebuchet ms", "platform/vgui/fonts/trebuc.ttf"}, + {"verdana", "platform/vgui/fonts/verdana.ttf"}, + {"tahoma", "platform/vgui/fonts/tahoma.ttf"}, + }; + + // remap typeface to diskname + const char *pDiskname = NULL; + for ( int i=0; iszTypeface, wchFontname ) ) + { + bRegistered = true; + break; + } + } + + XuiDestroyTypefaceList( pDescriptors, numTypeFaces ); + + if ( !bRegistered ) + { + // unregistered type face, register type face and retry + // only file based resource locators work + char filename[MAX_PATH]; + V_snprintf( filename, sizeof( filename ), "file://d:/%s", pDiskname ); + Q_FixSlashes( filename, '/' ); + + wchar_t wchFilename[MAX_PATH]; + Q_UTF8ToUnicode( filename, wchFilename, sizeof( wchFilename ) ); + + TypefaceDescriptor desc; + desc.fBaselineAdjust = 0; + desc.szFallbackTypeface = NULL; + desc.szLocator = wchFilename; + desc.szReserved1 = 0; + desc.szTypeface = wchFontname; + hr = XuiRegisterTypeface( &desc, FALSE ); + if ( FAILED( hr ) ) + { + return NULL; + } + } + + // empirically derived factor to achieve desired cell height + float pointSize = tall * 0.59f; + HXUIFONT hFont = NULL; + hr = XuiCreateFont( wchFontname, pointSize, style, 0, &hFont ); + if ( FAILED( hr ) ) + { + return NULL; + } + + return hFont; +} +#endif + +//----------------------------------------------------------------------------- +// Release TTF +//----------------------------------------------------------------------------- +#if defined( _X360 ) +void CShaderAPIDx8::CloseTrueTypeFont( HXUIFONT hFont ) +{ + if ( !hFont ) + return; + LOCK_SHADERAPI(); + + XuiReleaseFont( hFont ); +} +#endif + +//----------------------------------------------------------------------------- +// Get the TTF Metrics +//----------------------------------------------------------------------------- +#if defined( _X360 ) +bool CShaderAPIDx8::GetTrueTypeFontMetrics( HXUIFONT hFont, XUIFontMetrics *pFontMetrics, XUICharMetrics charMetrics[256] ) +{ + if ( !hFont ) + return false; + + LOCK_SHADERAPI(); + + V_memset( charMetrics, 0, 256 * sizeof( XUICharMetrics ) ); + + HRESULT hr = XuiGetFontMetrics( hFont, pFontMetrics ); + if ( !FAILED( hr ) ) + { + // X360 issue: max character width may be too small. + // Run through each character and fixup + for ( int i = 1; i < 256; i++ ) + { + wchar_t wch = i; + hr = XuiGetCharMetrics( hFont, wch, &charMetrics[i] ); + if ( !FAILED( hr ) ) + { + float maxWidth = charMetrics[i].fMaxX; + if ( charMetrics[i].fMinX < 0 ) + { + maxWidth = charMetrics[i].fMaxX - charMetrics[i].fMinX; + } + if ( maxWidth > pFontMetrics->fMaxWidth ) + { + pFontMetrics->fMaxWidth = maxWidth; + } + if ( charMetrics[i].fAdvance > pFontMetrics->fMaxWidth ) + { + pFontMetrics->fMaxWidth = charMetrics[i].fAdvance; + } + } + } + + // fonts are getting cut off, MaxHeight seems to be misreported smaller + // take MaxHeight to be the larger of its reported value or (ascent + descent) + float maxHeight = 0; + if ( pFontMetrics->fMaxDescent <= 0 ) + { + // descent is negative for below baseline + maxHeight = pFontMetrics->fMaxAscent - pFontMetrics->fMaxDescent; + } + if ( maxHeight > pFontMetrics->fMaxHeight ) + { + pFontMetrics->fMaxHeight = maxHeight; + } + } + + return ( !FAILED( hr ) ); +} +#endif + +//----------------------------------------------------------------------------- +// Gets the glyph bits in rgba order. This function PURPOSELY hijacks D3D +// because XUI is involved. It is called at a very specific place in the VGUI +// render frame where its deleterious affects are going to be harmless. +//----------------------------------------------------------------------------- +#if defined( _X360 ) +bool CShaderAPIDx8::GetTrueTypeGlyphs( HXUIFONT hFont, int numChars, wchar_t *pWch, int *pOffsetX, int *pOffsetY, int *pWidth, int *pHeight, unsigned char *pRGBA, int *pRGBAOffset ) +{ + if ( !hFont ) + return false; + + // Ensure this doesn't talk to D3D at the same time as the loading bar + AUTO_LOCK_FM( m_nonInteractiveModeMutex ); + + + LOCK_SHADERAPI(); + bool bSuccess = false; + IDirect3DSurface *pRTSurface = NULL; + IDirect3DSurface *pSavedSurface = NULL; + IDirect3DSurface *pSavedDepthSurface = NULL; + IDirect3DTexture *pTexture = NULL; + D3DVIEWPORT9 savedViewport; + D3DXMATRIX matView; + D3DXMATRIX matXForm; + D3DLOCKED_RECT lockedRect; + + // have to reset to default state to rasterize glyph correctly + // state will get re-established during next mesh draw + ResetRenderState( false ); + Dx9Device()->SetRenderState( D3DRS_ZENABLE, FALSE ); + + Dx9Device()->GetRenderTarget( 0, &pSavedSurface ); + Dx9Device()->GetDepthStencilSurface( &pSavedDepthSurface ); + Dx9Device()->GetViewport( &savedViewport ); + + // Figure out the size of surface/texture we need to allocate + int rtWidth = 0; + int rtHeight = 0; + for ( int i = 0; i < numChars; i++ ) + { + rtWidth += pWidth[i]; + rtHeight = max( rtHeight, pHeight[i] ); + } + + // per resolve() restrictions + rtWidth = AlignValue( rtWidth, 32 ); + rtHeight = AlignValue( rtHeight, 32 ); + + // create a render target to capture the glyph render + pRTSurface = g_TextureHeap.AllocRenderTargetSurface( rtWidth, rtHeight, D3DFMT_A8R8G8B8 ); + if ( !pRTSurface ) + goto cleanUp; + + Dx9Device()->SetRenderTarget( 0, pRTSurface ); + // Disable depth here otherwise you get a colour/depth multisample mismatch error (in 480p) + Dx9Device()->SetDepthStencilSurface( NULL ); + Dx9Device()->Clear( 0, NULL, D3DCLEAR_TARGET, 0x00000000, ( ReverseDepthOnX360() ? 0.0 : 1.0f ), 0 ); + + // create texture to get glyph render from EDRAM + HRESULT hr = Dx9Device()->CreateTexture( rtWidth, rtHeight, 1, 0, D3DFMT_A8R8G8B8, 0, &pTexture, NULL ); + if ( FAILED( hr ) ) + goto cleanUp; + + + bool bPreviousOwnState = OwnGPUResources( false ); + XuiRenderBegin( m_hDC, 0x00000000 ); + + D3DXMatrixIdentity( &matView ); + XuiRenderSetViewTransform( m_hDC, &matView ); + XuiRenderSetTransform( m_hDC, &matView ); + + // rasterize the glyph + XuiSelectFont( m_hDC, hFont ); + XuiSetColorFactor( m_hDC, 0xFFFFFFFF ); + + // Draw the characters, stepping across the texture + int xCursor = 0; + for ( int i = 0; i < numChars; i++) + { + // FIXME: the drawRect params don't make much sense (should use "(xCursor+pWidth[i]), pHeight[i]", but then some characters disappear!) + XUIRect drawRect = XUIRect( xCursor + pOffsetX[i], pOffsetY[i], rtWidth, rtHeight ); + wchar_t text[2] = { pWch[i], 0 }; + XuiDrawText( m_hDC, text, XUI_FONT_STYLE_NORMAL|XUI_FONT_STYLE_SINGLE_LINE|XUI_FONT_STYLE_NO_WORDWRAP, 0, &drawRect ); + xCursor += pWidth[i]; + } + + XuiRenderEnd( m_hDC ); + OwnGPUResources( bPreviousOwnState ); + + + // transfer from edram to system + hr = Dx9Device()->Resolve( 0, NULL, pTexture, NULL, 0, 0, NULL, 0, 0, NULL ); + if ( FAILED( hr ) ) + goto cleanUp; + + hr = pTexture->LockRect( 0, &lockedRect, NULL, 0 ); + if ( FAILED( hr ) ) + goto cleanUp; + + // transfer to linear format, one character at a time + xCursor = 0; + for ( int i = 0;i < numChars; i++ ) + { + int destPitch = pWidth[i]*4; + unsigned char *pLinear = pRGBA + pRGBAOffset[i]; + RECT copyRect = { xCursor, 0, xCursor + pWidth[i], pHeight[i] }; + xCursor += pWidth[i]; + XGUntileSurface( pLinear, destPitch, NULL, lockedRect.pBits, rtWidth, rtHeight, ©Rect, 4 ); + + // convert argb to rgba + float r, g, b, a; + for ( int y = 0; y < pHeight[i]; y++ ) + { + unsigned char *pSrc = (unsigned char*)pLinear + y*destPitch; + for ( int x = 0; x < pWidth[i]; x++ ) + { + // undo pre-multiplied alpha since glyph bits will be sourced as a rgba texture + if ( !pSrc[0] ) + a = 1; + else + a = (float)pSrc[0] * 1.0f/255.0f; + + r = ((float)pSrc[1] * 1.0f/255.0f)/a * 255.0f; + if ( r > 255 ) + r = 255; + + g = ((float)pSrc[2] * 1.0f/255.0f)/a * 255.0f; + if ( g > 255 ) + g = 255; + + b = ((float)pSrc[3] * 1.0f/255.0f)/a * 255.0f; + if ( b > 255 ) + b = 255; + + pSrc[3] = pSrc[0]; + pSrc[2] = b; + pSrc[1] = g; + pSrc[0] = r; + + pSrc += 4; + } + } + } + + pTexture->UnlockRect( 0 ); + + bSuccess = true; + +cleanUp: + if ( pRTSurface ) + { + Dx9Device()->SetRenderTarget( 0, pSavedSurface ); + Dx9Device()->SetDepthStencilSurface( pSavedDepthSurface ); + Dx9Device()->SetViewport( &savedViewport ); + pRTSurface->Release(); + } + + if ( pTexture ) + pTexture->Release(); + + if ( pSavedSurface ) + pSavedSurface->Release(); + + // XUI changed renderstates behind our back, so we need to reset to defaults again to get back in synch: + ResetRenderState( false ); + + return bSuccess; +} +#endif + +//----------------------------------------------------------------------------- +// Create a 360 Render Target Surface +//----------------------------------------------------------------------------- +#if defined( _X360 ) +ShaderAPITextureHandle_t CShaderAPIDx8::CreateRenderTargetSurface( int width, int height, ImageFormat format, const char *pDebugName, const char *pTextureGroupName ) +{ + LOCK_SHADERAPI(); + ShaderAPITextureHandle_t textureHandle = CreateTextureHandle(); + Texture_t *pTexture = &GetTexture( textureHandle ); + + pTexture->m_Flags = (Texture_t::IS_ALLOCATED | Texture_t::IS_RENDER_TARGET_SURFACE); + + pTexture->m_DebugName = pDebugName; + pTexture->m_Width = width; + pTexture->m_Height = height; + pTexture->m_Depth = 1; + pTexture->m_NumCopies = 1; + pTexture->m_CurrentCopy = 0; + + ImageFormat dstImageFormat = FindNearestSupportedFormat( format, false, true, false ); + D3DFORMAT actualFormat = ImageLoader::ImageFormatToD3DFormat( dstImageFormat ); + + pTexture->GetRenderTargetSurface( false ) = g_TextureHeap.AllocRenderTargetSurface( width, height, actualFormat ); + pTexture->GetRenderTargetSurface( true ) = g_TextureHeap.AllocRenderTargetSurface( width, height, (D3DFORMAT)MAKESRGBFMT( actualFormat ) ); + + pTexture->SetImageFormat( dstImageFormat ); + + pTexture->m_UTexWrap = D3DTADDRESS_CLAMP; + pTexture->m_VTexWrap = D3DTADDRESS_CLAMP; + pTexture->m_WTexWrap = D3DTADDRESS_CLAMP; + pTexture->m_MagFilter = D3DTEXF_LINEAR; + + pTexture->m_NumLevels = 1; + pTexture->m_MipFilter = D3DTEXF_NONE; + pTexture->m_MinFilter = D3DTEXF_LINEAR; + + pTexture->m_SwitchNeeded = false; + + ComputeStatsInfo( textureHandle, false, false ); + SetupTextureGroup( textureHandle, pTextureGroupName ); + + return textureHandle; +} +#endif + +//----------------------------------------------------------------------------- +// Shader constants are batched and written to gpu once prior to draw. +//----------------------------------------------------------------------------- +void CShaderAPIDx8::WriteShaderConstantsToGPU() +{ +#if defined( _X360 ) + // vector vertex constants can just blast their set range + if ( m_MaxVectorVertexShaderConstant ) + { + if ( m_bGPUOwned ) + { + // faster path, write directly into GPU command buffer, bypassing shadow state + // can only set what is actually owned + Assert( m_MaxVectorVertexShaderConstant <= VERTEX_SHADER_MODEL + 3*NUM_MODEL_TRANSFORMS ); + int numVectors = AlignValue( m_MaxVectorVertexShaderConstant, 4 ); + BYTE* pCommandBufferData; + Dx9Device()->GpuBeginVertexShaderConstantF4( 0, (D3DVECTOR4**)&pCommandBufferData, numVectors ); + memcpy( pCommandBufferData, m_DesiredState.m_pVectorVertexShaderConstant[0].Base(), numVectors * (sizeof( float ) * 4) ); + Dx9Device()->GpuEndVertexShaderConstantF4(); + } + else + { + Dx9Device()->SetVertexShaderConstantF( 0, m_DesiredState.m_pVectorVertexShaderConstant[0].Base(), m_MaxVectorVertexShaderConstant ); + } + + memcpy( m_DynamicState.m_pVectorVertexShaderConstant[0].Base(), m_DesiredState.m_pVectorVertexShaderConstant[0].Base(), m_MaxVectorVertexShaderConstant * 4 * sizeof(float) ); + m_MaxVectorVertexShaderConstant = 0; + } + + if ( m_MaxVectorPixelShaderConstant ) + { + if ( m_bGPUOwned ) + { + // faster path, write directly into GPU command buffer, bypassing shadow state + // can only set what is actually owned + Assert( m_MaxVectorPixelShaderConstant <= 32 ); + int numVectors = AlignValue( m_MaxVectorPixelShaderConstant, 4 ); + BYTE* pCommandBufferData; + Dx9Device()->GpuBeginPixelShaderConstantF4( 0, (D3DVECTOR4**)&pCommandBufferData, numVectors ); + memcpy( pCommandBufferData, m_DesiredState.m_pVectorPixelShaderConstant[0].Base(), numVectors * (sizeof( float ) * 4) ); + Dx9Device()->GpuEndPixelShaderConstantF4(); + } + else + { + Dx9Device()->SetPixelShaderConstantF( 0, m_DesiredState.m_pVectorPixelShaderConstant[0].Base(), m_MaxVectorPixelShaderConstant ); + } + + memcpy( m_DynamicState.m_pVectorPixelShaderConstant[0].Base(), m_DesiredState.m_pVectorPixelShaderConstant[0].Base(), m_MaxVectorPixelShaderConstant * 4 * sizeof(float) ); + m_MaxVectorPixelShaderConstant = 0; + } + + // boolean and integer constants can just blast their set range + // these are currently extremely small in number, if this changes they may benefit from a fast path pattern + if ( m_MaxBooleanVertexShaderConstant ) + { + Dx9Device()->SetVertexShaderConstantB( 0, m_DesiredState.m_pBooleanVertexShaderConstant, m_MaxBooleanVertexShaderConstant ); + memcpy( m_DynamicState.m_pBooleanVertexShaderConstant, m_DesiredState.m_pBooleanVertexShaderConstant, m_MaxBooleanVertexShaderConstant * sizeof(BOOL) ); + m_MaxBooleanVertexShaderConstant = 0; + } + if ( m_MaxIntegerVertexShaderConstant ) + { + Dx9Device()->SetVertexShaderConstantI( 0, (int *)m_DesiredState.m_pIntegerVertexShaderConstant, m_MaxIntegerVertexShaderConstant ); + memcpy( m_DynamicState.m_pIntegerVertexShaderConstant[0].Base(), m_DesiredState.m_pIntegerVertexShaderConstant[0].Base(), m_MaxIntegerVertexShaderConstant * sizeof(IntVector4D) ); + m_MaxIntegerVertexShaderConstant = 0; + } + + if ( m_MaxBooleanPixelShaderConstant ) + { + Dx9Device()->SetPixelShaderConstantB( 0, m_DesiredState.m_pBooleanPixelShaderConstant, m_MaxBooleanPixelShaderConstant ); + memcpy( m_DynamicState.m_pBooleanPixelShaderConstant, m_DesiredState.m_pBooleanPixelShaderConstant, m_MaxBooleanPixelShaderConstant * sizeof(BOOL) ); + m_MaxBooleanPixelShaderConstant = 0; + } + + // integer pixel constants are not used, so not supporting +#if 0 + if ( m_MaxIntegerPixelShaderConstant ) + { + Dx9Device()->SetPixelShaderConstantI( 0, (int *)m_DesiredState.m_pIntegerPixelShaderConstant, m_MaxIntegerPixelShaderConstant ); + memcpy( m_DynamicState.m_pIntegerPixelShaderConstant[0].Base(), m_DesiredState.m_pIntegerPixelShaderConstant[0].Base(), m_MaxIntegerPixelShaderConstant * sizeof(IntVector4D) ); + m_MaxIntegerPixelShaderConstant = 0; + } +#endif +#endif +} + +//----------------------------------------------------------------------------- +// The application is about to perform a hard reboot, but wants to hide the screen flash +// by persisting the front buffer across a reboot boundary. The persisted frame buffer +// can be detected and restored. +//----------------------------------------------------------------------------- +#if defined( _X360 ) +void CShaderAPIDx8::PersistDisplay() +{ + if ( m_PresentParameters.FrontBufferFormat != D3DFMT_LE_X8R8G8B8 ) + { + // The format must be what PersistDisplay() expects, otherwise D3DRIP. + // If this hits due to sRGB bit set that confuses PersistDisplay(), + // the fix may be to slam the presentation parameters to the expected format, + // do a ResetDevice(), and then PersistDisplay(). + Assert( 0 ); + return; + } + + IDirect3DTexture *pTexture; + HRESULT hr = Dx9Device()->GetFrontBuffer( &pTexture ); + if ( !FAILED( hr ) ) + { + OwnGPUResources( false ); + Dx9Device()->PersistDisplay( pTexture, NULL ); + pTexture->Release(); + } +} +#endif + +#if defined( _X360 ) +bool CShaderAPIDx8::PostQueuedTexture( const void *pData, int nDataSize, ShaderAPITextureHandle_t *pHandles, int numHandles, int nWidth, int nHeight, int nDepth, int numMips, int *pRefCount ) +{ + CUtlBuffer vtfBuffer; + IVTFTexture *pVTFTexture = NULL; + bool bOK = false; + + if ( !pData || !nDataSize ) + { + // invalid + goto cleanUp; + } + + // get a unique vtf and mount texture + // vtf can expect non-volatile buffer data to be stable through vtf lifetime + // this prevents redundant copious amounts of image memory transfers + pVTFTexture = CreateVTFTexture(); + vtfBuffer.SetExternalBuffer( (void *)pData, nDataSize, nDataSize ); + if ( !pVTFTexture->UnserializeFromBuffer( vtfBuffer, false, false, false, 0 ) ) + { + goto cleanUp; + } + + // provided vtf buffer is all mips, determine top mip due to possible picmip + int iTopMip = 0; + int mipWidth, mipHeight, mipDepth; + do + { + pVTFTexture->ComputeMipLevelDimensions( iTopMip, &mipWidth, &mipHeight, &mipDepth ); + if ( nWidth == mipWidth && nHeight == mipHeight && nDepth == mipDepth ) + { + break; + } + iTopMip++; + } + while ( mipWidth != 1 || mipHeight != 1 || mipDepth != 1 ); + + // create and blit + for ( int iFrame = 0; iFrame < numHandles; iFrame++ ) + { + ShaderAPITextureHandle_t hTexture = pHandles[iFrame]; + Texture_t *pTexture = &GetTexture( hTexture ); + + int nFaceCount = ( pTexture->m_CreationFlags & TEXTURE_CREATE_CUBEMAP ) ? CUBEMAP_FACE_COUNT-1 : 1; + + IDirect3DBaseTexture *pD3DTexture; + if ( pTexture->m_CreationFlags & TEXTURE_CREATE_NOD3DMEMORY ) + { + pD3DTexture = pTexture->GetTexture(); + if ( !g_TextureHeap.AllocD3DMemory( pD3DTexture ) ) + { + goto cleanUp; + } + } + else + { + pD3DTexture = pTexture->GetTexture(); + } + + // blit the hi-res texture bits into d3d memory + for ( int iFace = 0; iFace < nFaceCount; ++iFace ) + { + for ( int iMip = 0; iMip < numMips; ++iMip ) + { + pVTFTexture->ComputeMipLevelDimensions( iTopMip + iMip, &mipWidth, &mipHeight, &mipDepth ); + unsigned char *pSourceBits = pVTFTexture->ImageData( iFrame, iFace, iTopMip + iMip, 0, 0, 0 ); + + TextureLoadInfo_t info; + info.m_TextureHandle = hTexture; + info.m_pTexture = pD3DTexture; + info.m_nLevel = iMip; + info.m_nCopy = 0; + info.m_CubeFaceID = (D3DCUBEMAP_FACES)iFace; + info.m_nWidth = mipWidth; + info.m_nHeight = mipHeight; + info.m_nZOffset = 0; + info.m_SrcFormat = pVTFTexture->Format(); + info.m_pSrcData = pSourceBits; + info.m_bSrcIsTiled = pVTFTexture->IsPreTiled(); + info.m_bCanConvertFormat = ( pTexture->m_Flags & Texture_t::CAN_CONVERT_FORMAT ) != 0; + LoadTexture( info ); + } + } + + pTexture->m_Flags |= Texture_t::IS_FINALIZED; + (*pRefCount)--; + } + + // success + bOK = true; + +cleanUp: + if ( pVTFTexture ) + { + DestroyVTFTexture( pVTFTexture ); + } + + if ( !bOK ) + { + // undo artificial lock + (*pRefCount) -= numHandles; + } + + return bOK; +} +#endif + +#if defined( _X360 ) +void *CShaderAPIDx8::GetD3DDevice() +{ + return Dx9Device(); +} +#endif + +#if defined( _X360 ) +static void r_enable_gpr_allocations_callback( IConVar *var, const char *pOldValue, float flOldValue ) +{ + if ( ((ConVar *)var)->GetBool() == false ) + { + //reset back the default 64/64 allocation before we stop updating + if( Dx9Device() != NULL ) + { + Dx9Device()->SetShaderGPRAllocation( 0, 0, 0 ); + } + } +} + +ConVar r_enable_gpr_allocations( "r_enable_gpr_allocations", "1", 0, "Enable usage of IDirect3DDevice9::SetShaderGPRAllocation()", r_enable_gpr_allocations_callback ); + +static void CommitShaderGPRs( IDirect3DDevice9 *pDevice, const DynamicState_t &desiredState, DynamicState_t ¤tState, bool bForce ) +{ + if( desiredState.m_iVertexShaderGPRAllocation != currentState.m_iVertexShaderGPRAllocation ) + { + pDevice->SetShaderGPRAllocation( 0, desiredState.m_iVertexShaderGPRAllocation, 128 - desiredState.m_iVertexShaderGPRAllocation ); + currentState.m_iVertexShaderGPRAllocation = desiredState.m_iVertexShaderGPRAllocation; + } +} + +void CShaderAPIDx8::PushVertexShaderGPRAllocation( int iVertexShaderCount ) +{ + Assert( (iVertexShaderCount >= 16) && (iVertexShaderCount <= 112) ); + m_VertexShaderGPRAllocationStack.Push( iVertexShaderCount ); + + if ( r_enable_gpr_allocations.GetBool() ) + { + if ( m_DynamicState.m_iVertexShaderGPRAllocation != iVertexShaderCount ) + { + m_DesiredState.m_iVertexShaderGPRAllocation = iVertexShaderCount; + ADD_COMMIT_FUNC( COMMIT_PER_DRAW, COMMIT_ALWAYS, CommitShaderGPRs ); + } + } +} + +void CShaderAPIDx8::PopVertexShaderGPRAllocation( void ) +{ + m_VertexShaderGPRAllocationStack.Pop(); + + if ( r_enable_gpr_allocations.GetBool() ) + { + int iVertexShaderCount; + if ( m_VertexShaderGPRAllocationStack.Count() ) + iVertexShaderCount = m_VertexShaderGPRAllocationStack.Top(); + else + iVertexShaderCount = 64; + + if ( m_DynamicState.m_iVertexShaderGPRAllocation != iVertexShaderCount ) + { + m_DesiredState.m_iVertexShaderGPRAllocation = iVertexShaderCount; + ADD_COMMIT_FUNC( COMMIT_PER_DRAW, COMMIT_ALWAYS, CommitShaderGPRs ); + } + } +} + +void CShaderAPIDx8::EnableVSync_360( bool bEnable ) +{ + if( bEnable ) + { + Dx9Device()->SetRenderState( D3DRS_PRESENTIMMEDIATETHRESHOLD, 0 ); //only swap on vertical blanks + } + else + { + Dx9Device()->SetRenderState( D3DRS_PRESENTIMMEDIATETHRESHOLD, 100 ); //allow a swap at any point in the DAC scan + } +} +#endif + +// ------------ New Vertex/Index Buffer interface ---------------------------- + +void CShaderAPIDx8::BindVertexBuffer( int streamID, IVertexBuffer *pVertexBuffer, int nOffsetInBytes, int nFirstVertex, int nVertexCount, VertexFormat_t fmt, int nRepetitions ) +{ + LOCK_SHADERAPI(); + MeshMgr()->BindVertexBuffer( streamID, pVertexBuffer, nOffsetInBytes, nFirstVertex, nVertexCount, fmt, nRepetitions ); +} + +void CShaderAPIDx8::BindIndexBuffer( IIndexBuffer *pIndexBuffer, int nOffsetInBytes ) +{ + LOCK_SHADERAPI(); + MeshMgr()->BindIndexBuffer( pIndexBuffer, nOffsetInBytes ); +} + +void CShaderAPIDx8::Draw( MaterialPrimitiveType_t primitiveType, int nFirstIndex, int nIndexCount ) +{ + LOCK_SHADERAPI(); + MeshMgr()->Draw( primitiveType, nFirstIndex, nIndexCount ); +} + +// ------------ End ---------------------------- + +float CShaderAPIDx8::GammaToLinear_HardwareSpecific( float fGamma ) const +{ + if( IsPC() ) + { + return SrgbGammaToLinear( fGamma ); + } + else if( IsX360() ) + { + return SrgbGammaToLinear( fGamma ); + } + else + { + // Unknown console + return pow( fGamma, 2.2f ); // Use a gamma 2.2 curve + } +} + +float CShaderAPIDx8::LinearToGamma_HardwareSpecific( float fLinear ) const +{ + if ( IsPC() ) + { + return SrgbLinearToGamma( fLinear ); + } + else if ( IsX360() ) + { + return SrgbLinearToGamma( fLinear ); + } + else + { + // Unknown console + return pow( fLinear, ( 1.0f / 2.2f ) ); // Use a gamma 2.2 curve + } +} + + +bool CShaderAPIDx8::ShouldWriteDepthToDestAlpha( void ) const +{ + return IsPC() && g_pHardwareConfig->SupportsPixelShaders_2_b() && + (m_SceneFogMode != MATERIAL_FOG_LINEAR_BELOW_FOG_Z) && + (GetIntRenderingParameter(INT_RENDERPARM_WRITE_DEPTH_TO_DESTALPHA) != 0); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CShaderAPIDx8::AcquireThreadOwnership() +{ + SetCurrentThreadAsOwner(); +#if (defined( _X360 ) || defined( DX_TO_GL_ABSTRACTION )) + Dx9Device()->AcquireThreadOwnership(); +#endif +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CShaderAPIDx8::ReleaseThreadOwnership() +{ + RemoveThreadOwner(); +#if (defined( _X360 ) || defined( DX_TO_GL_ABSTRACTION )) + Dx9Device()->ReleaseThreadOwnership(); +#endif +} + + +//----------------------------------------------------------------------------- +// Actual low level setting of the color RT. All Xbox RT funnels here +// to track the actual RT state. Returns true if the RT gets set, otherwise false. +//----------------------------------------------------------------------------- +bool CShaderAPIDx8::SetRenderTargetInternalXbox( ShaderAPITextureHandle_t hRenderTargetTexture, bool bForce ) +{ + // valid for 360 only + if ( IsPC() ) + { + Assert( 0 ); + return false; + } + + if ( hRenderTargetTexture == INVALID_SHADERAPI_TEXTURE_HANDLE ) + { + // could be a reset, force to back buffer + hRenderTargetTexture = SHADER_RENDERTARGET_BACKBUFFER; + } + + if ( m_hCachedRenderTarget == INVALID_SHADERAPI_TEXTURE_HANDLE ) + { + // let the set go through to establish the initial state + bForce = true; + } + + if ( !bForce && ( hRenderTargetTexture == m_hCachedRenderTarget && m_DynamicState.m_bSRGBWritesEnabled == m_bUsingSRGBRenderTarget ) ) + { + // current RT matches expected state, leave state intact + return false; + } + + // track the updated state + m_bUsingSRGBRenderTarget = m_DynamicState.m_bSRGBWritesEnabled; + m_hCachedRenderTarget = hRenderTargetTexture; + + IDirect3DSurface *pSurface; + if ( m_hCachedRenderTarget == SHADER_RENDERTARGET_BACKBUFFER ) + { + if ( !m_bUsingSRGBRenderTarget ) + { + pSurface = m_pBackBufferSurface; + } + else + { + pSurface = m_pBackBufferSurfaceSRGB; + } + } + else + { + AssertValidTextureHandle( m_hCachedRenderTarget ); + Texture_t *pTexture = &GetTexture( m_hCachedRenderTarget ); + pSurface = pTexture->GetRenderTargetSurface( m_bUsingSRGBRenderTarget ); + } + + // the 360 does a wierd reset of some states on a SetRenderTarget() + // the viewport is a clobbered state, it may not be changed by later callers, so it MUST be put back as expected + // the other clobbered states are waiting to be discovered ... sigh +#if defined( _X360 ) + D3DVIEWPORT9 viewport; + Dx9Device()->GetViewport( &viewport ); + Dx9Device()->SetRenderTarget( 0, pSurface ); + Dx9Device()->SetViewport( &viewport ); +#endif + + return true; +} + + +//----------------------------------------------------------------------------- +// debug logging +//----------------------------------------------------------------------------- +void CShaderAPIDx8::PrintfVA( char *fmt, va_list vargs ) +{ +#ifdef DX_TO_GL_ABSTRACTION + #if GLMDEBUG + GLMPrintfVA( fmt, vargs ); + #endif +#else + AssertOnce( !"Impl me" ); +#endif +} + +void CShaderAPIDx8::Printf( const char *fmt, ... ) +{ +#ifdef DX_TO_GL_ABSTRACTION + #if GLMDEBUG + va_list vargs; + + va_start(vargs, fmt); + + GLMPrintfVA( fmt, vargs ); + + va_end( vargs ); + #endif +#else + AssertOnce( !"Impl me" ); +#endif +} + +float CShaderAPIDx8::Knob( char *knobname, float *setvalue ) +{ +#ifdef DX_TO_GL_ABSTRACTION + #if GLMDEBUG + return GLMKnob( knobname, setvalue ); + #else + return 0.0f; + #endif +#else + return 0.0f; +#endif +} + + + +#if defined( _X360 ) + +extern ConVar r_blocking_spew_threshold; +void D3DBlockingSpewCallback( DWORD Flags, D3DBLOCKTYPE BlockType, float ClockTime, DWORD ThreadTime ) +{ + if( ClockTime >= r_blocking_spew_threshold.GetFloat() ) + { + const char *pBlockType = ""; + switch( BlockType ) + { + case D3DBLOCKTYPE_NONE: + pBlockType = "D3DBLOCKTYPE_NONE"; + break; + case D3DBLOCKTYPE_PRIMARY_OVERRUN: + pBlockType = "D3DBLOCKTYPE_PRIMARY_OVERRUN"; + break; + case D3DBLOCKTYPE_SECONDARY_OVERRUN: + pBlockType = "D3DBLOCKTYPE_SECONDARY_OVERRUN"; + break; + case D3DBLOCKTYPE_SWAP_THROTTLE: + pBlockType = "D3DBLOCKTYPE_SWAP_THROTTLE"; + break; + case D3DBLOCKTYPE_BLOCK_UNTIL_IDLE: + pBlockType = "D3DBLOCKTYPE_BLOCK_UNTIL_IDLE"; + break; + case D3DBLOCKTYPE_BLOCK_UNTIL_NOT_BUSY: + pBlockType = "D3DBLOCKTYPE_BLOCK_UNTIL_NOT_BUSY"; + break; + case D3DBLOCKTYPE_BLOCK_ON_FENCE: + pBlockType = "D3DBLOCKTYPE_BLOCK_ON_FENCE"; + break; + case D3DBLOCKTYPE_VERTEX_SHADER_RELEASE: + pBlockType = "D3DBLOCKTYPE_VERTEX_SHADER_RELEASE"; + break; + case D3DBLOCKTYPE_PIXEL_SHADER_RELEASE: + pBlockType = "D3DBLOCKTYPE_PIXEL_SHADER_RELEASE"; + break; + case D3DBLOCKTYPE_VERTEX_BUFFER_RELEASE: + pBlockType = "D3DBLOCKTYPE_VERTEX_BUFFER_RELEASE"; + break; + case D3DBLOCKTYPE_VERTEX_BUFFER_LOCK: + pBlockType = "D3DBLOCKTYPE_VERTEX_BUFFER_LOCK"; + break; + case D3DBLOCKTYPE_INDEX_BUFFER_RELEASE: + pBlockType = "D3DBLOCKTYPE_INDEX_BUFFER_RELEASE"; + break; + case D3DBLOCKTYPE_INDEX_BUFFER_LOCK: + pBlockType = "D3DBLOCKTYPE_INDEX_BUFFER_LOCK"; + break; + case D3DBLOCKTYPE_TEXTURE_RELEASE: + pBlockType = "D3DBLOCKTYPE_TEXTURE_RELEASE"; + break; + case D3DBLOCKTYPE_TEXTURE_LOCK: + pBlockType = "D3DBLOCKTYPE_TEXTURE_LOCK"; + break; + case D3DBLOCKTYPE_COMMAND_BUFFER_RELEASE: + pBlockType = "D3DBLOCKTYPE_COMMAND_BUFFER_RELEASE"; + break; + case D3DBLOCKTYPE_COMMAND_BUFFER_LOCK: + pBlockType = "D3DBLOCKTYPE_COMMAND_BUFFER_LOCK"; + break; + case D3DBLOCKTYPE_CONSTANT_BUFFER_RELEASE: + pBlockType = "D3DBLOCKTYPE_CONSTANT_BUFFER_RELEASE"; + break; + case D3DBLOCKTYPE_CONSTANT_BUFFER_LOCK: + pBlockType = "D3DBLOCKTYPE_CONSTANT_BUFFER_LOCK"; + break; + + NO_DEFAULT; + }; + + Warning( "D3D Block: %s for %.2f ms\n", pBlockType, ClockTime ); + } +} + +static void r_blocking_spew_threshold_callback( IConVar *var, const char *pOldValue, float flOldValue ) +{ + if( Dx9Device() != NULL ) + { + if ( ((ConVar *)var)->GetFloat() >= 0.0f ) + { + Dx9Device()->SetBlockCallback( 0, D3DBlockingSpewCallback ); + } + else + { + Dx9Device()->SetBlockCallback( 0, NULL ); + } + } +} + +ConVar r_blocking_spew_threshold( "r_blocking_spew_threshold", "-1", 0, "Enable spew of Direct3D Blocks. Specify the minimum blocking time in milliseconds before spewing a warning.", r_blocking_spew_threshold_callback ); +#endif + diff --git a/materialsystem/shaderapidx9/shaderapidx8.h b/materialsystem/shaderapidx9/shaderapidx8.h new file mode 100644 index 0000000..fae297b --- /dev/null +++ b/materialsystem/shaderapidx9/shaderapidx8.h @@ -0,0 +1,118 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//===========================================================================// + +#ifndef SHADERAPIDX8_H +#define SHADERAPIDX8_H + +#include "shaderapibase.h" +#include "shaderapi/ishadershadow.h" +#include "locald3dtypes.h" + +//----------------------------------------------------------------------------- +// Vendor-specific defines +//----------------------------------------------------------------------------- +#define ATI_FETCH4_ENABLE MAKEFOURCC('G','E','T','4') +#define ATI_FETCH4_DISABLE MAKEFOURCC('G','E','T','1') +#define ATISAMP_FETCH4 D3DSAMP_MIPMAPLODBIAS + +//----------------------------------------------------------------------------- +// Forward declarations +//----------------------------------------------------------------------------- +class CMeshBase; +class CMeshBuilder; +struct ShadowState_t; +class IMaterialInternal; + + +//----------------------------------------------------------------------------- +// State that matters to buffered meshes... (for debugging only) +//----------------------------------------------------------------------------- +struct BufferedState_t +{ + D3DXMATRIX m_Transform[3]; + D3DVIEWPORT9 m_Viewport; + int m_BoundTexture[16]; + void *m_VertexShader; + void *m_PixelShader; +}; + + +//----------------------------------------------------------------------------- +// The DX8 shader API +//----------------------------------------------------------------------------- +// FIXME: Remove this! Either move them into CShaderAPIBase or CShaderAPIDx8 +class IShaderAPIDX8 : public CShaderAPIBase +{ +public: + // Draws the mesh + virtual void DrawMesh( CMeshBase *pMesh ) = 0; + + // Draw the mesh with the currently bound vertex and index buffers. + virtual void DrawWithVertexAndIndexBuffers( void ) = 0; + + // modifies the vertex data when necessary + virtual void ModifyVertexData( ) = 0; + + // Gets the current buffered state... (debug only) + virtual void GetBufferedState( BufferedState_t &state ) = 0; + + // Gets the current backface cull state.... + virtual D3DCULL GetCullMode() const = 0; + + // Measures fill rate + virtual void ComputeFillRate() = 0; + + // Selection mode methods + virtual bool IsInSelectionMode() const = 0; + + // We hit somefin in selection mode + virtual void RegisterSelectionHit( float minz, float maxz ) = 0; + + // Get the currently bound material + virtual IMaterialInternal* GetBoundMaterial() = 0; + + // These methods are called by the transition table + // They depend on dynamic state so can't exist inside the transition table + virtual void ApplyZBias( const ShadowState_t &shaderState ) = 0; + virtual void ApplyTextureEnable( const ShadowState_t &state, int stage ) = 0; + virtual void ApplyCullEnable( bool bEnable ) = 0; + virtual void SetVertexBlendState( int numBones ) = 0; + virtual void ApplyFogMode( ShaderFogMode_t fogMode, bool bSRGBWritesEnabled, bool bDisableGammaCorrection ) = 0; + + virtual int GetActualTextureStageCount() const = 0; + virtual int GetActualSamplerCount() const = 0; + + virtual bool IsRenderingMesh() const = 0; + + // Fog methods... + virtual void FogMode( MaterialFogMode_t fogMode ) = 0; + + virtual int GetCurrentFrameCounter( void ) const = 0; + + // Workaround hack for visualization of selection mode + virtual void SetupSelectionModeVisualizationState() = 0; + + virtual bool UsingSoftwareVertexProcessing() const = 0; + + //notification that the SRGB write state is being changed + virtual void EnabledSRGBWrite( bool bEnabled ) = 0; + + // Alpha to coverage + virtual void ApplyAlphaToCoverage( bool bEnable ) = 0; + +#if defined( _X360 ) + virtual void ApplySRGBReadState( int iTextureStage, bool bSRGBReadEnabled ) = 0; //360 needs to rebind the texture over again instead of setting a sampler state +#endif + + virtual void PrintfVA( char *fmt, va_list vargs ) = 0; + virtual void Printf( PRINTF_FORMAT_STRING const char *fmt, ... ) = 0; + virtual float Knob( char *knobname, float *setvalue = NULL ) = 0; +}; + + +#endif // SHADERAPIDX8_H diff --git a/materialsystem/shaderapidx9/shaderapidx8_global.h b/materialsystem/shaderapidx9/shaderapidx8_global.h new file mode 100644 index 0000000..2dab1e3 --- /dev/null +++ b/materialsystem/shaderapidx9/shaderapidx8_global.h @@ -0,0 +1,100 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//===========================================================================// + +#ifndef SHADERAPIDX8_GLOBAL_H +#define SHADERAPIDX8_GLOBAL_H + +#ifdef _WIN32 +#pragma once +#endif + +#include "tier0/dbg.h" +#include "tier0/memalloc.h" +#include "shaderapi_global.h" +#include "tier2/tier2.h" +#include "shaderdevicedx8.h" + + +//----------------------------------------------------------------------------- +// Use this to fill in structures with the current board state +//----------------------------------------------------------------------------- + +#ifdef _DEBUG +#define DEBUG_BOARD_STATE 0 +#endif + +#if !defined( _X360 ) +IDirect3DDevice9 *Dx9Device(); +IDirect3D9 *D3D(); +#endif + + +//----------------------------------------------------------------------------- +// Measures driver allocations +//----------------------------------------------------------------------------- +//#define MEASURE_DRIVER_ALLOCATIONS 1 + + +//----------------------------------------------------------------------------- +// Forward declarations +//----------------------------------------------------------------------------- +class IShaderUtil; +class IVertexBufferDX8; +class IShaderShadowDX8; +class IMeshMgr; +class IShaderAPIDX8; +class IFileSystem; +class IShaderManager; + + +//----------------------------------------------------------------------------- +// A new shader draw flag we need to workaround dx8 +//----------------------------------------------------------------------------- +enum +{ + SHADER_HAS_CONSTANT_COLOR = 0x80000000 +}; + +//----------------------------------------------------------------------------- +// The main shader API +//----------------------------------------------------------------------------- +extern IShaderAPIDX8 *g_pShaderAPIDX8; +inline IShaderAPIDX8* ShaderAPI() +{ + return g_pShaderAPIDX8; +} + +//----------------------------------------------------------------------------- +// The shader shadow +//----------------------------------------------------------------------------- +IShaderShadowDX8* ShaderShadow(); + +//----------------------------------------------------------------------------- +// Manager of all vertex + pixel shaders +//----------------------------------------------------------------------------- +inline IShaderManager *ShaderManager() +{ + extern IShaderManager *g_pShaderManager; + return g_pShaderManager; +} + +//----------------------------------------------------------------------------- +// The mesh manager +//----------------------------------------------------------------------------- +IMeshMgr* MeshMgr(); + +//----------------------------------------------------------------------------- +// The main hardware config interface +//----------------------------------------------------------------------------- +inline IMaterialSystemHardwareConfig* HardwareConfig() +{ + return g_pMaterialSystemHardwareConfig; +} + + +#endif // SHADERAPIDX8_GLOBAL_H diff --git a/materialsystem/shaderapidx9/shaderapidx9.vpc b/materialsystem/shaderapidx9/shaderapidx9.vpc new file mode 100644 index 0000000..efb8191 --- /dev/null +++ b/materialsystem/shaderapidx9/shaderapidx9.vpc @@ -0,0 +1,146 @@ +//----------------------------------------------------------------------------- +// SHADERDX8.VPC +// +// Project Script +//----------------------------------------------------------------------------- + +$Macro SRCDIR "..\.." +$Macro OUTBINDIR "$SRCDIR\..\game\bin" +$Macro OUTBINNAME "shaderapidx9" + +$include "$SRCDIR\vpc_scripts\source_dll_base.vpc" + + +// Common Configuration +$Configuration +{ + $Compiler + { + $AdditionalIncludeDirectories "$BASE;$SRCDIR\dx9sdk\include" [$WIN32 && !$GL] + $AdditionalIncludeDirectories "$BASE;$SRCDIR\x360xdk\include\xbox" [$X360] + $AdditionalIncludeDirectories "$BASE;..\" + $PreprocessorDefinitions "$BASE;SHADERAPIDX9;SHADER_DLL_EXPORT;PROTECTED_THINGS_ENABLE;strncpy=use_Q_strncpy_instead;_snprintf=use_Q_snprintf_instead" + $PreprocessorDefinitions "$BASE;USE_ACTUAL_DX" [($WIN32||$X360) && !$GL] + $PreprocessorDefinitions "$BASE;GL_GLEXT_PROTOTYPES;DX_TO_GL_ABSTRACTION" [$GL] +// $AdditionalOptions "/FC" + } + + $Linker + { + $SystemLibraries "iconv" [$OSXALL] + $GCC_ExtraLinkerFlags "-L/usr/lib32" [$LINUXALL] + $SystemFrameworks "Carbon;OpenGL;Quartz;Cocoa;IOKit" [$OSXALL] + $AdditionalDependencies "$BASE ws2_32.lib" [$WIN32] + } +} + +$Configuration "Debug" +{ + $Linker [$X360] + { + $AdditionalDependencies "$BASE d3dx9d.lib xuirund.lib xuirenderd.lib xaudiod2.lib xmcored.lib" + } +} + +$Configuration "Release" +{ + $Linker [$X360] + { + $AdditionalDependencies "$BASE d3dx9.lib xuirun.lib xuirender.lib xaudio2.lib xmcore.lib" + } +} + +$Project "shaderapidx9" +{ + $Folder "Source Files" + { + $File "colorformatdx8.cpp" + $File "$SRCDIR\public\filesystem_helpers.cpp" + $File "hardwareconfig.cpp" + $File "meshbase.cpp" + $File "meshdx8.cpp" + $File "recording.cpp" [$WIN32 && !$GL] + $File "shaderapidx8.cpp" + $File "shaderdevicebase.cpp" + $File "shaderapibase.cpp" + $File "shaderdevicedx8.cpp" + $File "shadershadowdx8.cpp" + $File "texturedx8.cpp" + $File "TransitionTable.cpp" + $File "cvballoctracker.cpp" + $File "vertexdecl.cpp" + $File "vertexshaderdx8.cpp" + $File "wmi.cpp" [$WIN32 && !$GL] + $File "textureheap.cpp" [$X360] + $File "winutils.cpp" [!$WIN32] + } + + $Folder "DirectX Header Files" [$WIN32 && !$GL] + { + $File "$SRCDIR\dx9sdk\include\d3dx9.h" + $File "$SRCDIR\dx9sdk\include\d3dx9anim.h" + $File "$SRCDIR\dx9sdk\include\d3dx9core.h" + $File "$SRCDIR\dx9sdk\include\d3dx9effect.h" + $File "$SRCDIR\dx9sdk\include\d3dx9math.h" + $File "$SRCDIR\dx9sdk\include\d3dx9math.inl" + $File "$SRCDIR\dx9sdk\include\d3dx9mesh.h" + $File "$SRCDIR\dx9sdk\include\d3dx9shader.h" + $File "$SRCDIR\dx9sdk\include\d3dx9shape.h" + $File "$SRCDIR\dx9sdk\include\d3dx9tex.h" + } + + $Folder "Public Header Files" + { + $File "$SRCDIR\public\shaderapi\IShaderDevice.h" + $File "$SRCDIR\public\shaderapi\ishaderutil.h" + $File "$SRCDIR\public\shaderapi\ishaderapi.h" + $File "$SRCDIR\public\shaderapi\ishaderdynamic.h" + $File "$SRCDIR\public\shaderapi\ishadershadow.h" + $File "$SRCDIR\public\materialsystem\idebugtextureinfo.h" + $File "$SRCDIR\public\materialsystem\ivballoctracker.h" + $File "$SRCDIR\public\materialsystem\shader_vcs_version.h" + } + + $Folder "Header Files" + { + $File "TransitionTable.h" + $File "vertexdecl.h" + $File "colorformatdx8.h" + $File "dynamicib.h" + $File "dynamicvb.h" + $File "hardwareconfig.h" + $File "meshbase.h" + $File "imeshdx8.h" + $File "locald3dtypes.h" + $File "recording.h" + $File "shaderapidx8.h" + $File "shaderdevicebase.h" + $File "shaderapibase.h" + $File "shaderdevicedx8.h" + $File "shaderapidx8_global.h" + $File "shadershadowdx8.h" + $File "stubd3ddevice.h" + $File "texturedx8.h" + $File "vertexshaderdx8.h" + $File "wmi.h" [$WIN32 && !$GL] + $File "textureheap.h" [$X360] + $File "winutils.h" [!$WIN32] + } + + $Folder "Link Libraries" [$WIN32] + { + + $File "$SRCDIR\dx9sdk\lib\d3d9.lib" [$WIN32 && !$GL] + $File "$SRCDIR\dx9sdk\lib\d3dx9.lib" [$WIN32 && !$GL&&!$VS2015] + $File "$LIBCOMMON\d3dx9.lib" [$WIN32 && !$GL &&$VS2015] + } + + $Folder "Link Libraries" + { + $Lib bitmap + $Lib mathlib + $Lib tier2 + //$Lib "$LIBCOMMON/bzip2" + $ImpLib togl [!$IS_LIB_PROJECT && $GL] + } +} \ No newline at end of file diff --git a/materialsystem/shaderapidx9/shaderdevicebase.cpp b/materialsystem/shaderapidx9/shaderdevicebase.cpp new file mode 100644 index 0000000..aef5996 --- /dev/null +++ b/materialsystem/shaderapidx9/shaderdevicebase.cpp @@ -0,0 +1,1256 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// +#define DISABLE_PROTECTED_THINGS +#include "togl/rendermechanism.h" +#include "shaderdevicebase.h" +#include "tier1/KeyValues.h" +#include "tier1/convar.h" +#include "tier1/utlbuffer.h" +#include "tier0/icommandline.h" +#include "tier2/tier2.h" +#include "filesystem.h" +#include "datacache/idatacache.h" +#include "shaderapi/ishaderutil.h" +#include "shaderapibase.h" +#include "shaderapi/ishadershadow.h" +#include "shaderapi_global.h" +#include "winutils.h" + +#ifdef _X360 +#include "xbox/xbox_win32stubs.h" +#endif + +//----------------------------------------------------------------------------- +// Globals +//----------------------------------------------------------------------------- +IShaderUtil* g_pShaderUtil; // The main shader utility interface +CShaderDeviceBase *g_pShaderDevice; +CShaderDeviceMgrBase *g_pShaderDeviceMgr; +CShaderAPIBase *g_pShaderAPI; +IShaderShadow *g_pShaderShadow; + +bool g_bUseShaderMutex = false; // Shader mutex globals +bool g_bShaderAccessDisallowed; +CShaderMutex g_ShaderMutex; + +//----------------------------------------------------------------------------- +// FIXME: Hack related to setting command-line values for convars. Remove!!! +//----------------------------------------------------------------------------- +class CShaderAPIConVarAccessor : public IConCommandBaseAccessor +{ +public: + virtual bool RegisterConCommandBase( ConCommandBase *pCommand ) + { + // Link to engine's list instead + g_pCVar->RegisterConCommand( pCommand ); + + char const *pValue = g_pCVar->GetCommandLineValue( pCommand->GetName() ); + if( pValue && !pCommand->IsCommand() ) + { + ( ( ConVar * )pCommand )->SetValue( pValue ); + } + return true; + } +}; + +static void InitShaderAPICVars( ) +{ + static CShaderAPIConVarAccessor g_ConVarAccessor; + if ( g_pCVar ) + { + ConVar_Register( FCVAR_MATERIAL_SYSTEM_THREAD, &g_ConVarAccessor ); + } +} + + + +//----------------------------------------------------------------------------- +// Read dx support levels +//----------------------------------------------------------------------------- +#if defined( DX_TO_GL_ABSTRACTION ) + #if defined( OSX ) + // OSX + #define SUPPORT_CFG_FILE "dxsupport_mac.cfg" + // TODO: make this different for Mac? + #define SUPPORT_CFG_OVERRIDE_FILE "dxsupport_override.cfg" + #else + // Linux/Win GL + #define SUPPORT_CFG_FILE "dxsupport_linux.cfg" + // TODO: make this different for Linux? + #define SUPPORT_CFG_OVERRIDE_FILE "dxsupport_override.cfg" + #endif +#else + // D3D + #define SUPPORT_CFG_FILE "dxsupport.cfg" + #define SUPPORT_CFG_OVERRIDE_FILE "dxsupport_override.cfg" +#endif + + +//----------------------------------------------------------------------------- +// constructor, destructor +//----------------------------------------------------------------------------- +CShaderDeviceMgrBase::CShaderDeviceMgrBase() +{ + m_pDXSupport = NULL; +} + +CShaderDeviceMgrBase::~CShaderDeviceMgrBase() +{ +} + + +//----------------------------------------------------------------------------- +// Factory used to get at internal interfaces (used by shaderapi + shader dlls) +//----------------------------------------------------------------------------- +static CreateInterfaceFn s_TempFactory; +void *ShaderDeviceFactory( const char *pName, int *pReturnCode ) +{ + if (pReturnCode) + { + *pReturnCode = IFACE_OK; + } + + void *pInterface = s_TempFactory( pName, pReturnCode ); + if ( pInterface ) + return pInterface; + + pInterface = Sys_GetFactoryThis()( pName, pReturnCode ); + if ( pInterface ) + return pInterface; + + if ( pReturnCode ) + { + *pReturnCode = IFACE_FAILED; + } + return NULL; +} + +//----------------------------------------------------------------------------- +// Connect, disconnect +//----------------------------------------------------------------------------- +bool CShaderDeviceMgrBase::Connect( CreateInterfaceFn factory ) +{ + LOCK_SHADERAPI(); + + Assert( !g_pShaderDeviceMgr ); + + s_TempFactory = factory; + + // Connection/convar registration + CreateInterfaceFn actualFactory = ShaderDeviceFactory; + ConnectTier1Libraries( &actualFactory, 1 ); + InitShaderAPICVars(); + ConnectTier2Libraries( &actualFactory, 1 ); + g_pShaderUtil = (IShaderUtil*)ShaderDeviceFactory( SHADER_UTIL_INTERFACE_VERSION, NULL ); + g_pShaderDeviceMgr = this; + + s_TempFactory = NULL; + + if ( !g_pShaderUtil || !g_pFullFileSystem || !g_pShaderDeviceMgr ) + { + Warning( "ShaderAPIDx10 was unable to access the required interfaces!\n" ); + return false; + } + + // 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 ); + return true; +} + +void CShaderDeviceMgrBase::Disconnect() +{ + LOCK_SHADERAPI(); + + g_pShaderDeviceMgr = NULL; + g_pShaderUtil = NULL; + DisconnectTier2Libraries(); + ConVar_Unregister(); + DisconnectTier1Libraries(); + + if ( m_pDXSupport ) + { + m_pDXSupport->deleteThis(); + m_pDXSupport = NULL; + } +} + + +//----------------------------------------------------------------------------- +// Query interface +//----------------------------------------------------------------------------- +void *CShaderDeviceMgrBase::QueryInterface( const char *pInterfaceName ) +{ + if ( !Q_stricmp( pInterfaceName, SHADER_DEVICE_MGR_INTERFACE_VERSION ) ) + return ( IShaderDeviceMgr* )this; + if ( !Q_stricmp( pInterfaceName, MATERIALSYSTEM_HARDWARECONFIG_INTERFACE_VERSION ) ) + return ( IMaterialSystemHardwareConfig* )g_pHardwareConfig; + return NULL; +} + + +//----------------------------------------------------------------------------- +// Returns the hardware caps for a particular adapter +//----------------------------------------------------------------------------- +const HardwareCaps_t& CShaderDeviceMgrBase::GetHardwareCaps( int nAdapter ) const +{ + Assert( ( nAdapter >= 0 ) && ( nAdapter < GetAdapterCount() ) ); + return m_Adapters[nAdapter].m_ActualCaps; +} + + +//----------------------------------------------------------------------------- +// Utility methods for reading config scripts +//----------------------------------------------------------------------------- +static inline int ReadHexValue( KeyValues *pVal, const char *pName ) +{ + const char *pString = pVal->GetString( pName, NULL ); + if (!pString) + { + return -1; + } + + char *pTemp; + int nVal = strtol( pString, &pTemp, 16 ); + return (pTemp != pString) ? nVal : -1; +} + +static bool ReadBool( KeyValues *pGroup, const char *pKeyName, bool bDefault ) +{ + int nVal = pGroup->GetInt( pKeyName, -1 ); + if ( nVal != -1 ) + { + // Warning( "\t%s = %s\n", pKeyName, (nVal != false) ? "true" : "false" ); + return (nVal != false); + } + return bDefault; +} + +static void ReadInt( KeyValues *pGroup, const char *pKeyName, int nInvalidValue, int *pResult ) +{ + int nVal = pGroup->GetInt( pKeyName, nInvalidValue ); + if ( nVal != nInvalidValue ) + { + *pResult = nVal; + // Warning( "\t%s = %d\n", pKeyName, *pResult ); + } +} + + +//----------------------------------------------------------------------------- +// Utility method to copy over a keyvalue +//----------------------------------------------------------------------------- +static void AddKey( KeyValues *pDest, KeyValues *pSrc ) +{ + // Note this will replace already-existing values + switch( pSrc->GetDataType() ) + { + case KeyValues::TYPE_NONE: + break; + case KeyValues::TYPE_STRING: + pDest->SetString( pSrc->GetName(), pSrc->GetString() ); + break; + case KeyValues::TYPE_INT: + pDest->SetInt( pSrc->GetName(), pSrc->GetInt() ); + break; + case KeyValues::TYPE_FLOAT: + pDest->SetFloat( pSrc->GetName(), pSrc->GetFloat() ); + break; + case KeyValues::TYPE_PTR: + pDest->SetPtr( pSrc->GetName(), pSrc->GetPtr() ); + break; + case KeyValues::TYPE_WSTRING: + pDest->SetWString( pSrc->GetName(), pSrc->GetWString() ); + break; + case KeyValues::TYPE_COLOR: + pDest->SetColor( pSrc->GetName(), pSrc->GetColor() ); + break; + default: + Assert( 0 ); + break; + } +} + +//----------------------------------------------------------------------------- +// Finds if we have a dxlevel-specific config in the support keyvalues +//----------------------------------------------------------------------------- +KeyValues *CShaderDeviceMgrBase::FindDXLevelSpecificConfig( KeyValues *pKeyValues, int nDxLevel ) +{ + KeyValues *pGroup = pKeyValues->GetFirstSubKey(); + for( pGroup = pKeyValues->GetFirstSubKey(); pGroup; pGroup = pGroup->GetNextKey() ) + { + int nFoundDxLevel = pGroup->GetInt( "name", 0 ); + if( nFoundDxLevel == nDxLevel ) + return pGroup; + } + + return NULL; +} + +//----------------------------------------------------------------------------- +// Finds if we have a dxlevel and vendor-specific config in the support keyvalues +//----------------------------------------------------------------------------- +KeyValues *CShaderDeviceMgrBase::FindDXLevelAndVendorSpecificConfig( KeyValues *pKeyValues, int nDxLevel, int nVendorID ) +{ + if ( IsX360() ) + { + // 360 unique dxlevel implies hw config, vendor variance not applicable + return NULL; + } + + KeyValues *pGroup = pKeyValues->GetFirstSubKey(); + for( pGroup = pKeyValues->GetFirstSubKey(); pGroup; pGroup = pGroup->GetNextKey() ) + { + int nFoundDxLevel = pGroup->GetInt( "name", 0 ); + int nFoundVendorID = ReadHexValue( pGroup, "VendorID" ); + if( nFoundDxLevel == nDxLevel && nFoundVendorID == nVendorID ) + return pGroup; + } + + return NULL; +} + +//----------------------------------------------------------------------------- +// Finds if we have a vendor-specific config in the support keyvalues +//----------------------------------------------------------------------------- +KeyValues *CShaderDeviceMgrBase::FindCPUSpecificConfig( KeyValues *pKeyValues, int nCPUMhz, bool bAMD ) +{ + if ( IsX360() ) + { + // 360 unique dxlevel implies hw config, cpu variance not applicable + return NULL; + } + + for( KeyValues *pGroup = pKeyValues->GetFirstSubKey(); pGroup; pGroup = pGroup->GetNextKey() ) + { + const char *pName = pGroup->GetString( "name", NULL ); + if ( !pName ) + continue; + + if ( ( bAMD && Q_stristr( pName, "AMD" ) ) || + ( !bAMD && Q_stristr( pName, "Intel" ) ) ) + { + int nMinMegahertz = pGroup->GetInt( "min megahertz", -1 ); + int nMaxMegahertz = pGroup->GetInt( "max megahertz", -1 ); + if( nMinMegahertz == -1 || nMaxMegahertz == -1 ) + continue; + + if( nMinMegahertz <= nCPUMhz && nCPUMhz < nMaxMegahertz ) + return pGroup; + } + } + return NULL; +} + + +//----------------------------------------------------------------------------- +// Finds if we have a vendor-specific config in the support keyvalues +//----------------------------------------------------------------------------- +KeyValues *CShaderDeviceMgrBase::FindCardSpecificConfig( KeyValues *pKeyValues, int nVendorId, int nDeviceId ) +{ + if ( IsX360() ) + { + // 360 unique dxlevel implies hw config, vendor variance not applicable + return NULL; + } + + KeyValues *pGroup = pKeyValues->GetFirstSubKey(); + for( pGroup = pKeyValues->GetFirstSubKey(); pGroup; pGroup = pGroup->GetNextKey() ) + { + int nFoundVendorId = ReadHexValue( pGroup, "VendorID" ); + int nFoundDeviceIdMin = ReadHexValue( pGroup, "MinDeviceID" ); + int nFoundDeviceIdMax = ReadHexValue( pGroup, "MaxDeviceID" ); + if ( nFoundVendorId == nVendorId && nDeviceId >= nFoundDeviceIdMin && nDeviceId <= nFoundDeviceIdMax ) + return pGroup; + } + + return NULL; +} + + +//----------------------------------------------------------------------------- +// Finds if we have a vendor-specific config in the support keyvalues +//----------------------------------------------------------------------------- +KeyValues *CShaderDeviceMgrBase::FindMemorySpecificConfig( KeyValues *pKeyValues, int nSystemRamMB ) +{ + if ( IsX360() ) + { + // 360 unique dxlevel implies hw config, memory variance not applicable + return NULL; + } + + for( KeyValues *pGroup = pKeyValues->GetFirstSubKey(); pGroup; pGroup = pGroup->GetNextKey() ) + { + // Used to help us debug this code +// const char *pDebugName = pGroup->GetString( "name", "blah" ); + + int nMinMB = pGroup->GetInt( "min megabytes", -1 ); + int nMaxMB = pGroup->GetInt( "max megabytes", -1 ); + if ( nMinMB == -1 || nMaxMB == -1 ) + continue; + + if ( nMinMB <= nSystemRamMB && nSystemRamMB < nMaxMB ) + return pGroup; + } + return NULL; +} + + +//----------------------------------------------------------------------------- +// Finds if we have a texture mem size specific config +//----------------------------------------------------------------------------- +KeyValues *CShaderDeviceMgrBase::FindVidMemSpecificConfig( KeyValues *pKeyValues, int nVideoRamMB ) +{ + if ( IsX360() ) + { + // 360 unique dxlevel implies hw config, vidmem variance not applicable + return NULL; + } + + for( KeyValues *pGroup = pKeyValues->GetFirstSubKey(); pGroup; pGroup = pGroup->GetNextKey() ) + { + int nMinMB = pGroup->GetInt( "min megatexels", -1 ); + int nMaxMB = pGroup->GetInt( "max megatexels", -1 ); + if ( nMinMB == -1 || nMaxMB == -1 ) + continue; + + if ( nMinMB <= nVideoRamMB && nVideoRamMB < nMaxMB ) + return pGroup; + } + return NULL; +} + + +//----------------------------------------------------------------------------- +// Methods related to reading DX support levels given particular devices +//----------------------------------------------------------------------------- + + +//----------------------------------------------------------------------------- +// Reads in the dxsupport.cfg keyvalues +//----------------------------------------------------------------------------- +static void OverrideValues_R( KeyValues *pDest, KeyValues *pSrc ) +{ + // Any same-named values get overridden in pDest. + for ( KeyValues *pSrcValue=pSrc->GetFirstValue(); pSrcValue; pSrcValue=pSrcValue->GetNextValue() ) + { + // Shouldn't be a container for more keys. + Assert( pSrcValue->GetDataType() != KeyValues::TYPE_NONE ); + AddKey( pDest, pSrcValue ); + } + + // Recurse. + for ( KeyValues *pSrcDir=pSrc->GetFirstTrueSubKey(); pSrcDir; pSrcDir=pSrcDir->GetNextTrueSubKey() ) + { + Assert( pSrcDir->GetDataType() == KeyValues::TYPE_NONE ); + + KeyValues *pDestDir = pDest->FindKey( pSrcDir->GetName() ); + if ( pDestDir && pDestDir->GetDataType() == KeyValues::TYPE_NONE ) + { + OverrideValues_R( pDestDir, pSrcDir ); + } + } +} + +static KeyValues * FindMatchingGroup( KeyValues *pSrc, KeyValues *pMatch ) +{ + KeyValues *pMatchSubKey = pMatch->FindKey( "name" ); + bool bHasSubKey = ( pMatchSubKey && ( pMatchSubKey->GetDataType() != KeyValues::TYPE_NONE ) ); + const char *name = bHasSubKey ? pMatchSubKey->GetString() : NULL; + int nMatchVendorID = ReadHexValue( pMatch, "VendorID" ); + int nMatchMinDeviceID = ReadHexValue( pMatch, "MinDeviceID" ); + int nMatchMaxDeviceID = ReadHexValue( pMatch, "MaxDeviceID" ); + + KeyValues *pSrcGroup = NULL; + for ( pSrcGroup = pSrc->GetFirstTrueSubKey(); pSrcGroup; pSrcGroup = pSrcGroup->GetNextTrueSubKey() ) + { + if ( name ) + { + KeyValues *pSrcGroupName = pSrcGroup->FindKey( "name" ); + Assert( pSrcGroupName ); + Assert( pSrcGroupName->GetDataType() != KeyValues::TYPE_NONE ); + if ( Q_stricmp( pSrcGroupName->GetString(), name ) ) + continue; + } + + if ( nMatchVendorID >= 0 ) + { + int nVendorID = ReadHexValue( pSrcGroup, "VendorID" ); + if ( nMatchVendorID != nVendorID ) + continue; + } + + if ( nMatchMinDeviceID >= 0 && nMatchMaxDeviceID >= 0 ) + { + int nMinDeviceID = ReadHexValue( pSrcGroup, "MinDeviceID" ); + int nMaxDeviceID = ReadHexValue( pSrcGroup, "MaxDeviceID" ); + if ( nMinDeviceID < 0 || nMaxDeviceID < 0 ) + continue; + + if ( nMatchMinDeviceID > nMinDeviceID || nMatchMaxDeviceID < nMaxDeviceID ) + continue; + } + + return pSrcGroup; + } + return NULL; +} + +static void OverrideKeyValues( KeyValues *pDst, KeyValues *pSrc ) +{ + KeyValues *pSrcGroup = NULL; + for ( pSrcGroup = pSrc->GetFirstTrueSubKey(); pSrcGroup; pSrcGroup = pSrcGroup->GetNextTrueSubKey() ) + { + // Match each group in pSrc to one in pDst containing the same "name" value: + KeyValues * pDstGroup = FindMatchingGroup( pDst, pSrcGroup ); + //Assert( pDstGroup ); + if ( pDstGroup ) + { + OverrideValues_R( pDstGroup, pSrcGroup ); + } + } + + // if( CommandLine()->FindParm( "-debugdxsupport" ) ) + // { + // CUtlBuffer tmpBuf; + // pDst->RecursiveSaveToFile( tmpBuf, 0 ); + // g_pFullFileSystem->WriteFile( "gary.txt", NULL, tmpBuf ); + // } +} + +KeyValues *CShaderDeviceMgrBase::ReadDXSupportKeyValues() +{ + if ( CommandLine()->CheckParm( "-ignoredxsupportcfg" ) ) + return NULL; + + if ( m_pDXSupport ) + return m_pDXSupport; + + KeyValues *pCfg = new KeyValues( "dxsupport" ); + + const char *pPathID = "EXECUTABLE_PATH"; + if ( IsX360() && g_pFullFileSystem->GetDVDMode() == DVDMODE_STRICT ) + { + // 360 dvd optimzation, expect it inside the platform zip + pPathID = "PLATFORM"; + } + + // First try to read a game-specific config, if it exists + if ( !pCfg->LoadFromFile( g_pFullFileSystem, SUPPORT_CFG_FILE, pPathID ) ) + { + pCfg->deleteThis(); + return NULL; + } + + char pTempPath[1024]; + if ( g_pFullFileSystem->GetSearchPath( "GAME", false, pTempPath, sizeof(pTempPath) ) > 1 ) + { + // Is there a mod-specific override file? + KeyValues *pOverride = new KeyValues( "dxsupport_override" ); + if ( pOverride->LoadFromFile( g_pFullFileSystem, SUPPORT_CFG_OVERRIDE_FILE, "GAME" ) ) + { + OverrideKeyValues( pCfg, pOverride ); + } + + pOverride->deleteThis(); + } + + m_pDXSupport = pCfg; + return pCfg; +} + + + +//----------------------------------------------------------------------------- +// Returns the max dx support level achievable with this board +//----------------------------------------------------------------------------- +void CShaderDeviceMgrBase::ReadDXSupportLevels( HardwareCaps_t &caps ) +{ + // See if the file tells us otherwise + KeyValues *pCfg = ReadDXSupportKeyValues(); + if ( !pCfg ) + return; + + KeyValues *pDeviceKeyValues = FindCardSpecificConfig( pCfg, caps.m_VendorID, caps.m_DeviceID ); + if ( pDeviceKeyValues ) + { + // First, set the max dx level + int nMaxDXSupportLevel = 0; + ReadInt( pDeviceKeyValues, "MaxDXLevel", 0, &nMaxDXSupportLevel ); + if ( nMaxDXSupportLevel != 0 ) + { + caps.m_nMaxDXSupportLevel = nMaxDXSupportLevel; + } + + // Next, set the preferred dx level + int nDXSupportLevel = 0; + ReadInt( pDeviceKeyValues, "DXLevel", 0, &nDXSupportLevel ); + if ( nDXSupportLevel != 0 ) + { + caps.m_nDXSupportLevel = nDXSupportLevel; + // Don't slam up the dxlevel level to 92 on DX10 cards in OpenGL Linux/Win mode (otherwise Intel will get dxlevel 92 when we want 90) + if ( !( IsOpenGL() && ( IsLinux() || IsWindows() ) ) ) + { + if ( caps.m_bDX10Card ) + { + caps.m_nDXSupportLevel = 92; + } + } + } + else + { + caps.m_nDXSupportLevel = caps.m_nMaxDXSupportLevel; + } + } +} + + +//----------------------------------------------------------------------------- +// Loads the hardware caps, for cases in which the D3D caps lie or where we need to augment the caps +//----------------------------------------------------------------------------- +void CShaderDeviceMgrBase::LoadHardwareCaps( KeyValues *pGroup, HardwareCaps_t &caps ) +{ + if( !pGroup ) + return; + + // don't just blanket kill clip planes on POSIX, only shoot them down if we're running ARB, or asked for nouserclipplanes. + //FIXME need to take into account the caps bit that GLM can now provide, so NV can use normal clipping and ATI can fall back to fastclip. + + if ( CommandLine()->FindParm("-arbmode") || CommandLine()->CheckParm( "-nouserclip" ) ) + { + caps.m_UseFastClipping = true; + } + else + { + caps.m_UseFastClipping = ReadBool( pGroup, "NoUserClipPlanes", caps.m_UseFastClipping ); + } + + caps.m_bNeedsATICentroidHack = ReadBool( pGroup, "CentroidHack", caps.m_bNeedsATICentroidHack ); + caps.m_bDisableShaderOptimizations = ReadBool( pGroup, "DisableShaderOptimizations", caps.m_bDisableShaderOptimizations ); +} + + + +//----------------------------------------------------------------------------- +// Reads in the hardware caps from the dxsupport.cfg file +//----------------------------------------------------------------------------- +void CShaderDeviceMgrBase::ReadHardwareCaps( HardwareCaps_t &caps, int nDxLevel ) +{ + KeyValues *pCfg = ReadDXSupportKeyValues(); + if ( !pCfg ) + return; + + // Next, read the hardware caps for that dx support level. + KeyValues *pDxLevelKeyValues = FindDXLevelSpecificConfig( pCfg, nDxLevel ); + // Look for a vendor specific line for a given dxlevel. + KeyValues *pDXLevelAndVendorKeyValue = FindDXLevelAndVendorSpecificConfig( pCfg, nDxLevel, caps.m_VendorID ); + // Finally, override the hardware caps based on the specific card + KeyValues *pCardKeyValues = FindCardSpecificConfig( pCfg, caps.m_VendorID, caps.m_DeviceID ); + + // Apply + if( pCardKeyValues && ReadHexValue( pCardKeyValues, "MinDeviceID" ) == 0 && ReadHexValue( pCardKeyValues, "MaxDeviceID" ) == 0xffff ) + { + // The card specific case is a catch all for device ids, so run it before running the dxlevel and card specific stuff. + LoadHardwareCaps( pDxLevelKeyValues, caps ); + LoadHardwareCaps( pCardKeyValues, caps ); + LoadHardwareCaps( pDXLevelAndVendorKeyValue, caps ); + } + else + { + // The card specific case is a small range of cards, so run it last to override all other configs. + LoadHardwareCaps( pDxLevelKeyValues, caps ); + // don't run this one since we have a specific config for this card. + // LoadHardwareCaps( pDXLevelAndVendorKeyValue, caps ); + LoadHardwareCaps( pCardKeyValues, caps ); + } +} + + +//----------------------------------------------------------------------------- +// Reads in ConVars + config variables +//----------------------------------------------------------------------------- +void CShaderDeviceMgrBase::LoadConfig( KeyValues *pKeyValues, KeyValues *pConfiguration ) +{ + if( !pKeyValues ) + return; + + if( CommandLine()->FindParm( "-debugdxsupport" ) ) + { + CUtlBuffer tmpBuf; + pKeyValues->RecursiveSaveToFile( tmpBuf, 0 ); + Warning( "%s\n", ( const char * )tmpBuf.Base() ); + } + for( KeyValues *pGroup = pKeyValues->GetFirstSubKey(); pGroup; pGroup = pGroup->GetNextKey() ) + { + AddKey( pConfiguration, pGroup ); + } +} + + +//----------------------------------------------------------------------------- +// Computes amount of ram +//----------------------------------------------------------------------------- +static unsigned long GetRam() +{ + MEMORYSTATUS stat; + GlobalMemoryStatus( &stat ); + + char buf[256]; + V_snprintf( buf, sizeof( buf ), "GlobalMemoryStatus: %llu\n", (uint64)(stat.dwTotalPhys) ); + Plat_DebugString( buf ); + + return (stat.dwTotalPhys / (1024 * 1024)); +} + + +//----------------------------------------------------------------------------- +// Gets the recommended configuration associated with a particular dx level +//----------------------------------------------------------------------------- +bool CShaderDeviceMgrBase::GetRecommendedConfigurationInfo( int nAdapter, int nDXLevel, int nVendorID, int nDeviceID, KeyValues *pConfiguration ) +{ + LOCK_SHADERAPI(); + + const HardwareCaps_t& caps = GetHardwareCaps( nAdapter ); + if ( nDXLevel == 0 ) + { + nDXLevel = caps.m_nDXSupportLevel; + } + nDXLevel = GetClosestActualDXLevel( nDXLevel ); + if ( nDXLevel > caps.m_nMaxDXSupportLevel ) + return false; + + KeyValues *pCfg = ReadDXSupportKeyValues(); + if ( !pCfg ) + return true; + + // Look for a dxlevel specific line + KeyValues *pDxLevelKeyValues = FindDXLevelSpecificConfig( pCfg, nDXLevel ); + // Look for a vendor specific line for a given dxlevel. + KeyValues *pDXLevelAndVendorKeyValue = FindDXLevelAndVendorSpecificConfig( pCfg, nDXLevel, nVendorID ); + // Next, override with device-specific overrides + KeyValues *pCardKeyValues = FindCardSpecificConfig( pCfg, nVendorID, nDeviceID ); + + // Apply + if ( pCardKeyValues && ReadHexValue( pCardKeyValues, "MinDeviceID" ) == 0 && ReadHexValue( pCardKeyValues, "MaxDeviceID" ) == 0xffff ) + { + // The card specific case is a catch all for device ids, so run it before running the dxlevel and card specific stuff. + LoadConfig( pDxLevelKeyValues, pConfiguration ); + LoadConfig( pCardKeyValues, pConfiguration ); + LoadConfig( pDXLevelAndVendorKeyValue, pConfiguration ); + } + else + { + // The card specific case is a small range of cards, so run it last to override all other configs. + LoadConfig( pDxLevelKeyValues, pConfiguration ); + // don't run this one since we have a specific config for this card. + // LoadConfig( pDXLevelAndVendorKeyValue, pConfiguration ); + LoadConfig( pCardKeyValues, pConfiguration ); + } + + // Next, override with cpu-speed based overrides + const CPUInformation& pi = *GetCPUInformation(); + int nCPUSpeedMhz = (int)(pi.m_Speed / 1000000.0f); + + bool bAMD = Q_stristr( pi.m_szProcessorID, "amd" ) != NULL; + + char buf[256]; + V_snprintf( buf, sizeof( buf ), "CShaderDeviceMgrBase::GetRecommendedConfigurationInfo: CPU speed: %d MHz, Processor: %s\n", nCPUSpeedMhz, pi.m_szProcessorID ); + Plat_DebugString( buf ); + + KeyValues *pCPUKeyValues = FindCPUSpecificConfig( pCfg, nCPUSpeedMhz, bAMD ); + LoadConfig( pCPUKeyValues, pConfiguration ); + + // override with system memory-size based overrides + int nSystemMB = GetRam(); + DevMsg( "%d MB of system RAM\n", nSystemMB ); + KeyValues *pMemoryKeyValues = FindMemorySpecificConfig( pCfg, nSystemMB ); + LoadConfig( pMemoryKeyValues, pConfiguration ); + + // override with texture memory-size based overrides + int nTextureMemorySize = GetVidMemBytes( nAdapter ); + int vidMemMB = nTextureMemorySize / ( 1024 * 1024 ); + KeyValues *pVidMemKeyValues = FindVidMemSpecificConfig( pCfg, vidMemMB ); + if ( pVidMemKeyValues && nTextureMemorySize > 0 ) + { + if ( CommandLine()->FindParm( "-debugdxsupport" ) ) + { + CUtlBuffer tmpBuf; + pVidMemKeyValues->RecursiveSaveToFile( tmpBuf, 0 ); + Warning( "pVidMemKeyValues\n%s\n", ( const char * )tmpBuf.Base() ); + } + KeyValues *pMatPicmipKeyValue = pVidMemKeyValues->FindKey( "ConVar.mat_picmip", false ); + + // FIXME: Man, is this brutal. If it wasn't 1 day till orange box ship, I'd do something in dxsupport maybe + if ( pMatPicmipKeyValue && ( ( nDXLevel == caps.m_nMaxDXSupportLevel ) || ( vidMemMB < 100 ) ) ) + { + KeyValues *pConfigMatPicMip = pConfiguration->FindKey( "ConVar.mat_picmip", false ); + int newPicMip = pMatPicmipKeyValue->GetInt(); + int oldPicMip = pConfigMatPicMip ? pConfigMatPicMip->GetInt() : 0; + pConfiguration->SetInt( "ConVar.mat_picmip", max( newPicMip, oldPicMip ) ); + } + } + + // Hack to slam the mat_dxlevel ConVar to match the requested dxlevel + pConfiguration->SetInt( "ConVar.mat_dxlevel", nDXLevel ); + + if ( CommandLine()->FindParm( "-debugdxsupport" ) ) + { + CUtlBuffer tmpBuf; + pConfiguration->RecursiveSaveToFile( tmpBuf, 0 ); + Warning( "final config:\n%s\n", ( const char * )tmpBuf.Base() ); + } + + return true; +} + + +//----------------------------------------------------------------------------- +// Gets recommended congifuration for a particular adapter at a particular dx level +//----------------------------------------------------------------------------- +bool CShaderDeviceMgrBase::GetRecommendedConfigurationInfo( int nAdapter, int nDXLevel, KeyValues *pCongifuration ) +{ + Assert( nAdapter >= 0 && nAdapter <= GetAdapterCount() ); + MaterialAdapterInfo_t info; + GetAdapterInfo( nAdapter, info ); + return GetRecommendedConfigurationInfo( nAdapter, nDXLevel, info.m_VendorID, info.m_DeviceID, pCongifuration ); +} + + +//----------------------------------------------------------------------------- +// Returns only valid dx levels +//----------------------------------------------------------------------------- +int CShaderDeviceMgrBase::GetClosestActualDXLevel( int nDxLevel ) const +{ + if ( nDxLevel < ABSOLUTE_MINIMUM_DXLEVEL ) + return ABSOLUTE_MINIMUM_DXLEVEL; + + if ( nDxLevel == 80 ) + return 80; + if ( nDxLevel <= 89 ) + return 81; + + if ( IsOpenGL() ) + { + return ( nDxLevel <= 90 ) ? 90 : 92; + } + + if ( nDxLevel <= 94 ) + return 90; + + if ( IsX360() && nDxLevel <= 98 ) + return 98; + if ( nDxLevel <= 99 ) + return 95; + return 100; +} + + +//----------------------------------------------------------------------------- +// Mode change callback +//----------------------------------------------------------------------------- +void CShaderDeviceMgrBase::AddModeChangeCallback( ShaderModeChangeCallbackFunc_t func ) +{ + LOCK_SHADERAPI(); + Assert( func && m_ModeChangeCallbacks.Find( func ) < 0 ); + m_ModeChangeCallbacks.AddToTail( func ); +} + +void CShaderDeviceMgrBase::RemoveModeChangeCallback( ShaderModeChangeCallbackFunc_t func ) +{ + LOCK_SHADERAPI(); + m_ModeChangeCallbacks.FindAndRemove( func ); +} + +void CShaderDeviceMgrBase::InvokeModeChangeCallbacks() +{ + int nCount = m_ModeChangeCallbacks.Count(); + for ( int i = 0; i < nCount; ++i ) + { + m_ModeChangeCallbacks[i](); + } +} + + +//----------------------------------------------------------------------------- +// Factory to return from SetMode +//----------------------------------------------------------------------------- +void* CShaderDeviceMgrBase::ShaderInterfaceFactory( const char *pInterfaceName, int *pReturnCode ) +{ + if ( pReturnCode ) + { + *pReturnCode = IFACE_OK; + } + if ( !Q_stricmp( pInterfaceName, SHADER_DEVICE_INTERFACE_VERSION ) ) + return static_cast< IShaderDevice* >( g_pShaderDevice ); + if ( !Q_stricmp( pInterfaceName, SHADERAPI_INTERFACE_VERSION ) ) + return static_cast< IShaderAPI* >( g_pShaderAPI ); + if ( !Q_stricmp( pInterfaceName, SHADERSHADOW_INTERFACE_VERSION ) ) + return static_cast< IShaderShadow* >( g_pShaderShadow ); + + if ( pReturnCode ) + { + *pReturnCode = IFACE_FAILED; + } + return NULL; +} + + +//----------------------------------------------------------------------------- +// +// The Base implementation of the shader device +// +//----------------------------------------------------------------------------- + + +//----------------------------------------------------------------------------- +// constructor, destructor +//----------------------------------------------------------------------------- +CShaderDeviceBase::CShaderDeviceBase() +{ + m_bInitialized = false; + m_nAdapter = -1; + m_hWnd = NULL; + m_hWndCookie = NULL; + m_dwThreadId = ThreadGetCurrentId(); +} + +CShaderDeviceBase::~CShaderDeviceBase() +{ +} + +void CShaderDeviceBase::SetCurrentThreadAsOwner() +{ + m_dwThreadId = ThreadGetCurrentId(); +} + +void CShaderDeviceBase::RemoveThreadOwner() +{ + m_dwThreadId = 0xFFFFFFFF; +} + +bool CShaderDeviceBase::ThreadOwnsDevice() +{ + if ( ThreadGetCurrentId() == m_dwThreadId ) + return true; + return false; +} + + +// Methods of IShaderDevice +ImageFormat CShaderDeviceBase::GetBackBufferFormat() const +{ + return IMAGE_FORMAT_UNKNOWN; +} + +int CShaderDeviceBase::StencilBufferBits() const +{ + return 0; +} + +bool CShaderDeviceBase::IsAAEnabled() const +{ + return false; +} + + +//----------------------------------------------------------------------------- +// Methods for interprocess communication to release resources +//----------------------------------------------------------------------------- +#define MATERIAL_SYSTEM_WINDOW_ID 0xFEEDDEAD + +#ifdef USE_ACTUAL_DX +static VD3DHWND GetTopmostParentWindow( VD3DHWND hWnd ) +{ + // Find the parent window... + VD3DHWND hParent = GetParent( hWnd ); + while ( hParent ) + { + hWnd = hParent; + hParent = GetParent( hWnd ); + } + + return hWnd; +} + +static BOOL CALLBACK EnumChildWindowsProc( VD3DHWND hWnd, LPARAM lParam ) +{ + int windowId = GetWindowLongPtr( hWnd, GWLP_USERDATA ); + if (windowId == MATERIAL_SYSTEM_WINDOW_ID) + { + COPYDATASTRUCT copyData; + copyData.dwData = lParam; + copyData.cbData = 0; + copyData.lpData = 0; + + SendMessage(hWnd, WM_COPYDATA, 0, (LPARAM)©Data); + } + return TRUE; +} + +static BOOL CALLBACK EnumWindowsProc( VD3DHWND hWnd, LPARAM lParam ) +{ + EnumChildWindows( hWnd, EnumChildWindowsProc, lParam ); + return TRUE; +} + +static BOOL CALLBACK EnumWindowsProcNotThis( VD3DHWND hWnd, LPARAM lParam ) +{ + if ( g_pShaderDevice && ( GetTopmostParentWindow( (VD3DHWND)g_pShaderDevice->GetIPCHWnd() ) == hWnd ) ) + return TRUE; + + EnumChildWindows( hWnd, EnumChildWindowsProc, lParam ); + return TRUE; +} +#endif + +//----------------------------------------------------------------------------- +// Adds a hook to let us know when other instances are setting the mode +//----------------------------------------------------------------------------- + +#ifdef STRICT +#define WINDOW_PROC WNDPROC +#else +#define WINDOW_PROC FARPROC +#endif + +#ifdef USE_ACTUAL_DX +static LRESULT CALLBACK ShaderDX8WndProc(VD3DHWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam ) +{ +#if !defined( _X360 ) + // FIXME: Should these IPC messages tell when an app has focus or not? + // If so, we'd want to totally disable the shader api layer when an app + // doesn't have focus. + + // Look for the special IPC message that tells us we're trying to set + // the mode.... + switch(msg) + { + case WM_COPYDATA: + { + if ( !g_pShaderDevice ) + break; + + COPYDATASTRUCT* pData = (COPYDATASTRUCT*)lParam; + + // that number is our magic cookie number + if ( pData->dwData == CShaderDeviceBase::RELEASE_MESSAGE ) + { + g_pShaderDevice->OtherAppInitializing(true); + } + else if ( pData->dwData == CShaderDeviceBase::REACQUIRE_MESSAGE ) + { + g_pShaderDevice->OtherAppInitializing(false); + } + else if ( pData->dwData == CShaderDeviceBase::EVICT_MESSAGE ) + { + g_pShaderDevice->EvictManagedResourcesInternal( ); + } + } + break; + } + + return DefWindowProc( hWnd, msg, wParam, lParam ); +#endif +} +#endif + + +//----------------------------------------------------------------------------- +// Install, remove ability to talk to other shaderapi apps +//----------------------------------------------------------------------------- +void CShaderDeviceBase::InstallWindowHook( void* hWnd ) +{ + Assert( m_hWndCookie == NULL ); +#ifdef USE_ACTUAL_DX +#if !defined( _X360 ) + VD3DHWND hParent = GetTopmostParentWindow( (VD3DHWND)hWnd ); + + // Attach a child window to the parent; we're gonna store special info there + // We can't use the USERDATA, cause other apps may want to use this. + HINSTANCE hInst = (HINSTANCE)GetWindowLongPtr( hParent, GWLP_HINSTANCE ); + WNDCLASS wc; + memset( &wc, 0, sizeof( wc ) ); + wc.style = CS_NOCLOSE | CS_PARENTDC; + wc.lpfnWndProc = ShaderDX8WndProc; + wc.hInstance = hInst; + wc.lpszClassName = "shaderdx8"; + + // In case an old one is sitting around still... + UnregisterClass( "shaderdx8", hInst ); + + RegisterClass( &wc ); + + // Create the window + m_hWndCookie = CreateWindow( "shaderdx8", "shaderdx8", WS_CHILD, + 0, 0, 0, 0, hParent, NULL, hInst, NULL ); + + // Marks it as a material system window + SetWindowLongPtr( (VD3DHWND)m_hWndCookie, GWLP_USERDATA, MATERIAL_SYSTEM_WINDOW_ID ); +#endif +#endif +} + +void CShaderDeviceBase::RemoveWindowHook( void* hWnd ) +{ +#ifdef USE_ACTUAL_DX +#if !defined( _X360 ) + if ( m_hWndCookie ) + { + DestroyWindow( (VD3DHWND)m_hWndCookie ); + m_hWndCookie = 0; + } + + VD3DHWND hParent = GetTopmostParentWindow( (VD3DHWND)hWnd ); + HINSTANCE hInst = (HINSTANCE)GetWindowLongPtr( hParent, GWLP_HINSTANCE ); + UnregisterClass( "shaderdx8", hInst ); +#endif +#endif +} + + +//----------------------------------------------------------------------------- +// Sends a message to other shaderapi applications +//----------------------------------------------------------------------------- +void CShaderDeviceBase::SendIPCMessage( IPCMessage_t msg ) +{ +#ifdef USE_ACTUAL_DX +#if !defined( _X360 ) + // Gotta send this to all windows, since we don't know which ones + // are material system apps... + if ( msg != EVICT_MESSAGE ) + { + EnumWindows( EnumWindowsProc, (DWORD)msg ); + } + else + { + EnumWindows( EnumWindowsProcNotThis, (DWORD)msg ); + } +#endif +#endif +} + + +//----------------------------------------------------------------------------- +// Find view +//----------------------------------------------------------------------------- +int CShaderDeviceBase::FindView( void* hWnd ) const +{ + /* FIXME: Is this necessary? + // Look for the view in the list of views + for (int i = m_Views.Count(); --i >= 0; ) + { + if (m_Views[i].m_HWnd == (VD3DHWND)hwnd) + return i; + } + */ + return -1; +} + +//----------------------------------------------------------------------------- +// Creates a child window +//----------------------------------------------------------------------------- +bool CShaderDeviceBase::AddView( void* hWnd ) +{ + LOCK_SHADERAPI(); + /* + // If we haven't created a device yet + if (!Dx9Device()) + return false; + + // Make sure no duplicate hwnds... + if (FindView(hwnd) >= 0) + return false; + + // In this case, we need to create the device; this is our + // default swap chain. This here says we're gonna use a part of the + // existing buffer and just grab that. + int view = m_Views.AddToTail(); + m_Views[view].m_HWnd = (VD3DHWND)hwnd; + // memcpy( &m_Views[view].m_PresentParamters, m_PresentParameters, sizeof(m_PresentParamters) ); + + HRESULT hr; + hr = Dx9Device()->CreateAdditionalSwapChain( &m_PresentParameters, + &m_Views[view].m_pSwapChain ); + return !FAILED(hr); + */ + + return true; +} + +void CShaderDeviceBase::RemoveView( void* hWnd ) +{ + LOCK_SHADERAPI(); + /* + // Look for the view in the list of views + int i = FindView(hwnd); + if (i >= 0) + { + // FIXME m_Views[i].m_pSwapChain->Release(); + m_Views.FastRemove(i); + } + */ +} + +//----------------------------------------------------------------------------- +// Activates a child window +//----------------------------------------------------------------------------- +void CShaderDeviceBase::SetView( void* hWnd ) +{ + LOCK_SHADERAPI(); + + ShaderViewport_t viewport; + g_pShaderAPI->GetViewports( &viewport, 1 ); + + // Get the window (*not* client) rect of the view window + m_ViewHWnd = (VD3DHWND)hWnd; + GetWindowSize( m_nWindowWidth, m_nWindowHeight ); + + // Reset the viewport (takes into account the view rect) + // Don't need to set the viewport if it's not ready + g_pShaderAPI->SetViewports( 1, &viewport ); +} + + +//----------------------------------------------------------------------------- +// Gets the window size +//----------------------------------------------------------------------------- +void CShaderDeviceBase::GetWindowSize( int& nWidth, int& nHeight ) const +{ +#if defined( USE_SDL ) + + // this matches up to what the threaded material system does + g_pShaderAPI->GetBackBufferDimensions( nWidth, nHeight ); + +#else + + // If the window was minimized last time swap buffers happened, or if it's iconic now, + // return 0 size +#ifdef _WIN32 + if ( !m_bIsMinimized && !IsIconic( ( HWND )m_hWnd ) ) +#else + if ( !m_bIsMinimized && !IsIconic( (VD3DHWND)m_hWnd ) ) +#endif + { + // NOTE: Use the 'current view' (which may be the same as the main window) + RECT rect; +#ifdef _WIN32 + GetClientRect( ( HWND )m_ViewHWnd, &rect ); +#else + toglGetClientRect( (VD3DHWND)m_ViewHWnd, &rect ); +#endif + nWidth = rect.right - rect.left; + nHeight = rect.bottom - rect.top; + } + else + { + nWidth = nHeight = 0; + } + +#endif +} + + diff --git a/materialsystem/shaderapidx9/shaderdevicebase.h b/materialsystem/shaderapidx9/shaderdevicebase.h new file mode 100644 index 0000000..37d7cc0 --- /dev/null +++ b/materialsystem/shaderapidx9/shaderdevicebase.h @@ -0,0 +1,234 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//===========================================================================// + +#ifndef SHADERDEVICEBASE_H +#define SHADERDEVICEBASE_H + +#ifdef _WIN32 +#pragma once +#endif + + +#include "togl/rendermechanism.h" +#include "shaderapi/IShaderDevice.h" +#include "IHardwareConfigInternal.h" +#include "bitmap/imageformat.h" +#include "materialsystem/imaterialsystem.h" +#include "hardwareconfig.h" + + +//----------------------------------------------------------------------------- +// Forward declarations +//----------------------------------------------------------------------------- +class KeyValues; + + +//----------------------------------------------------------------------------- +// define this if you want to run with NVPERFHUD +//----------------------------------------------------------------------------- +//#define NVPERFHUD 1 + + +//----------------------------------------------------------------------------- +// Uncomment this to activate the reference rasterizer +//----------------------------------------------------------------------------- +//#define USE_REFERENCE_RASTERIZER 1 + +//----------------------------------------------------------------------------- +// Uncomment to check for -nulldevice on command line and use D3DDEVTYPE_NULLREF. +//----------------------------------------------------------------------------- +#define ENABLE_NULLREF_DEVICE_SUPPORT + +//----------------------------------------------------------------------------- +// The Base implementation of the shader device +//----------------------------------------------------------------------------- +class CShaderDeviceMgrBase : public IShaderDeviceMgr +{ +public: + // constructor, destructor + CShaderDeviceMgrBase(); + virtual ~CShaderDeviceMgrBase(); + + // Methods of IAppSystem + virtual bool Connect( CreateInterfaceFn factory ); + virtual void Disconnect(); + virtual void *QueryInterface( const char *pInterfaceName ); + + // Methods of IShaderDeviceMgr + virtual bool GetRecommendedConfigurationInfo( int nAdapter, int nDXLevel, KeyValues *pCongifuration ); + virtual void AddModeChangeCallback( ShaderModeChangeCallbackFunc_t func ); + virtual void RemoveModeChangeCallback( ShaderModeChangeCallbackFunc_t func ); + + // Reads in the hardware caps from the dxsupport.cfg file + void ReadHardwareCaps( HardwareCaps_t &caps, int nDxLevel ); + + // Reads in the max + preferred DX support level + void ReadDXSupportLevels( HardwareCaps_t &caps ); + + // Returns the hardware caps for a particular adapter + const HardwareCaps_t& GetHardwareCaps( int nAdapter ) const; + + // Invokes mode change callbacks + void InvokeModeChangeCallbacks(); + + // Factory to return from SetMode + static void* ShaderInterfaceFactory( const char *pInterfaceName, int *pReturnCode ); + + // Returns only valid dx levels + int GetClosestActualDXLevel( int nDxLevel ) const; + +protected: + struct AdapterInfo_t + { + HardwareCaps_t m_ActualCaps; + }; + +private: + // Reads in the dxsupport.cfg keyvalues + KeyValues *ReadDXSupportKeyValues(); + + // Reads in ConVars + config variables + void LoadConfig( KeyValues *pKeyValues, KeyValues *pConfiguration ); + + // Loads the hardware caps, for cases in which the D3D caps lie or where we need to augment the caps + void LoadHardwareCaps( KeyValues *pGroup, HardwareCaps_t &caps ); + + // Gets the recommended configuration associated with a particular dx level + bool GetRecommendedConfigurationInfo( int nAdapter, int nDXLevel, int nVendorID, int nDeviceID, KeyValues *pConfiguration ); + + // Returns the amount of video memory in bytes for a particular adapter + virtual int GetVidMemBytes( int nAdapter ) const = 0; + + // Looks for override keyvalues in the dxsupport cfg keyvalues + KeyValues *FindDXLevelSpecificConfig( KeyValues *pKeyValues, int nDxLevel ); + KeyValues *FindDXLevelAndVendorSpecificConfig( KeyValues *pKeyValues, int nDxLevel, int nVendorID ); + KeyValues *FindCPUSpecificConfig( KeyValues *pKeyValues, int nCPUMhz, bool bAMD ); + KeyValues *FindMemorySpecificConfig( KeyValues *pKeyValues, int nSystemRamMB ); + KeyValues *FindVidMemSpecificConfig( KeyValues *pKeyValues, int nVideoRamMB ); + KeyValues *FindCardSpecificConfig( KeyValues *pKeyValues, int nVendorID, int nDeviceID ); + +protected: + // Stores adapter info for all adapters + CUtlVector m_Adapters; + + // Installed mode change callbacks + CUtlVector< ShaderModeChangeCallbackFunc_t > m_ModeChangeCallbacks; + + KeyValues *m_pDXSupport; +}; + + +//----------------------------------------------------------------------------- +// The Base implementation of the shader device +//----------------------------------------------------------------------------- +class CShaderDeviceBase : public IShaderDevice +{ +public: + enum IPCMessage_t + { + RELEASE_MESSAGE = 0x5E740DE0, + REACQUIRE_MESSAGE = 0x5E740DE1, + EVICT_MESSAGE = 0x5E740DE2, + }; + + // Methods of IShaderDevice +public: + virtual ImageFormat GetBackBufferFormat() const; + virtual int StencilBufferBits() const; + virtual bool IsAAEnabled() const; + virtual bool AddView( void* hWnd ); + virtual void RemoveView( void* hWnd ); + virtual void SetView( void* hWnd ); + virtual void GetWindowSize( int& nWidth, int& nHeight ) const; + + // Methods exposed to the rest of shader api + virtual bool InitDevice( void *hWnd, int nAdapter, const ShaderDeviceInfo_t& mode ) = 0; + virtual void ShutdownDevice() = 0; + virtual bool IsDeactivated() const = 0; + +public: + // constructor, destructor + CShaderDeviceBase(); + virtual ~CShaderDeviceBase(); + + virtual void OtherAppInitializing( bool initializing ) {} + virtual void EvictManagedResourcesInternal() {} + + void* GetIPCHWnd(); + void SendIPCMessage( IPCMessage_t message ); + +protected: + // IPC communication for multiple shaderapi apps + void InstallWindowHook( void *hWnd ); + void RemoveWindowHook( void *hWnd ); + void SetCurrentThreadAsOwner(); + void RemoveThreadOwner(); + bool ThreadOwnsDevice(); + + // Finds a child window + int FindView( void* hWnd ) const; + + int m_nAdapter; + void *m_hWnd; + void* m_hWndCookie; + bool m_bInitialized : 1; + bool m_bIsMinimized : 1; + + // The current view hwnd + void* m_ViewHWnd; + + int m_nWindowWidth; + int m_nWindowHeight; + uint32 m_dwThreadId; +}; + + +//----------------------------------------------------------------------------- +// Inline methods +//----------------------------------------------------------------------------- +inline void* CShaderDeviceBase::GetIPCHWnd() +{ + return m_hWndCookie; +} + + +//----------------------------------------------------------------------------- +// Helper class to reduce code related to shader buffers +//----------------------------------------------------------------------------- +template< class T > +class CShaderBuffer : public IShaderBuffer +{ +public: + CShaderBuffer( T *pBlob ) : m_pBlob( pBlob ) {} + + virtual size_t GetSize() const + { + return m_pBlob ? m_pBlob->GetBufferSize() : 0; + } + + virtual const void* GetBits() const + { + return m_pBlob ? m_pBlob->GetBufferPointer() : NULL; + } + + virtual void Release() + { + if ( m_pBlob ) + { + m_pBlob->Release(); + } + delete this; + } + +private: + T *m_pBlob; +}; + + + +#endif // SHADERDEVICEBASE_H \ No newline at end of file diff --git a/materialsystem/shaderapidx9/shaderdevicedx10.cpp b/materialsystem/shaderapidx9/shaderdevicedx10.cpp new file mode 100644 index 0000000..39de5a1 --- /dev/null +++ b/materialsystem/shaderapidx9/shaderdevicedx10.cpp @@ -0,0 +1,1002 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//===========================================================================// + +#include +#include + +#include "shaderdevicedx10.h" +#include "shaderdevicedx8.h" +#include "shaderapi/ishaderutil.h" +#include "shaderapidx10.h" +#include "shadershadowdx10.h" +#include "meshdx10.h" +#include "shaderapidx10_global.h" +#include "tier1/KeyValues.h" +#include "tier2/tier2.h" +#include "tier0/icommandline.h" +#include "inputlayoutdx10.h" +#include "shaderapibase.h" + + +//----------------------------------------------------------------------------- +// Explicit instantiation of shader buffer implementation +//----------------------------------------------------------------------------- +template class CShaderBuffer< ID3D10Blob >; + + +//----------------------------------------------------------------------------- +// +// Device manager +// +//----------------------------------------------------------------------------- +static CShaderDeviceMgrDx10 g_ShaderDeviceMgrDx10; + +EXPOSE_SINGLE_INTERFACE_GLOBALVAR( CShaderDeviceMgrDx10, IShaderDeviceMgr, + SHADER_DEVICE_MGR_INTERFACE_VERSION, g_ShaderDeviceMgrDx10 ) + +static CShaderDeviceDx10 g_ShaderDeviceDx10; +CShaderDeviceDx10* g_pShaderDeviceDx10 = &g_ShaderDeviceDx10; + +//----------------------------------------------------------------------------- +// constructor, destructor +//----------------------------------------------------------------------------- +CShaderDeviceMgrDx10::CShaderDeviceMgrDx10() +{ + m_pDXGIFactory = NULL; + m_bObeyDxCommandlineOverride = true; +} + +CShaderDeviceMgrDx10::~CShaderDeviceMgrDx10() +{ +} + + +//----------------------------------------------------------------------------- +// Connect, disconnect +//----------------------------------------------------------------------------- +bool CShaderDeviceMgrDx10::Connect( CreateInterfaceFn factory ) +{ + LOCK_SHADERAPI(); + + if ( !BaseClass::Connect( factory ) ) + return false; + + HRESULT hr = CreateDXGIFactory( __uuidof(IDXGIFactory), (void**)(&m_pDXGIFactory) ); + if ( FAILED( hr ) ) + { + Warning( "Failed to create the DXGI Factory!\n" ); + return false; + } + + InitAdapterInfo(); + return true; +} + +void CShaderDeviceMgrDx10::Disconnect() +{ + LOCK_SHADERAPI(); + + if ( m_pDXGIFactory ) + { + m_pDXGIFactory->Release(); + m_pDXGIFactory = NULL; + } + + BaseClass::Disconnect(); +} + + +//----------------------------------------------------------------------------- +// Initialization +//----------------------------------------------------------------------------- +InitReturnVal_t CShaderDeviceMgrDx10::Init( ) +{ + LOCK_SHADERAPI(); + + return INIT_OK; +} + + +//----------------------------------------------------------------------------- +// Shutdown +//----------------------------------------------------------------------------- +void CShaderDeviceMgrDx10::Shutdown( ) +{ + LOCK_SHADERAPI(); + + if ( g_pShaderDevice ) + { + g_pShaderDevice->ShutdownDevice(); + g_pShaderDevice = NULL; + } +} + + +//----------------------------------------------------------------------------- +// Initialize adapter information +//----------------------------------------------------------------------------- +void CShaderDeviceMgrDx10::InitAdapterInfo() +{ + m_Adapters.RemoveAll(); + + IDXGIAdapter *pAdapter; + for( UINT nCount = 0; m_pDXGIFactory->EnumAdapters( nCount, &pAdapter ) != DXGI_ERROR_NOT_FOUND; ++nCount ) + { + int j = m_Adapters.AddToTail(); + AdapterInfo_t &info = m_Adapters[j]; + +#ifdef _DEBUG + memset( &info.m_ActualCaps, 0xDD, sizeof(info.m_ActualCaps) ); +#endif + + IDXGIOutput *pOutput = GetAdapterOutput( nCount ); + info.m_ActualCaps.m_bDeviceOk = ComputeCapsFromD3D( &info.m_ActualCaps, pAdapter, pOutput ); + if ( !info.m_ActualCaps.m_bDeviceOk ) + continue; + + ReadDXSupportLevels( info.m_ActualCaps ); + + // Read dxsupport.cfg which has config overrides for particular cards. + ReadHardwareCaps( info.m_ActualCaps, info.m_ActualCaps.m_nMaxDXSupportLevel ); + + // What's in "-shader" overrides dxsupport.cfg + const char *pShaderParam = CommandLine()->ParmValue( "-shader" ); + if ( pShaderParam ) + { + Q_strncpy( info.m_ActualCaps.m_pShaderDLL, pShaderParam, sizeof( info.m_ActualCaps.m_pShaderDLL ) ); + } + } +} + + +//----------------------------------------------------------------------------- +// Determines hardware caps from D3D +//----------------------------------------------------------------------------- +bool CShaderDeviceMgrDx10::ComputeCapsFromD3D( HardwareCaps_t *pCaps, IDXGIAdapter *pAdapter, IDXGIOutput *pOutput ) +{ + HRESULT hr = pAdapter->CheckInterfaceSupport( __uuidof(ID3D10Device), NULL ); + if ( hr != S_OK ) + { + // Fall back to Dx9 + return false; + } + + DXGI_ADAPTER_DESC desc; + hr = pAdapter->GetDesc( &desc ); + Assert( !FAILED( hr ) ); + if ( FAILED(hr) ) + return false; + + bool bForceFloatHDR = ( CommandLine()->CheckParm( "-floathdr" ) != NULL ); + + // DX10 settings + // NOTE: We'll need to have different settings for dx10.1 and dx11 + Q_UnicodeToUTF8( desc.Description, pCaps->m_pDriverName, MATERIAL_ADAPTER_NAME_LENGTH ); + pCaps->m_VendorID = desc.VendorId; + pCaps->m_DeviceID = desc.DeviceId; + pCaps->m_SubSysID = desc.SubSysId; + pCaps->m_Revision = desc.Revision; + pCaps->m_NumSamplers = 16; + pCaps->m_NumTextureStages = 0; + pCaps->m_HasSetDeviceGammaRamp = true; + pCaps->m_bSoftwareVertexProcessing = false; + pCaps->m_SupportsVertexShaders = true; + pCaps->m_SupportsVertexShaders_2_0 = false; + pCaps->m_SupportsPixelShaders = true; + pCaps->m_SupportsPixelShaders_1_4 = false; + pCaps->m_SupportsPixelShaders_2_0 = false; + pCaps->m_SupportsPixelShaders_2_b = false; + pCaps->m_SupportsShaderModel_3_0 = false; + pCaps->m_SupportsCompressedTextures = COMPRESSED_TEXTURES_ON; + pCaps->m_SupportsCompressedVertices = VERTEX_COMPRESSION_ON; + pCaps->m_bSupportsAnisotropicFiltering = true; + pCaps->m_bSupportsMagAnisotropicFiltering = true; + pCaps->m_bSupportsVertexTextures = true; + pCaps->m_nMaxAnisotropy = 16; + pCaps->m_MaxTextureWidth = D3D10_REQ_TEXTURE2D_U_OR_V_DIMENSION; + pCaps->m_MaxTextureHeight = D3D10_REQ_TEXTURE2D_U_OR_V_DIMENSION; + pCaps->m_MaxTextureDepth = D3D10_REQ_TEXTURE3D_U_V_OR_W_DIMENSION; + pCaps->m_MaxTextureAspectRatio = 1024; // FIXME + pCaps->m_MaxPrimitiveCount = 65536; // FIXME + pCaps->m_ZBiasAndSlopeScaledDepthBiasSupported = true; + pCaps->m_SupportsMipmapping = true; + pCaps->m_SupportsOverbright = true; + pCaps->m_SupportsCubeMaps = true; + pCaps->m_NumPixelShaderConstants = 1024; // FIXME + pCaps->m_NumVertexShaderConstants = 1024; // FIXME + pCaps->m_TextureMemorySize = desc.DedicatedVideoMemory; + pCaps->m_MaxNumLights = 4; + pCaps->m_SupportsHardwareLighting = false; + pCaps->m_MaxBlendMatrices = 0; + pCaps->m_MaxBlendMatrixIndices = 0; + pCaps->m_MaxVertexShaderBlendMatrices = 53; // FIXME + pCaps->m_SupportsMipmappedCubemaps = true; + pCaps->m_SupportsNonPow2Textures = true; + pCaps->m_nDXSupportLevel = 100; + pCaps->m_PreferDynamicTextures = false; + pCaps->m_HasProjectedBumpEnv = true; + pCaps->m_MaxUserClipPlanes = 6; // FIXME + pCaps->m_HDRType = bForceFloatHDR ? HDR_TYPE_FLOAT : HDR_TYPE_INTEGER; + pCaps->m_SupportsSRGB = true; + pCaps->m_FakeSRGBWrite = true; + pCaps->m_CanDoSRGBReadFromRTs = true; + pCaps->m_bSupportsSpheremapping = true; + pCaps->m_UseFastClipping = false; + pCaps->m_pShaderDLL[0] = 0; + pCaps->m_bNeedsATICentroidHack = false; + pCaps->m_bColorOnSecondStream = true; + pCaps->m_bSupportsStreamOffset = true; + pCaps->m_nMaxDXSupportLevel = 100; + pCaps->m_bFogColorSpecifiedInLinearSpace = ( desc.VendorId == VENDORID_NVIDIA ); + pCaps->m_nVertexTextureCount = 16; + pCaps->m_nMaxVertexTextureDimension = D3D10_REQ_TEXTURE2D_U_OR_V_DIMENSION; + pCaps->m_bSupportsAlphaToCoverage = false; // FIXME + pCaps->m_bSupportsShadowDepthTextures = true; + pCaps->m_bSupportsFetch4 = ( desc.VendorId == VENDORID_ATI ); + pCaps->m_bSupportsBorderColor = true; + pCaps->m_ShadowDepthTextureFormat = IMAGE_FORMAT_UNKNOWN; + pCaps->m_nMaxViewports = 4; + + DXGI_GAMMA_CONTROL_CAPABILITIES gammaCaps; + pOutput->GetGammaControlCapabilities( &gammaCaps ); + pCaps->m_flMinGammaControlPoint = gammaCaps.MinConvertedValue; + pCaps->m_flMaxGammaControlPoint = gammaCaps.MaxConvertedValue; + pCaps->m_nGammaControlPointCount = gammaCaps.NumGammaControlPoints; + pCaps->m_bCanStretchRectFromTextures = true; + return true; +} + + +//----------------------------------------------------------------------------- +// Gets the number of adapters... +//----------------------------------------------------------------------------- +int CShaderDeviceMgrDx10::GetAdapterCount() const +{ + return m_Adapters.Count(); +} + + +//----------------------------------------------------------------------------- +// Returns info about each adapter +//----------------------------------------------------------------------------- +void CShaderDeviceMgrDx10::GetAdapterInfo( int nAdapter, MaterialAdapterInfo_t& info ) const +{ + Assert( ( nAdapter >= 0 ) && ( nAdapter < m_Adapters.Count() ) ); + const HardwareCaps_t &caps = m_Adapters[ nAdapter ].m_ActualCaps; + memcpy( &info, &caps, sizeof(MaterialAdapterInfo_t) ); +} + + +//----------------------------------------------------------------------------- +// Returns the adapter interface for a particular adapter +//----------------------------------------------------------------------------- +IDXGIAdapter* CShaderDeviceMgrDx10::GetAdapter( int nAdapter ) const +{ + Assert( m_pDXGIFactory && ( nAdapter < GetAdapterCount() ) ); + + IDXGIAdapter *pAdapter; + HRESULT hr = m_pDXGIFactory->EnumAdapters( nAdapter, &pAdapter ); + return ( FAILED(hr) ) ? NULL : pAdapter; +} + + +//----------------------------------------------------------------------------- +// Returns the amount of video memory in bytes for a particular adapter +//----------------------------------------------------------------------------- +int CShaderDeviceMgrDx10::GetVidMemBytes( int nAdapter ) const +{ + LOCK_SHADERAPI(); + IDXGIAdapter *pAdapter = GetAdapter( nAdapter ); + if ( !pAdapter ) + return 0; + + DXGI_ADAPTER_DESC desc; + +#ifdef DBGFLAG_ASSERT + HRESULT hr = +#endif + pAdapter->GetDesc( &desc ); + Assert( !FAILED( hr ) ); + return desc.DedicatedVideoMemory; +} + + +//----------------------------------------------------------------------------- +// Returns the appropriate adapter output to use +//----------------------------------------------------------------------------- +IDXGIOutput* CShaderDeviceMgrDx10::GetAdapterOutput( int nAdapter ) const +{ + LOCK_SHADERAPI(); + IDXGIAdapter *pAdapter = GetAdapter( nAdapter ); + if ( !pAdapter ) + return 0; + + IDXGIOutput *pOutput; + for( UINT i = 0; pAdapter->EnumOutputs( i, &pOutput ) != DXGI_ERROR_NOT_FOUND; ++i ) + { + DXGI_OUTPUT_DESC desc; + HRESULT hr = pOutput->GetDesc( &desc ); + if ( FAILED( hr ) ) + continue; + + // FIXME: Is this what I want? Or should I be looking at other fields, + // like DXGI_MODE_ROTATION_IDENTITY? + if ( !desc.AttachedToDesktop ) + continue; + + return pOutput; + } + + return NULL; +} + + +//----------------------------------------------------------------------------- +// Returns the number of modes +//----------------------------------------------------------------------------- +int CShaderDeviceMgrDx10::GetModeCount( int nAdapter ) const +{ + LOCK_SHADERAPI(); + Assert( m_pDXGIFactory && ( nAdapter < GetAdapterCount() ) ); + + IDXGIOutput *pOutput = GetAdapterOutput( nAdapter ); + if ( !pOutput ) + return 0; + + UINT num = 0; + DXGI_FORMAT format = DXGI_FORMAT_R8G8B8A8_UNORM_SRGB; //desired color format + UINT flags = 0; //desired scanline order and/or scaling + + // get the number of available display mode for the given format and scanline order + HRESULT hr = pOutput->GetDisplayModeList( format, flags, &num, 0 ); + return ( FAILED(hr) ) ? 0 : num; +} + + +//----------------------------------------------------------------------------- +// Returns mode information.. +//----------------------------------------------------------------------------- +void CShaderDeviceMgrDx10::GetModeInfo( ShaderDisplayMode_t* pInfo, int nAdapter, int nMode ) const +{ + // Default error state + pInfo->m_nWidth = pInfo->m_nHeight = 0; + pInfo->m_Format = IMAGE_FORMAT_UNKNOWN; + pInfo->m_nRefreshRateNumerator = pInfo->m_nRefreshRateDenominator = 0; + + LOCK_SHADERAPI(); + Assert( m_pDXGIFactory && ( nAdapter < GetAdapterCount() ) ); + + IDXGIOutput *pOutput = GetAdapterOutput( nAdapter ); + if ( !pOutput ) + return; + + UINT num = 0; + DXGI_FORMAT format = DXGI_FORMAT_R8G8B8A8_UNORM_SRGB; //desired color format + UINT flags = DXGI_ENUM_MODES_INTERLACED; //desired scanline order and/or scaling + + // get the number of available display mode for the given format and scanline order + HRESULT hr = pOutput->GetDisplayModeList( format, flags, &num, 0 ); + Assert( !FAILED( hr ) ); + + if ( (UINT)nMode >= num ) + return; + + DXGI_MODE_DESC *pDescs = (DXGI_MODE_DESC*)_alloca( num * sizeof( DXGI_MODE_DESC ) ); + hr = pOutput->GetDisplayModeList( format, flags, &num, pDescs ); + Assert( !FAILED( hr ) ); + + pInfo->m_nWidth = pDescs[nMode].Width; + pInfo->m_nHeight = pDescs[nMode].Height; +// pInfo->m_Format = ImageLoader::D3DFormatToImageFormat( pDescs[nMode].Format ); + pInfo->m_nRefreshRateNumerator = pDescs[nMode].RefreshRate.Numerator; + pInfo->m_nRefreshRateDenominator = pDescs[nMode].RefreshRate.Denominator; +} + + +//----------------------------------------------------------------------------- +// Returns the current mode for an adapter +//----------------------------------------------------------------------------- +void CShaderDeviceMgrDx10::GetCurrentModeInfo( ShaderDisplayMode_t* pInfo, int nAdapter ) const +{ + // FIXME: Implement! + Assert( 0 ); +} + + +//----------------------------------------------------------------------------- +// Initialization, shutdown +//----------------------------------------------------------------------------- +bool CShaderDeviceMgrDx10::SetAdapter( int nAdapter, int nFlags ) +{ + /* + if ( !g_pShaderDeviceDx10->Init() ) + { + Warning( "Unable to initialize dx10 device!\n" ); + return false; + } + + g_pMaterialSystemHardwareConfig = g_pShaderDeviceDx10; + g_pShaderDevice = g_pShaderDeviceDx10; + */ + return true; +} + + +//----------------------------------------------------------------------------- +// Sets the mode +//----------------------------------------------------------------------------- +CreateInterfaceFn CShaderDeviceMgrDx10::SetMode( void *hWnd, int nAdapter, const ShaderDeviceInfo_t& mode ) +{ + LOCK_SHADERAPI(); + + Assert( nAdapter < GetAdapterCount() ); + int nDXLevel = mode.m_nDXLevel != 0 ? mode.m_nDXLevel : m_Adapters[nAdapter].m_ActualCaps.m_nDXSupportLevel; + if ( m_bObeyDxCommandlineOverride ) + { + nDXLevel = CommandLine()->ParmValue( "-dxlevel", nDXLevel ); + m_bObeyDxCommandlineOverride = false; + } + if ( nDXLevel > m_Adapters[nAdapter].m_ActualCaps.m_nMaxDXSupportLevel ) + { + nDXLevel = m_Adapters[nAdapter].m_ActualCaps.m_nMaxDXSupportLevel; + } + nDXLevel = GetClosestActualDXLevel( nDXLevel ); + + if ( nDXLevel < 100 ) + { + // Fall back to the Dx9 implementations + return g_pShaderDeviceMgrDx8->SetMode( hWnd, nAdapter, mode ); + } + + if ( g_pShaderAPI ) + { + g_pShaderAPI->OnDeviceShutdown(); + g_pShaderAPI = NULL; + } + + if ( g_pShaderDevice ) + { + g_pShaderDevice->ShutdownDevice(); + g_pShaderDevice = NULL; + } + + g_pShaderShadow = NULL; + + ShaderDeviceInfo_t adjustedMode = mode; + adjustedMode.m_nDXLevel = nDXLevel; + if ( !g_pShaderDeviceDx10->InitDevice( hWnd, nAdapter, adjustedMode ) ) + return NULL; + + if ( !g_pShaderAPIDx10->OnDeviceInit() ) + return NULL; + + g_pShaderDevice = g_pShaderDeviceDx10; + g_pShaderAPI = g_pShaderAPIDx10; + g_pShaderShadow = g_pShaderShadowDx10; + + return ShaderInterfaceFactory; +} + + +//----------------------------------------------------------------------------- +// +// Device +// +//----------------------------------------------------------------------------- + + +//----------------------------------------------------------------------------- +// constructor, destructor +//----------------------------------------------------------------------------- +CShaderDeviceDx10::CShaderDeviceDx10() +{ + m_pDevice = NULL; + m_pOutput = NULL; + m_pSwapChain = NULL; + m_pRenderTargetView = NULL; +} + +CShaderDeviceDx10::~CShaderDeviceDx10() +{ +} + + +//----------------------------------------------------------------------------- +// Sets the mode +//----------------------------------------------------------------------------- +bool CShaderDeviceDx10::InitDevice( void *hWnd, int nAdapter, const ShaderDeviceInfo_t& mode ) +{ + // Make sure we've been shutdown previously + if ( m_nAdapter != -1 ) + { + Warning( "CShaderDeviceDx10::SetMode: Previous mode has not been shut down!\n" ); + return false; + } + + LOCK_SHADERAPI(); + IDXGIAdapter *pAdapter = g_ShaderDeviceMgrDx10.GetAdapter( nAdapter ); + if ( !pAdapter ) + return false; + + m_pOutput = g_ShaderDeviceMgrDx10.GetAdapterOutput( nAdapter ); + if ( !m_pOutput ) + return false; + m_pOutput->AddRef(); + + DXGI_SWAP_CHAIN_DESC sd; + ZeroMemory( &sd, sizeof(sd) ); + sd.BufferDesc.Width = mode.m_DisplayMode.m_nWidth; + sd.BufferDesc.Height = mode.m_DisplayMode.m_nHeight; + sd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; + sd.BufferDesc.RefreshRate.Numerator = mode.m_DisplayMode.m_nRefreshRateNumerator; + sd.BufferDesc.RefreshRate.Denominator = mode.m_DisplayMode.m_nRefreshRateDenominator; + sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; + sd.BufferCount = mode.m_nBackBufferCount; + sd.OutputWindow = (HWND)hWnd; + sd.Windowed = mode.m_bWindowed ? TRUE : FALSE; + sd.Flags = mode.m_bWindowed ? 0 : DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH; + + // NOTE: Having more than 1 back buffer disables MSAA! + sd.SwapEffect = mode.m_nBackBufferCount > 1 ? DXGI_SWAP_EFFECT_SEQUENTIAL : DXGI_SWAP_EFFECT_DISCARD; + + // FIXME: Chicken + egg problem with SampleDesc. + sd.SampleDesc.Count = mode.m_nAASamples ? mode.m_nAASamples : 1; + sd.SampleDesc.Quality = mode.m_nAAQuality; + + UINT nDeviceFlags = 0; +#ifdef _DEBUG + nDeviceFlags |= D3D10_CREATE_DEVICE_DEBUG; +#endif + + HRESULT hr = D3D10CreateDeviceAndSwapChain( pAdapter, D3D10_DRIVER_TYPE_HARDWARE, + NULL, nDeviceFlags, D3D10_SDK_VERSION, &sd, &m_pSwapChain, &m_pDevice ); + + if ( FAILED( hr ) ) + return false; + + // Create a render target view + ID3D10Texture2D *pBackBuffer; + hr = m_pSwapChain->GetBuffer( 0, __uuidof( ID3D10Texture2D ), (LPVOID*)&pBackBuffer ); + if ( FAILED( hr ) ) + return FALSE; + + hr = m_pDevice->CreateRenderTargetView( pBackBuffer, NULL, &m_pRenderTargetView ); + pBackBuffer->Release(); + if( FAILED( hr ) ) + return FALSE; + + m_pDevice->OMSetRenderTargets( 1, &m_pRenderTargetView, NULL ); + + m_hWnd = hWnd; + m_nAdapter = nAdapter; + + // This is our current view. + m_ViewHWnd = hWnd; + GetWindowSize( m_nWindowWidth, m_nWindowHeight ); + + g_pHardwareConfig->SetupHardwareCaps( mode, g_ShaderDeviceMgrDx10.GetHardwareCaps( nAdapter ) ); + + return true; +} + + +//----------------------------------------------------------------------------- +// Shuts down the mode +//----------------------------------------------------------------------------- +void CShaderDeviceDx10::ShutdownDevice() +{ + if ( m_pRenderTargetView ) + { + m_pRenderTargetView->Release(); + m_pRenderTargetView = NULL; + } + + if ( m_pDevice ) + { + m_pDevice->Release(); + m_pDevice = NULL; + } + + if ( m_pSwapChain ) + { + m_pSwapChain->Release(); + m_pSwapChain = NULL; + } + + if ( m_pOutput ) + { + m_pOutput->Release(); + m_pOutput = NULL; + } + + m_hWnd = NULL; + m_nAdapter = -1; +} + + +//----------------------------------------------------------------------------- +// Are we using graphics? +//----------------------------------------------------------------------------- +bool CShaderDeviceDx10::IsUsingGraphics() const +{ + return ( m_nAdapter >= 0 ); +} + + +//----------------------------------------------------------------------------- +// Returns the adapter +//----------------------------------------------------------------------------- +int CShaderDeviceDx10::GetCurrentAdapter() const +{ + return m_nAdapter; +} + + +//----------------------------------------------------------------------------- +// Get back buffer information +//----------------------------------------------------------------------------- +ImageFormat CShaderDeviceDx10::GetBackBufferFormat() const +{ + return IMAGE_FORMAT_RGB888; +} + +void CShaderDeviceDx10::GetBackBufferDimensions( int& width, int& height ) const +{ + width = 1024; + height = 768; +} + + +//----------------------------------------------------------------------------- +// Use this to spew information about the 3D layer +//----------------------------------------------------------------------------- +void CShaderDeviceDx10::SpewDriverInfo() const +{ + Warning( "Dx10 Driver!\n" ); +} + + + +//----------------------------------------------------------------------------- +// Swap buffers +//----------------------------------------------------------------------------- +void CShaderDeviceDx10::Present() +{ + // FIXME: Deal with window occlusion, alt-tab, etc. + HRESULT hr = m_pSwapChain->Present( 0, 0 ); + if ( FAILED(hr) ) + { + Assert( 0 ); + } +} + + +//----------------------------------------------------------------------------- +// Camma ramp +//----------------------------------------------------------------------------- +void CShaderDeviceDx10::SetHardwareGammaRamp( float fGamma, float fGammaTVRangeMin, float fGammaTVRangeMax, float fGammaTVExponent, bool bTVEnabled ) +{ + DevMsg( "SetHardwareGammaRamp( %f )\n", fGamma ); + + Assert( m_pOutput ); + if( !m_pOutput ) + return; + + float flMin = g_pHardwareConfig->Caps().m_flMinGammaControlPoint; + float flMax = g_pHardwareConfig->Caps().m_flMaxGammaControlPoint; + int nGammaPoints = g_pHardwareConfig->Caps().m_nGammaControlPointCount; + + DXGI_GAMMA_CONTROL gammaControl; + gammaControl.Scale.Red = gammaControl.Scale.Green = gammaControl.Scale.Blue = 1.0f; + gammaControl.Offset.Red = gammaControl.Offset.Green = gammaControl.Offset.Blue = 0.0f; + float flOOCount = 1.0f / ( nGammaPoints - 1 ); + for ( int i = 0; i < nGammaPoints; i++ ) + { + float flGamma22 = i * flOOCount; + float flCorrection = pow( flGamma22, fGamma / 2.2f ); + flCorrection = clamp( flCorrection, flMin, flMax ); + + gammaControl.GammaCurve[i].Red = flCorrection; + gammaControl.GammaCurve[i].Green = flCorrection; + gammaControl.GammaCurve[i].Blue = flCorrection; + } + + HRESULT hr = m_pOutput->SetGammaControl( &gammaControl ); + if ( FAILED(hr) ) + { + Warning( "CShaderDeviceDx10::SetHardwareGammaRamp: Unable to set gamma controls!\n" ); + } +} + + +//----------------------------------------------------------------------------- +// Compiles all manner of shaders +//----------------------------------------------------------------------------- +IShaderBuffer* CShaderDeviceDx10::CompileShader( const char *pProgram, size_t nBufLen, const char *pShaderVersion ) +{ + int nCompileFlags = D3D10_SHADER_AVOID_FLOW_CONTROL; + nCompileFlags |= D3D10_SHADER_ENABLE_BACKWARDS_COMPATIBILITY; + +#ifdef _DEBUG + nCompileFlags |= D3D10_SHADER_DEBUG; +#endif + + ID3D10Blob *pCompiledShader, *pErrorMessages; + HRESULT hr = D3DX10CompileFromMemory( pProgram, nBufLen, "", + NULL, NULL, "main", pShaderVersion, nCompileFlags, 0, NULL, + &pCompiledShader, &pErrorMessages, NULL ); + + if ( FAILED( hr ) ) + { + if ( pErrorMessages ) + { + const char *pErrorMessage = (const char *)pErrorMessages->GetBufferPointer(); + Warning( "Vertex shader compilation failed! Reported the following errors:\n%s\n", pErrorMessage ); + pErrorMessages->Release(); + } + return NULL; + } + + // NOTE: This uses small block heap allocator; so I'm not going + // to bother creating a memory pool. + CShaderBuffer< ID3D10Blob > *pShaderBuffer = new CShaderBuffer< ID3D10Blob >( pCompiledShader ); + if ( pErrorMessages ) + { + pErrorMessages->Release(); + } + + return pShaderBuffer; +} + + +//----------------------------------------------------------------------------- +// Release input layouts +//----------------------------------------------------------------------------- +void CShaderDeviceDx10::ReleaseInputLayouts( VertexShaderIndex_t nIndex ) +{ + InputLayoutDict_t &dict = m_VertexShaderDict[nIndex].m_InputLayouts; + unsigned short hCurr = dict.FirstInorder(); + while( hCurr != dict.InvalidIndex() ) + { + if ( dict[hCurr].m_pInputLayout ) + { + dict[hCurr].m_pInputLayout->Release(); + dict[hCurr].m_pInputLayout = NULL; + } + hCurr = dict.NextInorder( hCurr ); + } +} + + +//----------------------------------------------------------------------------- +// Create, destroy vertex shader +//----------------------------------------------------------------------------- +VertexShaderHandle_t CShaderDeviceDx10::CreateVertexShader( IShaderBuffer* pShaderBuffer ) +{ + // Create the vertex shader + ID3D10VertexShader *pShader = NULL; + HRESULT hr = m_pDevice->CreateVertexShader( pShaderBuffer->GetBits(), + pShaderBuffer->GetSize(), &pShader ); + + if ( FAILED( hr ) || !pShader ) + return VERTEX_SHADER_HANDLE_INVALID; + + ID3D10ShaderReflection *pInfo; + hr = D3D10ReflectShader( pShaderBuffer->GetBits(), pShaderBuffer->GetSize(), &pInfo ); + if ( FAILED( hr ) || !pInfo ) + { + pShader->Release(); + return VERTEX_SHADER_HANDLE_INVALID; + } + + // Insert the shader into the dictionary of shaders + VertexShaderIndex_t i = m_VertexShaderDict.AddToTail( ); + VertexShader_t &dict = m_VertexShaderDict[i]; + dict.m_pShader = pShader; + dict.m_pInfo = pInfo; + dict.m_nByteCodeLen = pShaderBuffer->GetSize(); + dict.m_pByteCode = new unsigned char[ dict.m_nByteCodeLen ]; + memcpy( dict.m_pByteCode, pShaderBuffer->GetBits(), dict.m_nByteCodeLen ); + return (VertexShaderHandle_t)i; +} + +void CShaderDeviceDx10::DestroyVertexShader( VertexShaderHandle_t hShader ) +{ + if ( hShader == VERTEX_SHADER_HANDLE_INVALID ) + return; + + g_pShaderAPIDx10->Unbind( hShader ); + + VertexShaderIndex_t i = (VertexShaderIndex_t)hShader; + VertexShader_t &dict = m_VertexShaderDict[i]; + VerifyEquals( dict.m_pShader->Release(), 0 ); + VerifyEquals( dict.m_pInfo->Release(), 0 ); + delete[] dict.m_pByteCode; + ReleaseInputLayouts( i ); + m_VertexShaderDict.Remove( i ); +} + + +//----------------------------------------------------------------------------- +// Create, destroy geometry shader +//----------------------------------------------------------------------------- +GeometryShaderHandle_t CShaderDeviceDx10::CreateGeometryShader( IShaderBuffer* pShaderBuffer ) +{ + // Create the geometry shader + ID3D10GeometryShader *pShader = NULL; + HRESULT hr = m_pDevice->CreateGeometryShader( pShaderBuffer->GetBits(), + pShaderBuffer->GetSize(), &pShader ); + + if ( FAILED( hr ) || !pShader ) + return GEOMETRY_SHADER_HANDLE_INVALID; + + ID3D10ShaderReflection *pInfo; + hr = D3D10ReflectShader( pShaderBuffer->GetBits(), pShaderBuffer->GetSize(), &pInfo ); + if ( FAILED( hr ) || !pInfo ) + { + pShader->Release(); + return GEOMETRY_SHADER_HANDLE_INVALID; + } + + // Insert the shader into the dictionary of shaders + GeometryShaderIndex_t i = m_GeometryShaderDict.AddToTail( ); + m_GeometryShaderDict[i].m_pShader = pShader; + m_GeometryShaderDict[i].m_pInfo = pInfo; + return (GeometryShaderHandle_t)i; +} + +void CShaderDeviceDx10::DestroyGeometryShader( GeometryShaderHandle_t hShader ) +{ + if ( hShader == GEOMETRY_SHADER_HANDLE_INVALID ) + return; + + g_pShaderAPIDx10->Unbind( hShader ); + + GeometryShaderIndex_t i = (GeometryShaderIndex_t)hShader; + VerifyEquals( m_GeometryShaderDict[ i ].m_pShader->Release(), 0 ); + VerifyEquals( m_GeometryShaderDict[ i ].m_pInfo->Release(), 0 ); + m_GeometryShaderDict.Remove( i ); +} + + +//----------------------------------------------------------------------------- +// Create, destroy pixel shader +//----------------------------------------------------------------------------- +PixelShaderHandle_t CShaderDeviceDx10::CreatePixelShader( IShaderBuffer* pShaderBuffer ) +{ + // Create the pixel shader + ID3D10PixelShader *pShader = NULL; + HRESULT hr = m_pDevice->CreatePixelShader( pShaderBuffer->GetBits(), + pShaderBuffer->GetSize(), &pShader ); + + if ( FAILED( hr ) || !pShader ) + return PIXEL_SHADER_HANDLE_INVALID; + + ID3D10ShaderReflection *pInfo; + hr = D3D10ReflectShader( pShaderBuffer->GetBits(), pShaderBuffer->GetSize(), &pInfo ); + if ( FAILED( hr ) || !pInfo ) + { + pShader->Release(); + return PIXEL_SHADER_HANDLE_INVALID; + } + + // Insert the shader into the dictionary of shaders + PixelShaderIndex_t i = m_PixelShaderDict.AddToTail( ); + m_PixelShaderDict[i].m_pShader = pShader; + m_PixelShaderDict[i].m_pInfo = pInfo; + return (PixelShaderHandle_t)i; +} + +void CShaderDeviceDx10::DestroyPixelShader( PixelShaderHandle_t hShader ) +{ + if ( hShader == PIXEL_SHADER_HANDLE_INVALID ) + return; + + g_pShaderAPIDx10->Unbind( hShader ); + + PixelShaderIndex_t i = (PixelShaderIndex_t)hShader; + VerifyEquals( m_PixelShaderDict[ i ].m_pShader->Release(), 0 ); + VerifyEquals( m_PixelShaderDict[ i ].m_pInfo->Release(), 0 ); + m_PixelShaderDict.Remove( i ); +} + + +//----------------------------------------------------------------------------- +// Finds or creates an input layout for a given vertex shader + stream format +//----------------------------------------------------------------------------- +ID3D10InputLayout* CShaderDeviceDx10::GetInputLayout( VertexShaderHandle_t hShader, VertexFormat_t format ) +{ + if ( hShader == VERTEX_SHADER_HANDLE_INVALID ) + return NULL; + + // FIXME: VertexFormat_t is not the appropriate way of specifying this + // because it has no stream information + InputLayout_t insert; + insert.m_VertexFormat = format; + + VertexShaderIndex_t i = (VertexShaderIndex_t)hShader; + InputLayoutDict_t &dict = m_VertexShaderDict[i].m_InputLayouts; + unsigned short hIndex = dict.Find( insert ); + if ( hIndex != dict.InvalidIndex() ) + return dict[hIndex].m_pInputLayout; + + VertexShader_t &shader = m_VertexShaderDict[i]; + insert.m_pInputLayout = CreateInputLayout( format, shader.m_pInfo, shader.m_pByteCode, shader.m_nByteCodeLen ); + dict.Insert( insert ); + return insert.m_pInputLayout; +} + + +//----------------------------------------------------------------------------- +// Creates/destroys Mesh +//----------------------------------------------------------------------------- +IMesh* CShaderDeviceDx10::CreateStaticMesh( VertexFormat_t vertexFormat, const char *pBudgetGroup, IMaterial * pMaterial ) +{ + LOCK_SHADERAPI(); + return NULL; +} + +void CShaderDeviceDx10::DestroyStaticMesh( IMesh* pMesh ) +{ + LOCK_SHADERAPI(); +} + + +//----------------------------------------------------------------------------- +// Creates/destroys vertex buffers + index buffers +//----------------------------------------------------------------------------- +IVertexBuffer *CShaderDeviceDx10::CreateVertexBuffer( ShaderBufferType_t type, VertexFormat_t fmt, int nVertexCount, const char *pBudgetGroup ) +{ + LOCK_SHADERAPI(); + CVertexBufferDx10 *pVertexBuffer = new CVertexBufferDx10( type, fmt, nVertexCount, pBudgetGroup ); + return pVertexBuffer; +} + +void CShaderDeviceDx10::DestroyVertexBuffer( IVertexBuffer *pVertexBuffer ) +{ + LOCK_SHADERAPI(); + if ( pVertexBuffer ) + { + CVertexBufferDx10 *pVertexBufferBase = assert_cast( pVertexBuffer ); + g_pShaderAPIDx10->UnbindVertexBuffer( pVertexBufferBase->GetDx10Buffer() ); + delete pVertexBufferBase; + } +} + +IIndexBuffer *CShaderDeviceDx10::CreateIndexBuffer( ShaderBufferType_t type, MaterialIndexFormat_t fmt, int nIndexCount, const char *pBudgetGroup ) +{ + LOCK_SHADERAPI(); + CIndexBufferDx10 *pIndexBuffer = new CIndexBufferDx10( type, fmt, nIndexCount, pBudgetGroup ); + return pIndexBuffer; +} + +void CShaderDeviceDx10::DestroyIndexBuffer( IIndexBuffer *pIndexBuffer ) +{ + LOCK_SHADERAPI(); + if ( pIndexBuffer ) + { + CIndexBufferDx10 *pIndexBufferBase = assert_cast( pIndexBuffer ); + g_pShaderAPIDx10->UnbindIndexBuffer( pIndexBufferBase->GetDx10Buffer() ); + delete pIndexBufferBase; + } +} + +IVertexBuffer *CShaderDeviceDx10::GetDynamicVertexBuffer( int nStreamID, VertexFormat_t vertexFormat, bool bBuffered ) +{ + LOCK_SHADERAPI(); + return NULL; +} + +IIndexBuffer *CShaderDeviceDx10::GetDynamicIndexBuffer( MaterialIndexFormat_t fmt, bool bBuffered ) +{ + LOCK_SHADERAPI(); + return NULL; +} + + + diff --git a/materialsystem/shaderapidx9/shaderdevicedx10.h b/materialsystem/shaderapidx9/shaderdevicedx10.h new file mode 100644 index 0000000..ecf6a8d --- /dev/null +++ b/materialsystem/shaderapidx9/shaderdevicedx10.h @@ -0,0 +1,254 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//===========================================================================// + +#ifndef SHADERDEVICEDX10_H +#define SHADERDEVICEDX10_H + +#ifdef _WIN32 +#pragma once +#endif + + +#include "shaderdevicebase.h" +#include "tier1/utlvector.h" +#include "tier1/utlrbtree.h" +#include "tier1/utllinkedlist.h" + + +//----------------------------------------------------------------------------- +// Forward declaration +//----------------------------------------------------------------------------- +struct IDXGIFactory; +struct IDXGIAdapter; +struct IDXGIOutput; +struct IDXGISwapChain; +struct ID3D10Device; +struct ID3D10RenderTargetView; +struct ID3D10VertexShader; +struct ID3D10PixelShader; +struct ID3D10GeometryShader; +struct ID3D10InputLayout; +struct ID3D10ShaderReflection; + + +//----------------------------------------------------------------------------- +// The Base implementation of the shader device +//----------------------------------------------------------------------------- +class CShaderDeviceMgrDx10 : public CShaderDeviceMgrBase +{ + typedef CShaderDeviceMgrBase BaseClass; + +public: + // constructor, destructor + CShaderDeviceMgrDx10(); + virtual ~CShaderDeviceMgrDx10(); + + // Methods of IAppSystem + virtual bool Connect( CreateInterfaceFn factory ); + virtual void Disconnect(); + virtual InitReturnVal_t Init(); + virtual void Shutdown(); + + // Methods of IShaderDeviceMgr + virtual int GetAdapterCount() const; + virtual void GetAdapterInfo( int adapter, MaterialAdapterInfo_t& info ) const; + virtual int GetModeCount( int nAdapter ) const; + virtual void GetModeInfo( ShaderDisplayMode_t* pInfo, int nAdapter, int mode ) const; + virtual void GetCurrentModeInfo( ShaderDisplayMode_t* pInfo, int nAdapter ) const; + virtual bool SetAdapter( int nAdapter, int nFlags ); + virtual CreateInterfaceFn SetMode( void *hWnd, int nAdapter, const ShaderDeviceInfo_t& mode ); + +private: + // Initialize adapter information + void InitAdapterInfo(); + + // Determines hardware caps from D3D + bool ComputeCapsFromD3D( HardwareCaps_t *pCaps, IDXGIAdapter *pAdapter, IDXGIOutput *pOutput ); + + // Returns the amount of video memory in bytes for a particular adapter + virtual int GetVidMemBytes( int nAdapter ) const; + + // Returns the appropriate adapter output to use + IDXGIOutput* GetAdapterOutput( int nAdapter ) const; + + // Returns the adapter interface for a particular adapter + IDXGIAdapter* GetAdapter( int nAdapter ) const; + + // Used to enumerate adapters, attach to windows + IDXGIFactory *m_pDXGIFactory; + + bool m_bObeyDxCommandlineOverride: 1; + + friend class CShaderDeviceDx10; +}; + + +//----------------------------------------------------------------------------- +// The Dx10 implementation of the shader device +//----------------------------------------------------------------------------- +class CShaderDeviceDx10 : public CShaderDeviceBase +{ +public: + // constructor, destructor + CShaderDeviceDx10(); + virtual ~CShaderDeviceDx10(); + +public: + // Methods of IShaderDevice + virtual bool IsUsingGraphics() const; + virtual int GetCurrentAdapter() const; + virtual ImageFormat GetBackBufferFormat() const; + virtual void GetBackBufferDimensions( int& width, int& height ) const; + virtual void SpewDriverInfo() const; + virtual void Present(); + virtual IShaderBuffer* CompileShader( const char *pProgram, size_t nBufLen, const char *pShaderVersion ); + virtual VertexShaderHandle_t CreateVertexShader( IShaderBuffer *pShader ); + virtual void DestroyVertexShader( VertexShaderHandle_t hShader ); + virtual GeometryShaderHandle_t CreateGeometryShader( IShaderBuffer* pShaderBuffer ); + virtual void DestroyGeometryShader( GeometryShaderHandle_t hShader ); + virtual PixelShaderHandle_t CreatePixelShader( IShaderBuffer* pShaderBuffer ); + virtual void DestroyPixelShader( PixelShaderHandle_t hShader ); + virtual void ReleaseResources() {} + virtual void ReacquireResources() {} + virtual IMesh* CreateStaticMesh( VertexFormat_t format, const char *pTextureBudgetGroup, IMaterial * pMaterial ); + virtual void DestroyStaticMesh( IMesh* mesh ); + virtual IVertexBuffer *CreateVertexBuffer( ShaderBufferType_t type, VertexFormat_t fmt, int nVertexCount, const char *pTextureBudgetGroup ); + virtual void DestroyVertexBuffer( IVertexBuffer *pVertexBuffer ); + virtual IIndexBuffer *CreateIndexBuffer( ShaderBufferType_t type, MaterialIndexFormat_t fmt, int nIndexCount, const char *pTextureBudgetGroup ); + virtual void DestroyIndexBuffer( IIndexBuffer *pIndexBuffer ); + virtual IVertexBuffer *GetDynamicVertexBuffer( int nStreamID, VertexFormat_t vertexFormat, bool bBuffered = true ); + virtual IIndexBuffer *GetDynamicIndexBuffer( MaterialIndexFormat_t fmt, bool bBuffered = true ); + virtual void SetHardwareGammaRamp( float fGamma, float fGammaTVRangeMin, float fGammaTVRangeMax, float fGammaTVExponent, bool bTVEnabled ); + + // A special path used to tick the front buffer while loading on the 360 + virtual void EnableNonInteractiveMode( MaterialNonInteractiveMode_t mode, ShaderNonInteractiveInfo_t *pInfo ) {} + virtual void RefreshFrontBufferNonInteractive( ) {} + virtual void HandleThreadEvent( uint32 threadEvent ) {} + +public: + // Methods of CShaderDeviceBase + virtual bool InitDevice( void *hWnd, int nAdapter, const ShaderDeviceInfo_t& mode ); + virtual void ShutdownDevice(); + virtual bool IsDeactivated() const { return false; } + + // Other public methods + ID3D10VertexShader* GetVertexShader( VertexShaderHandle_t hShader ) const; + ID3D10GeometryShader* GetGeometryShader( GeometryShaderHandle_t hShader ) const; + ID3D10PixelShader* GetPixelShader( PixelShaderHandle_t hShader ) const; + ID3D10InputLayout* GetInputLayout( VertexShaderHandle_t hShader, VertexFormat_t format ); + +private: + struct InputLayout_t + { + ID3D10InputLayout *m_pInputLayout; + VertexFormat_t m_VertexFormat; + }; + + typedef CUtlRBTree< InputLayout_t, unsigned short > InputLayoutDict_t; + + static bool InputLayoutLessFunc( const InputLayout_t &lhs, const InputLayout_t &rhs ) + { + return ( lhs.m_VertexFormat < rhs.m_VertexFormat ); + } + + struct VertexShader_t + { + ID3D10VertexShader *m_pShader; + ID3D10ShaderReflection *m_pInfo; + void *m_pByteCode; + size_t m_nByteCodeLen; + InputLayoutDict_t m_InputLayouts; + + VertexShader_t() : m_InputLayouts( 0, 0, InputLayoutLessFunc ) {} + }; + + struct GeometryShader_t + { + ID3D10GeometryShader *m_pShader; + ID3D10ShaderReflection *m_pInfo; + }; + + struct PixelShader_t + { + ID3D10PixelShader *m_pShader; + ID3D10ShaderReflection *m_pInfo; + }; + + typedef CUtlFixedLinkedList< VertexShader_t >::IndexType_t VertexShaderIndex_t; + typedef CUtlFixedLinkedList< GeometryShader_t >::IndexType_t GeometryShaderIndex_t; + typedef CUtlFixedLinkedList< PixelShader_t >::IndexType_t PixelShaderIndex_t; + + void SetupHardwareCaps(); + void ReleaseInputLayouts( VertexShaderIndex_t nIndex ); + + IDXGIOutput *m_pOutput; + ID3D10Device *m_pDevice; + IDXGISwapChain *m_pSwapChain; + ID3D10RenderTargetView *m_pRenderTargetView; + + CUtlFixedLinkedList< VertexShader_t > m_VertexShaderDict; + CUtlFixedLinkedList< GeometryShader_t > m_GeometryShaderDict; + CUtlFixedLinkedList< PixelShader_t > m_PixelShaderDict; + + friend ID3D10Device *D3D10Device(); + friend IDXGISwapChain *D3D10SwapChain(); + friend ID3D10RenderTargetView *D3D10RenderTargetView(); +}; + + +//----------------------------------------------------------------------------- +// Inline methods of CShaderDeviceDx10 +//----------------------------------------------------------------------------- +inline ID3D10VertexShader* CShaderDeviceDx10::GetVertexShader( VertexShaderHandle_t hShader ) const +{ + if ( hShader != VERTEX_SHADER_HANDLE_INVALID ) + return m_VertexShaderDict[ (VertexShaderIndex_t)hShader ].m_pShader; + return NULL; +} + +inline ID3D10GeometryShader* CShaderDeviceDx10::GetGeometryShader( GeometryShaderHandle_t hShader ) const +{ + if ( hShader != GEOMETRY_SHADER_HANDLE_INVALID ) + return m_GeometryShaderDict[ (GeometryShaderIndex_t)hShader ].m_pShader; + return NULL; +} + +inline ID3D10PixelShader* CShaderDeviceDx10::GetPixelShader( PixelShaderHandle_t hShader ) const +{ + if ( hShader != PIXEL_SHADER_HANDLE_INVALID ) + return m_PixelShaderDict[ (PixelShaderIndex_t)hShader ].m_pShader; + return NULL; +} + + +//----------------------------------------------------------------------------- +// Singleton +//----------------------------------------------------------------------------- +extern CShaderDeviceDx10* g_pShaderDeviceDx10; + + +//----------------------------------------------------------------------------- +// Utility methods +//----------------------------------------------------------------------------- +inline ID3D10Device *D3D10Device() +{ + return g_pShaderDeviceDx10->m_pDevice; +} + +inline IDXGISwapChain *D3D10SwapChain() +{ + return g_pShaderDeviceDx10->m_pSwapChain; +} + +inline ID3D10RenderTargetView *D3D10RenderTargetView() +{ + return g_pShaderDeviceDx10->m_pRenderTargetView; +} + + +#endif // SHADERDEVICEDX10_H \ No newline at end of file diff --git a/materialsystem/shaderapidx9/shaderdevicedx8.cpp b/materialsystem/shaderapidx9/shaderdevicedx8.cpp new file mode 100644 index 0000000..53c27b7 --- /dev/null +++ b/materialsystem/shaderapidx9/shaderdevicedx8.cpp @@ -0,0 +1,3707 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//===========================================================================// +#define DISABLE_PROTECTED_THINGS +#include "locald3dtypes.h" + +#include "shaderdevicedx8.h" +#include "shaderapi/ishaderutil.h" +#include "shaderapidx8_global.h" +#include "filesystem.h" +#include "tier0/icommandline.h" +#include "tier2/tier2.h" +#include "shadershadowdx8.h" +#include "colorformatdx8.h" +#include "materialsystem/IShader.h" +#include "shaderapidx8.h" +#include "shaderapidx8_global.h" +#include "imeshdx8.h" +#include "materialsystem/materialsystem_config.h" +#include "vertexshaderdx8.h" +#include "recording.h" +#include "winutils.h" +#include "tier0/vprof_telemetry.h" + +#if defined ( DX_TO_GL_ABSTRACTION ) +// Placed here so inlines placed in dxabstract.h can access gGL +COpenGLEntryPoints *gGL = NULL; +#endif + +#define D3D_BATCH_PERF_ANALYSIS 0 + +#if D3D_BATCH_PERF_ANALYSIS +#if defined( DX_TO_GL_ABSTRACTION ) +#error Cannot enable D3D_BATCH_PERF_ANALYSIS when using DX_TO_GL_ABSTRACTION, use GL_BATCH_PERF_ANALYSIS instead. +#endif +// Define this if you want all d3d9 interfaces hooked and run through the dx9hook.h shim interfaces. For profiling, etc. +#define DO_DX9_HOOK +#endif + +#ifdef DO_DX9_HOOK + +#if D3D_BATCH_PERF_ANALYSIS +ConVar d3d_batch_vis( "d3d_batch_vis", "0" ); +ConVar d3d_batch_vis_abs_scale( "d3d_batch_vis_abs_scale", ".050" ); +ConVar d3d_present_vis_abs_scale( "d3d_batch_vis_abs_scale", ".050" ); +ConVar d3d_batch_vis_y_scale( "d3d_batch_vis_y_scale", "0.0" ); +uint64 g_nTotalD3DCalls, g_nTotalD3DCycles; +static double s_rdtsc_to_ms; +#endif + +#include "dx9hook.h" +#endif + +#ifndef _X360 +#include "wmi.h" +#endif + +#if defined( _X360 ) +#include "xbox/xbox_console.h" +#include "xbox/xbox_win32stubs.h" +#endif + + +//#define DX8_COMPATABILITY_MODE + +//----------------------------------------------------------------------------- +// Globals +//----------------------------------------------------------------------------- +static CShaderDeviceMgrDx8 g_ShaderDeviceMgrDx8; +CShaderDeviceMgrDx8* g_pShaderDeviceMgrDx8 = &g_ShaderDeviceMgrDx8; + +#ifndef SHADERAPIDX10 + +// In the shaderapidx10.dll, we use its version of IShaderDeviceMgr. +EXPOSE_SINGLE_INTERFACE_GLOBALVAR( CShaderDeviceMgrDx8, IShaderDeviceMgr, + SHADER_DEVICE_MGR_INTERFACE_VERSION, g_ShaderDeviceMgrDx8 ) + +#endif + +#if defined( _X360 ) +IDirect3D9 *m_pD3D; +#endif + +IDirect3DDevice *g_pD3DDevice = NULL; + +#if defined(IS_WINDOWS_PC) && defined(SHADERAPIDX9) +// HACK: need to pass knowledge of D3D9Ex usage into callers of D3D Create* methods +// so they do not try to specify D3DPOOL_MANAGED, which is unsupported in D3D9Ex +bool g_ShaderDeviceUsingD3D9Ex = false; +static ConVar mat_supports_d3d9ex( "mat_supports_d3d9ex", "0", FCVAR_HIDDEN ); +#endif + +// hook into mat_forcedynamic from the engine. +static ConVar mat_forcedynamic( "mat_forcedynamic", "0", FCVAR_CHEAT ); + +// this is hooked into the engines convar +ConVar mat_debugalttab( "mat_debugalttab", "0", FCVAR_CHEAT ); + + +//----------------------------------------------------------------------------- +// +// Device manager +// +//----------------------------------------------------------------------------- + + +//----------------------------------------------------------------------------- +// constructor, destructor +//----------------------------------------------------------------------------- +CShaderDeviceMgrDx8::CShaderDeviceMgrDx8() +{ + m_pD3D = NULL; + m_bObeyDxCommandlineOverride = true; + m_bAdapterInfoIntialized = false; + +#if defined( PIX_INSTRUMENTATION ) && defined ( DX_TO_GL_ABSTRACTION ) && defined( _WIN32 ) + m_hD3D9 = NULL; + m_pBeginEvent = NULL; + m_pEndEvent = NULL; + m_pSetMarker = NULL; + m_pSetOptions = NULL; +#endif +} + +CShaderDeviceMgrDx8::~CShaderDeviceMgrDx8() +{ +} + +#ifdef OSX +#include +#endif +//----------------------------------------------------------------------------- +// Connect, disconnect +//----------------------------------------------------------------------------- +bool CShaderDeviceMgrDx8::Connect( CreateInterfaceFn factory ) +{ + LOCK_SHADERAPI(); + + if ( !BaseClass::Connect( factory ) ) + return false; + +#if defined ( DX_TO_GL_ABSTRACTION ) + gGL = ToGLConnectLibraries( factory ); +#endif + +#if defined(IS_WINDOWS_PC) && defined(SHADERAPIDX9) && !defined(RECORDING) && !defined( DX_TO_GL_ABSTRACTION ) + m_pD3D = NULL; + + // Attempt to create a D3D9Ex device (Windows Vista and later) if possible + bool bD3D9ExForceDisable = ( CommandLine()->FindParm( "-nod3d9ex" ) != 0 ) || + ( CommandLine()->ParmValue( "-dxlevel", 95 ) < 90 ); + + bool bD3D9ExAvailable = false; + if ( HMODULE hMod = ::LoadLibraryA( "d3d9.dll" ) ) + { + typedef HRESULT ( WINAPI *CreateD3D9ExFunc_t )( UINT, IUnknown** ); + if ( CreateD3D9ExFunc_t pfnCreateD3D9Ex = (CreateD3D9ExFunc_t) ::GetProcAddress( hMod, "Direct3DCreate9Ex" ) ) + { + IUnknown *pD3D9Ex = NULL; + if ( (*pfnCreateD3D9Ex)( D3D_SDK_VERSION, &pD3D9Ex ) == S_OK && pD3D9Ex ) + { + bD3D9ExAvailable = true; + if ( bD3D9ExForceDisable ) + { + pD3D9Ex->Release(); + } + else + { + g_ShaderDeviceUsingD3D9Ex = true; + // The following is more "correct" but incompatible with the Steam overlay: + //pD3D9Ex->QueryInterface( IID_IDirect3D9, (void**) &m_pD3D ); + //pD3D9Ex->Release(); + m_pD3D = static_cast< IDirect3D9* >( pD3D9Ex ); + } + } + } + ::FreeLibrary( hMod ); + } + + if ( !m_pD3D ) + { + g_ShaderDeviceUsingD3D9Ex = false; + m_pD3D = Direct3DCreate9(D3D_SDK_VERSION); + } + + mat_supports_d3d9ex.SetValue( bD3D9ExAvailable ? 1 : 0 ); +#else + #if defined( DO_DX9_HOOK ) + m_pD3D = Direct3DCreate9Hook(D3D_SDK_VERSION); + #else + m_pD3D = Direct3DCreate9(D3D_SDK_VERSION); + #endif +#endif + + if ( !m_pD3D ) + { + Warning( "Failed to create D3D9!\n" ); + return false; + } + +#if defined( PIX_INSTRUMENTATION ) && defined ( DX_TO_GL_ABSTRACTION ) && defined( _WIN32 ) + // This is a little odd, but AMD PerfStudio hooks D3D9.DLL and intercepts all of the D3DPERF API's (even for OpenGL apps). + // So dynamically load d3d9.dll and get the address of these exported functions. + if ( !m_hD3D9 ) + { + m_hD3D9 = LoadLibraryA("d3d9.dll"); + } + if ( m_hD3D9 ) + { + Plat_DebugString( "PIX_INSTRUMENTATION: Loaded d3d9.dll\n" ); + printf( "PIX_INSTRUMENTATION: Loaded d3d9.dll\n" ); + + m_pBeginEvent = (D3DPERF_BeginEvent_FuncPtr)GetProcAddress( m_hD3D9, "D3DPERF_BeginEvent" ); + m_pEndEvent = (D3DPERF_EndEvent_FuncPtr)GetProcAddress( m_hD3D9, "D3DPERF_EndEvent" ); + m_pSetMarker = (D3DPERF_SetMarker_FuncPtr)GetProcAddress( m_hD3D9, "D3DPERF_SetOptions" ); + m_pSetOptions = (D3DPERF_SetOptions_FuncPtr)GetProcAddress( m_hD3D9, "D3DPERF_SetMarker" ); + } +#endif + + // FIXME: Want this to be here, but we can't because Steam + // hasn't had it's application ID set up yet. + +// InitAdapterInfo(); + return true; +} + +void CShaderDeviceMgrDx8::Disconnect() +{ + LOCK_SHADERAPI(); + +#if defined( PIX_INSTRUMENTATION ) && defined ( DX_TO_GL_ABSTRACTION ) && defined( _WIN32 ) + if ( m_hD3D9 ) + { + m_pBeginEvent = NULL; + m_pEndEvent = NULL; + m_pSetMarker = NULL; + m_pSetOptions = NULL; + + FreeLibrary( m_hD3D9 ); + m_hD3D9 = NULL; + } +#endif + + if ( m_pD3D ) + { + m_pD3D->Release(); + m_pD3D = 0; + } + +#if defined ( DX_TO_GL_ABSTRACTION ) + ToGLDisconnectLibraries(); +#endif + + BaseClass::Disconnect(); +} + + + +//----------------------------------------------------------------------------- +// Initialization +//----------------------------------------------------------------------------- +InitReturnVal_t CShaderDeviceMgrDx8::Init( ) +{ + // FIXME: Remove call to InitAdapterInfo once Steam startup issues are resolved. + // Do it in Connect instead. + InitAdapterInfo(); + + return INIT_OK; +} + + +//----------------------------------------------------------------------------- +// Shutdown +//----------------------------------------------------------------------------- +void CShaderDeviceMgrDx8::Shutdown( ) +{ + LOCK_SHADERAPI(); + +// FIXME: Make PIX work + +// BeginPIXEvent( PIX_VALVE_ORANGE, "Shutdown" ); + + if ( g_pShaderAPI ) + { + g_pShaderAPI->OnDeviceShutdown(); + } + + if ( g_pShaderDevice ) + { + g_pShaderDevice->ShutdownDevice(); + g_pMaterialSystemHardwareConfig = NULL; + } + +// EndPIXEvent(); + + +} + + + +//----------------------------------------------------------------------------- +// Inline methods +//----------------------------------------------------------------------------- + + +//----------------------------------------------------------------------------- +// Initialize adapter information +//----------------------------------------------------------------------------- +void CShaderDeviceMgrDx8::InitAdapterInfo() +{ + if ( m_bAdapterInfoIntialized ) + return; + + m_bAdapterInfoIntialized = true; + m_Adapters.RemoveAll(); + + Assert(m_pD3D); + int nCount = m_pD3D->GetAdapterCount( ); + for( int i = 0; i < nCount; ++i ) + { + int j = m_Adapters.AddToTail(); + AdapterInfo_t &info = m_Adapters[j]; + +#ifdef _DEBUG + memset( &info.m_ActualCaps, 0xDD, sizeof(info.m_ActualCaps) ); +#endif + + info.m_ActualCaps.m_bDeviceOk = ComputeCapsFromD3D( &info.m_ActualCaps, i ); + if ( !info.m_ActualCaps.m_bDeviceOk ) + continue; + + ReadDXSupportLevels( info.m_ActualCaps ); + + // Read dxsupport.cfg which has config overrides for particular cards. + ReadHardwareCaps( info.m_ActualCaps, info.m_ActualCaps.m_nMaxDXSupportLevel ); + + // What's in "-shader" overrides dxsupport.cfg + const char *pShaderParam = CommandLine()->ParmValue( "-shader" ); + if ( pShaderParam ) + { + Q_strncpy( info.m_ActualCaps.m_pShaderDLL, pShaderParam, sizeof( info.m_ActualCaps.m_pShaderDLL ) ); + } + } +} + +//-------------------------------------------------------------------------------- +// Code to detect support for texture border color (widely supported but the caps +// bit is messed up in drivers due to a stupid WHQL test that requires this to work +// with float textures which we don't generally care about wrt this address mode) +//-------------------------------------------------------------------------------- +void CShaderDeviceMgrDx8::CheckBorderColorSupport( HardwareCaps_t *pCaps, int nAdapter ) +{ +#ifdef DX_TO_GL_ABSTRACTION + if( true ) +#else + if( IsX360() ) +#endif + { + pCaps->m_bSupportsBorderColor = true; + } + else // Most PC parts do this, but let's not deal with that yet (JasonM) + { + pCaps->m_bSupportsBorderColor = false; + } +} + +//-------------------------------------------------------------------------------- +// Vendor-dependent code to detect support for various flavors of shadow mapping +//-------------------------------------------------------------------------------- +void CShaderDeviceMgrDx8::CheckVendorDependentShadowMappingSupport( HardwareCaps_t *pCaps, int nAdapter ) +{ + // Set a default null texture format...may be overridden below by IHV-specific surface type + pCaps->m_NullTextureFormat = IMAGE_FORMAT_ARGB8888; + if ( m_pD3D->CheckDeviceFormat( nAdapter, DX8_DEVTYPE, D3DFMT_X8R8G8B8, D3DUSAGE_RENDERTARGET, D3DRTYPE_TEXTURE, D3DFMT_R5G6B5 ) == S_OK ) + { + pCaps->m_NullTextureFormat = IMAGE_FORMAT_RGB565; + } + +#if defined( _X360 ) + pCaps->m_ShadowDepthTextureFormat = ReverseDepthOnX360() ? IMAGE_FORMAT_X360_DST24F : IMAGE_FORMAT_X360_DST24; + pCaps->m_bSupportsShadowDepthTextures = true; + pCaps->m_bSupportsFetch4 = false; + return; +#elif defined ( DX_TO_GL_ABSTRACTION ) + // We may want to only do this on the higher-end Mac SKUs, since it's not free... + pCaps->m_ShadowDepthTextureFormat = IMAGE_FORMAT_NV_DST16; // This format shunts us down the right shader combo path + + pCaps->m_bSupportsShadowDepthTextures = true; + + pCaps->m_bSupportsFetch4 = false; + return; +#endif + + if ( IsPC() || !IsX360() ) + { + bool bToolsMode = IsWindows() && ( CommandLine()->CheckParm( "-tools" ) != NULL ); + bool bFound16Bit = false; + + if ( ( pCaps->m_VendorID == VENDORID_NVIDIA ) && ( pCaps->m_SupportsShaderModel_3_0 ) ) // ps_3_0 parts from nVidia + { + // First, test for null texture support + if ( m_pD3D->CheckDeviceFormat( nAdapter, DX8_DEVTYPE, D3DFMT_X8R8G8B8, D3DUSAGE_RENDERTARGET, D3DRTYPE_TEXTURE, NVFMT_NULL ) == S_OK ) + { + pCaps->m_NullTextureFormat = IMAGE_FORMAT_NV_NULL; + } + + // + // NVIDIA has two no-PCF formats (these are not filtering modes, but surface formats + // NVFMT_RAWZ is supported by NV4x (not supported here yet...requires a dp3 to reconstruct in shader code, which doesn't seem to work) + // NVFMT_INTZ is supported on newer chips as of G8x (just read like ATI non-fetch4 mode) + // +/* + if ( m_pD3D->CheckDeviceFormat( nAdapter, DX8_DEVTYPE, D3DFMT_X8R8G8B8, D3DUSAGE_RENDERTARGET, D3DRTYPE_TEXTURE, NVFMT_INTZ ) == S_OK ) + { + pCaps->m_ShadowDepthTextureFormat = IMAGE_FORMAT_NV_INTZ; + pCaps->m_bSupportsFetch4 = false; + pCaps->m_bSupportsShadowDepthTextures = true; + return; + } +*/ + if ( m_pD3D->CheckDeviceFormat( nAdapter, DX8_DEVTYPE, D3DFMT_X8R8G8B8, D3DUSAGE_DEPTHSTENCIL, D3DRTYPE_TEXTURE, D3DFMT_D16 ) == S_OK ) + { + pCaps->m_ShadowDepthTextureFormat = IMAGE_FORMAT_NV_DST16; + pCaps->m_bSupportsFetch4 = false; + pCaps->m_bSupportsShadowDepthTextures = true; + bFound16Bit = true; + + if ( !bToolsMode ) // Tools will continue on and try for 24 bit... + return; + } + + if ( m_pD3D->CheckDeviceFormat( nAdapter, DX8_DEVTYPE, D3DFMT_X8R8G8B8, D3DUSAGE_DEPTHSTENCIL, D3DRTYPE_TEXTURE, D3DFMT_D24S8 ) == S_OK ) + { + pCaps->m_ShadowDepthTextureFormat = IMAGE_FORMAT_NV_DST24; + pCaps->m_bSupportsFetch4 = false; + pCaps->m_bSupportsShadowDepthTextures = true; + return; + } + + if ( bFound16Bit ) // Found 16 bit but not 24 + return; + } + else if ( ( pCaps->m_VendorID == VENDORID_ATI ) && pCaps->m_SupportsPixelShaders_2_b ) // ps_2_b parts from ATI + { + // Initially, check for Fetch4 (tied to ATIFMT_D24S8 support) + pCaps->m_bSupportsFetch4 = false; + if ( m_pD3D->CheckDeviceFormat( nAdapter, DX8_DEVTYPE, D3DFMT_X8R8G8B8, D3DUSAGE_DEPTHSTENCIL, D3DRTYPE_TEXTURE, ATIFMT_D24S8 ) == S_OK ) + { + pCaps->m_bSupportsFetch4 = true; + } + + if ( m_pD3D->CheckDeviceFormat( nAdapter, DX8_DEVTYPE, D3DFMT_X8R8G8B8, D3DUSAGE_DEPTHSTENCIL, D3DRTYPE_TEXTURE, ATIFMT_D16 ) == S_OK ) // Prefer 16-bit + { + pCaps->m_ShadowDepthTextureFormat = IMAGE_FORMAT_ATI_DST16; + pCaps->m_bSupportsShadowDepthTextures = true; + bFound16Bit = true; + + if ( !bToolsMode ) // Tools will continue on and try for 24 bit... + return; + } + + if ( m_pD3D->CheckDeviceFormat( nAdapter, DX8_DEVTYPE, D3DFMT_X8R8G8B8, D3DUSAGE_DEPTHSTENCIL, D3DRTYPE_TEXTURE, ATIFMT_D24S8 ) == S_OK ) + { + pCaps->m_ShadowDepthTextureFormat = IMAGE_FORMAT_ATI_DST24; + pCaps->m_bSupportsShadowDepthTextures = true; + return; + } + + if ( bFound16Bit ) // Found 16 bit but not 24 + return; + } + } + + // Other vendor or old hardware + pCaps->m_ShadowDepthTextureFormat = IMAGE_FORMAT_UNKNOWN; + pCaps->m_bSupportsShadowDepthTextures = false; + pCaps->m_bSupportsFetch4 = false; +} + + +//----------------------------------------------------------------------------- +// Vendor-dependent code to detect Alpha To Coverage Backdoors +//----------------------------------------------------------------------------- +void CShaderDeviceMgrDx8::CheckVendorDependentAlphaToCoverage( HardwareCaps_t *pCaps, int nAdapter ) +{ + pCaps->m_bSupportsAlphaToCoverage = false; + + // Bail out on OpenGL +#ifdef DX_TO_GL_ABSTRACTION + pCaps->m_bSupportsAlphaToCoverage = true; + pCaps->m_AlphaToCoverageEnableValue = TRUE; + pCaps->m_AlphaToCoverageDisableValue = FALSE; + pCaps->m_AlphaToCoverageState = D3DRS_ADAPTIVETESS_Y; // Just match the NVIDIA state hackery + return; +#endif + + if ( pCaps->m_nDXSupportLevel < 90 ) + return; + +#ifdef _X360 + { + pCaps->m_bSupportsAlphaToCoverage = true; + pCaps->m_AlphaToCoverageEnableValue = TRUE; + pCaps->m_AlphaToCoverageDisableValue = FALSE; + pCaps->m_AlphaToCoverageState = D3DRS_ALPHATOMASKENABLE; + return; + } +#endif // _X360 + + if ( pCaps->m_VendorID == VENDORID_NVIDIA ) + { + // nVidia has two modes...assume SSAA is superior to MSAA and hence more desirable (though it's probably not) + // + // Currently, they only seem to expose any of this on 7800 and up though older parts certainly + // support at least the MSAA mode since they support it on OpenGL via the arb_multisample extension + bool bNVIDIA_MSAA = false; + bool bNVIDIA_SSAA = false; + + if ( m_pD3D->CheckDeviceFormat( nAdapter, DX8_DEVTYPE, // Check MSAA version + D3DFMT_X8R8G8B8, 0, D3DRTYPE_SURFACE, + (D3DFORMAT)MAKEFOURCC('A', 'T', 'O', 'C')) == S_OK ) + { + bNVIDIA_MSAA = true; + } + + if ( m_pD3D->CheckDeviceFormat( nAdapter, DX8_DEVTYPE, // Check SSAA version + D3DFMT_X8R8G8B8, 0, D3DRTYPE_SURFACE, + (D3DFORMAT)MAKEFOURCC('S', 'S', 'A', 'A')) == S_OK ) + { + bNVIDIA_SSAA = true; + } + + // nVidia pitches SSAA but we prefer ATOC + if ( bNVIDIA_MSAA )// || bNVIDIA_SSAA ) + { + // if ( bNVIDIA_SSAA ) + // m_AlphaToCoverageEnableValue = MAKEFOURCC('S', 'S', 'A', 'A'); + // else + pCaps->m_AlphaToCoverageEnableValue = MAKEFOURCC('A', 'T', 'O', 'C'); + + pCaps->m_AlphaToCoverageState = D3DRS_ADAPTIVETESS_Y; + pCaps->m_AlphaToCoverageDisableValue = (DWORD)D3DFMT_UNKNOWN; + pCaps->m_bSupportsAlphaToCoverage = true; + return; + } + } + else if ( pCaps->m_VendorID == VENDORID_ATI ) + { + // Supported on all ATI parts...just go ahead and set the state when appropriate + pCaps->m_AlphaToCoverageState = D3DRS_POINTSIZE; + pCaps->m_AlphaToCoverageEnableValue = MAKEFOURCC('A','2','M','1'); + pCaps->m_AlphaToCoverageDisableValue = MAKEFOURCC('A','2','M','0'); + pCaps->m_bSupportsAlphaToCoverage = true; + return; + } +} + +ConVar mat_hdr_level( "mat_hdr_level", "2", FCVAR_ARCHIVE ); +ConVar mat_slopescaledepthbias_shadowmap( "mat_slopescaledepthbias_shadowmap", "16", FCVAR_CHEAT ); +#ifdef DX_TO_GL_ABSTRACTION +ConVar mat_depthbias_shadowmap( "mat_depthbias_shadowmap", "20", FCVAR_CHEAT | FCVAR_DEVELOPMENTONLY ); +#else +ConVar mat_depthbias_shadowmap( "mat_depthbias_shadowmap", "0.0005", FCVAR_CHEAT ); +#endif + +// For testing Fast Clip +ConVar mat_fastclip( "mat_fastclip", "0", FCVAR_CHEAT ); + +//----------------------------------------------------------------------------- +// Determine capabilities +//----------------------------------------------------------------------------- +bool CShaderDeviceMgrDx8::ComputeCapsFromD3D( HardwareCaps_t *pCaps, int nAdapter ) +{ + D3DCAPS caps; + D3DADAPTER_IDENTIFIER9 ident; + HRESULT hr; + + // NOTE: When getting the caps, we want to be limited by the hardware + // even if we're running with software T&L... + hr = m_pD3D->GetDeviceCaps( nAdapter, DX8_DEVTYPE, &caps ); + if ( FAILED( hr ) ) + return false; + + hr = m_pD3D->GetAdapterIdentifier( nAdapter, D3DENUM_WHQL_LEVEL, &ident ); + if ( FAILED( hr ) ) + return false; + + if ( IsOpenGL() ) + { + if ( !ident.DeviceId && !ident.VendorId ) + { + ident.DeviceId = 1; // fake default device/vendor ID for OpenGL + ident.VendorId = 1; + } + } + + // Intended for debugging only + if ( CommandLine()->CheckParm( "-force_device_id" ) ) + { + const char *pDevID = CommandLine()->ParmValue( "-force_device_id", "" ); + if ( pDevID ) + { + int nDevID = V_atoi( pDevID ); // use V_atoi for hex support + if ( nDevID > 0 ) + { + ident.DeviceId = nDevID; + } + } + } + + // Intended for debugging only + if ( CommandLine()->CheckParm( "-force_vendor_id" ) ) + { + const char *pVendorID = CommandLine()->ParmValue( "-force_vendor_id", "" ); + if ( pVendorID ) + { + int nVendorID = V_atoi( pVendorID ); // use V_atoi for hex support + if ( pVendorID > 0 ) + { + ident.VendorId = nVendorID; + } + } + } + + Q_strncpy( pCaps->m_pDriverName, ident.Description, MATERIAL_ADAPTER_NAME_LENGTH ); + pCaps->m_VendorID = ident.VendorId; + pCaps->m_DeviceID = ident.DeviceId; + pCaps->m_SubSysID = ident.SubSysId; + pCaps->m_Revision = ident.Revision; + + pCaps->m_nDriverVersionHigh = ident.DriverVersion.HighPart; + pCaps->m_nDriverVersionLow = ident.DriverVersion.LowPart; + + pCaps->m_pShaderDLL[0] = 0; + pCaps->m_nMaxViewports = 1; + + pCaps->m_PreferDynamicTextures = ( caps.Caps2 & D3DCAPS2_DYNAMICTEXTURES ) ? 1 : 0; + + pCaps->m_HasProjectedBumpEnv = ( caps.TextureCaps & D3DPTEXTURECAPS_NOPROJECTEDBUMPENV ) == 0; + + pCaps->m_HasSetDeviceGammaRamp = (caps.Caps2 & D3DCAPS2_CANCALIBRATEGAMMA) != 0; + pCaps->m_SupportsVertexShaders = ((caps.VertexShaderVersion >> 8) & 0xFF) >= 1; + pCaps->m_SupportsPixelShaders = ((caps.PixelShaderVersion >> 8) & 0xFF) >= 1; + + pCaps->m_bScissorSupported = ( caps.RasterCaps & D3DPRASTERCAPS_SCISSORTEST ) != 0; + +#if defined( DX8_COMPATABILITY_MODE ) + pCaps->m_SupportsPixelShaders_1_4 = false; + pCaps->m_SupportsPixelShaders_2_0 = false; + pCaps->m_SupportsPixelShaders_2_b = false; + pCaps->m_SupportsVertexShaders_2_0 = false; + pCaps->m_SupportsShaderModel_3_0 = false; + pCaps->m_SupportsMipmappedCubemaps = false; +#else + pCaps->m_SupportsPixelShaders_1_4 = ( caps.PixelShaderVersion & 0xffff ) >= 0x0104; + pCaps->m_SupportsPixelShaders_2_0 = ( caps.PixelShaderVersion & 0xffff ) >= 0x0200; + pCaps->m_SupportsPixelShaders_2_b = ( ( caps.PixelShaderVersion & 0xffff ) >= 0x0200) && (caps.PS20Caps.NumInstructionSlots >= 512); // More caps to this, but this will do + pCaps->m_SupportsVertexShaders_2_0 = ( caps.VertexShaderVersion & 0xffff ) >= 0x0200; + pCaps->m_SupportsShaderModel_3_0 = ( caps.PixelShaderVersion & 0xffff ) >= 0x0300; + pCaps->m_SupportsMipmappedCubemaps = ( caps.TextureCaps & D3DPTEXTURECAPS_MIPCUBEMAP ) ? true : false; +#endif + + // Slam this off for OpenGL + if ( IsOpenGL() ) + { + pCaps->m_SupportsShaderModel_3_0 = false; + } + + // Slam 3.0 shaders off for Intel + if ( pCaps->m_VendorID == VENDORID_INTEL ) + { + pCaps->m_SupportsShaderModel_3_0 = false; + } + + pCaps->m_MaxVertexShader30InstructionSlots = 0; + pCaps->m_MaxPixelShader30InstructionSlots = 0; + + if ( pCaps->m_SupportsShaderModel_3_0 ) + { + pCaps->m_MaxVertexShader30InstructionSlots = caps.MaxVertexShader30InstructionSlots; + pCaps->m_MaxPixelShader30InstructionSlots = caps.MaxPixelShader30InstructionSlots; + } + + if( CommandLine()->CheckParm( "-nops2b" ) ) + { + pCaps->m_SupportsPixelShaders_2_b = false; + } + + pCaps->m_bSoftwareVertexProcessing = false; + if ( IsWindows() && CommandLine()->CheckParm( "-mat_softwaretl" ) ) + { + pCaps->m_bSoftwareVertexProcessing = true; + } + + if ( IsWindows() && !( caps.DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT ) ) + { + // no hardware t&l. . use software + pCaps->m_bSoftwareVertexProcessing = true; + } + + // Set mat_forcedynamic if software vertex processing since the software vp pipe has + // problems with sparse vertex buffers (it transforms the whole thing.) + if ( pCaps->m_bSoftwareVertexProcessing ) + { + mat_forcedynamic.SetValue( 1 ); + } + + if ( pCaps->m_bSoftwareVertexProcessing ) + { + pCaps->m_SupportsVertexShaders = true; + pCaps->m_SupportsVertexShaders_2_0 = true; + } + +#ifdef OSX + // Static control flow is disabled by default on OSX (the Mac version of togl has known bugs preventing this path from working properly that we've fixed in togl linux/win) + pCaps->m_bSupportsStaticControlFlow = CommandLine()->CheckParm( "-glslcontrolflow" ) != NULL; +#else + pCaps->m_bSupportsStaticControlFlow = !CommandLine()->CheckParm( "-noglslcontrolflow" ); +#endif + + // NOTE: Texture stages is a fixed-function concept + // NOTE: Normally, the number of texture units == the number of texture + // stages except for NVidia hardware, which reports more stages than units. + // The reason for this is because they expose the inner hardware pixel + // pipeline through the extra stages. The only thing we use stages for + // in the hardware is for configuring the color + alpha args + ops. + pCaps->m_NumSamplers = caps.MaxSimultaneousTextures; + pCaps->m_NumTextureStages = caps.MaxTextureBlendStages; + if ( pCaps->m_SupportsPixelShaders_2_0 ) + { + pCaps->m_NumSamplers = 16; + } + else + { + Assert( pCaps->m_NumSamplers <= pCaps->m_NumTextureStages ); + } + + // Clamp + pCaps->m_NumSamplers = min( pCaps->m_NumSamplers, (int)MAX_SAMPLERS ); + pCaps->m_NumTextureStages = min( pCaps->m_NumTextureStages, (int)MAX_TEXTURE_STAGES ); + + if ( D3DSupportsCompressedTextures() ) + { + pCaps->m_SupportsCompressedTextures = COMPRESSED_TEXTURES_ON; + } + else + { + pCaps->m_SupportsCompressedTextures = COMPRESSED_TEXTURES_OFF; + } + + pCaps->m_bSupportsAnisotropicFiltering = (caps.TextureFilterCaps & D3DPTFILTERCAPS_MINFANISOTROPIC) != 0; + pCaps->m_bSupportsMagAnisotropicFiltering = (caps.TextureFilterCaps & D3DPTFILTERCAPS_MAGFANISOTROPIC) != 0; + + // OpenGL does not support this--at least not on OSX which is the primary GL target, so just don't use that path on GL at all. +#if !defined( DX_TO_GL_ABSTRACTION ) + pCaps->m_bCanStretchRectFromTextures = ( ( caps.DevCaps2 & D3DDEVCAPS2_CAN_STRETCHRECT_FROM_TEXTURES ) != 0 ) && ( pCaps->m_VendorID != VENDORID_INTEL ); +#else + pCaps->m_bCanStretchRectFromTextures = false; +#endif + + pCaps->m_nMaxAnisotropy = pCaps->m_bSupportsAnisotropicFiltering ? caps.MaxAnisotropy : 1; + + pCaps->m_SupportsCubeMaps = ( caps.TextureCaps & D3DPTEXTURECAPS_CUBEMAP ) ? true : false; + pCaps->m_SupportsNonPow2Textures = + ( !( caps.TextureCaps & D3DPTEXTURECAPS_POW2 ) || + ( caps.TextureCaps & D3DPTEXTURECAPS_NONPOW2CONDITIONAL ) ); + + Assert( caps.TextureCaps & D3DPTEXTURECAPS_PROJECTED ); + + if ( pCaps->m_bSoftwareVertexProcessing ) + { + // This should be pushed down based on pixel shaders. + pCaps->m_NumVertexShaderConstants = 256; + pCaps->m_NumBooleanVertexShaderConstants = pCaps->m_SupportsPixelShaders_2_0 ? 16 : 0; // 2.0 parts have 16 bool vs registers + pCaps->m_NumBooleanPixelShaderConstants = pCaps->m_SupportsPixelShaders_2_0 ? 16 : 0; // 2.0 parts have 16 bool ps registers + pCaps->m_NumIntegerVertexShaderConstants = pCaps->m_SupportsPixelShaders_2_0 ? 16 : 0; // 2.0 parts have 16 bool vs registers + pCaps->m_NumIntegerPixelShaderConstants = pCaps->m_SupportsPixelShaders_2_0 ? 16 : 0; // 2.0 parts have 16 bool ps registers + } + else + { + pCaps->m_NumVertexShaderConstants = caps.MaxVertexShaderConst; + if ( CommandLine()->FindParm( "-limitvsconst" ) ) + { + pCaps->m_NumVertexShaderConstants = min( 256, pCaps->m_NumVertexShaderConstants ); + } + pCaps->m_NumBooleanVertexShaderConstants = pCaps->m_SupportsPixelShaders_2_0 ? 16 : 0; // 2.0 parts have 16 bool vs registers + pCaps->m_NumBooleanPixelShaderConstants = pCaps->m_SupportsPixelShaders_2_0 ? 16 : 0; // 2.0 parts have 16 bool ps registers + + // This is a little misleading...this is really 16 int4 registers + pCaps->m_NumIntegerVertexShaderConstants = pCaps->m_SupportsPixelShaders_2_0 ? 16 : 0; // 2.0 parts have 16 bool vs registers + pCaps->m_NumIntegerPixelShaderConstants = pCaps->m_SupportsPixelShaders_2_0 ? 16 : 0; // 2.0 parts have 16 bool ps registers + } + + if ( pCaps->m_SupportsPixelShaders ) + { + if (pCaps->m_SupportsShaderModel_3_0) + { + pCaps->m_NumPixelShaderConstants = 224; + } + else + { + pCaps->m_NumPixelShaderConstants = 32; + } + } + //else + //{ + // pCaps->m_NumPixelShaderConstants = 0; + //} + + pCaps->m_SupportsHardwareLighting = (caps.DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT) != 0; + + pCaps->m_MaxNumLights = caps.MaxActiveLights; + if ( pCaps->m_MaxNumLights > MAX_NUM_LIGHTS ) + { + pCaps->m_MaxNumLights = MAX_NUM_LIGHTS; + } + + if ( IsOpenGL() ) + { + // Set according to control flow bit on OpenGL + pCaps->m_MaxNumLights = MIN( pCaps->m_MaxNumLights, ( pCaps->m_bSupportsStaticControlFlow && pCaps->m_SupportsPixelShaders_2_b ) ? MAX_NUM_LIGHTS : ( MAX_NUM_LIGHTS - 2 ) ); + } + + if ( pCaps->m_bSoftwareVertexProcessing ) + { + pCaps->m_SupportsHardwareLighting = true; + pCaps->m_MaxNumLights = 2; + } + pCaps->m_MaxTextureWidth = caps.MaxTextureWidth; + pCaps->m_MaxTextureHeight = caps.MaxTextureHeight; + pCaps->m_MaxTextureDepth = caps.MaxVolumeExtent ? caps.MaxVolumeExtent : 1; + pCaps->m_MaxTextureAspectRatio = caps.MaxTextureAspectRatio; + if ( pCaps->m_MaxTextureAspectRatio == 0 ) + { + pCaps->m_MaxTextureAspectRatio = max( pCaps->m_MaxTextureWidth, pCaps->m_MaxTextureHeight); + } + pCaps->m_MaxPrimitiveCount = caps.MaxPrimitiveCount; + pCaps->m_MaxBlendMatrices = caps.MaxVertexBlendMatrices; + pCaps->m_MaxBlendMatrixIndices = caps.MaxVertexBlendMatrixIndex; + + bool addSupported = (caps.TextureOpCaps & D3DTEXOPCAPS_ADD) != 0; + bool modSupported = (caps.TextureOpCaps & D3DTEXOPCAPS_MODULATE2X) != 0; + + pCaps->m_bNeedsATICentroidHack = false; + pCaps->m_bDisableShaderOptimizations = false; + + pCaps->m_SupportsMipmapping = true; + pCaps->m_SupportsOverbright = true; + + // Thank you to all you driver writers who actually correctly return caps + if ( !modSupported || !addSupported ) + { + Assert( 0 ); + pCaps->m_SupportsOverbright = false; + } + + // Check if ZBias and SlopeScaleDepthBias are supported. .if not, tweak the projection matrix instead + // for polyoffset. + pCaps->m_ZBiasAndSlopeScaledDepthBiasSupported = + ( ( caps.RasterCaps & D3DPRASTERCAPS_DEPTHBIAS) != 0 ) && + ( ( caps.RasterCaps & D3DPRASTERCAPS_SLOPESCALEDEPTHBIAS ) != 0 ); + if ( IsX360() ) + { + // driver lies, force it + pCaps->m_ZBiasAndSlopeScaledDepthBiasSupported = true; + } + + // Spheremapping supported? + pCaps->m_bSupportsSpheremapping = (caps.VertexProcessingCaps & D3DVTXPCAPS_TEXGEN_SPHEREMAP) != 0; + + // How many user clip planes? + pCaps->m_MaxUserClipPlanes = caps.MaxUserClipPlanes; + if ( CommandLine()->CheckParm( "-nouserclip" ) /* || (IsOpenGL() && (!CommandLine()->FindParm("-glslmode"))) || r_emulategl.GetBool() */ ) + { + // rbarris 03Feb10: this now ignores POSIX / -glslmode / r_emulategl because we're defaulting GLSL mode "on". + // so this will mean that the engine will always ask for user clip planes. + // this will misbehave under ARB mode, since ARB shaders won't respect that state. + // it's difficult to make this fluid without teaching the engine about a cap that could change during run. + + pCaps->m_MaxUserClipPlanes = 0; + } + + if ( pCaps->m_MaxUserClipPlanes > MAXUSERCLIPPLANES ) + { + pCaps->m_MaxUserClipPlanes = MAXUSERCLIPPLANES; + } + + pCaps->m_FakeSRGBWrite = false; + pCaps->m_CanDoSRGBReadFromRTs = true; + pCaps->m_bSupportsGLMixedSizeTargets = false; +#ifdef DX_TO_GL_ABSTRACTION + // using #if because we're referencing fields in the RHS which don't exist in Windows headers for the caps9 struct + pCaps->m_FakeSRGBWrite = caps.FakeSRGBWrite != 0; + pCaps->m_CanDoSRGBReadFromRTs = caps.CanDoSRGBReadFromRTs != 0; + pCaps->m_bSupportsGLMixedSizeTargets = caps.MixedSizeTargets != 0; +#endif + + // Query for SRGB support as needed for our DX 9 stuff + if ( IsPC() || !IsX360() ) + { + pCaps->m_SupportsSRGB = ( D3D()->CheckDeviceFormat( nAdapter, DX8_DEVTYPE, D3DFMT_X8R8G8B8, D3DUSAGE_QUERY_SRGBREAD, D3DRTYPE_TEXTURE, D3DFMT_DXT1 ) == S_OK); + + if ( pCaps->m_SupportsSRGB ) + { + pCaps->m_SupportsSRGB = ( D3D()->CheckDeviceFormat( nAdapter, DX8_DEVTYPE, D3DFMT_X8R8G8B8, D3DUSAGE_QUERY_SRGBREAD | D3DUSAGE_QUERY_SRGBWRITE, D3DRTYPE_TEXTURE, D3DFMT_A8R8G8B8 ) == S_OK); + } + } + else + { + // 360 does support it, but is queried in the wrong manner, so force it + pCaps->m_SupportsSRGB = true; + } + + if ( CommandLine()->CheckParm( "-nosrgb" ) ) + { + pCaps->m_SupportsSRGB = false; + } + + pCaps->m_bSupportsVertexTextures = ( D3D()->CheckDeviceFormat( nAdapter, DX8_DEVTYPE, D3DFMT_X8R8G8B8, + D3DUSAGE_QUERY_VERTEXTEXTURE, D3DRTYPE_TEXTURE, D3DFMT_R32F ) == S_OK ); + + if ( IsOpenGL() ) + { + pCaps->m_bSupportsVertexTextures = false; + } + + // FIXME: vs30 has a fixed setting here at 4. + // Future hardware will need some other way of computing this. + pCaps->m_nVertexTextureCount = pCaps->m_bSupportsVertexTextures ? 4 : 0; + + // FIXME: How do I actually compute this? + pCaps->m_nMaxVertexTextureDimension = pCaps->m_bSupportsVertexTextures ? 4096 : 0; + + // Does the device support filterable int16 textures? + bool bSupportsInteger16Textures = + ( D3D()->CheckDeviceFormat( nAdapter, DX8_DEVTYPE, + D3DFMT_X8R8G8B8, D3DUSAGE_QUERY_FILTER, + D3DRTYPE_TEXTURE, D3DFMT_A16B16G16R16 ) == S_OK ); + + // Does the device support filterable fp16 textures? + bool bSupportsFloat16Textures = + ( D3D()->CheckDeviceFormat( nAdapter, DX8_DEVTYPE, + D3DFMT_X8R8G8B8, D3DUSAGE_QUERY_FILTER, + D3DRTYPE_TEXTURE, D3DFMT_A16B16G16R16F ) == S_OK ); + + // Does the device support blendable fp16 render targets? + bool bSupportsFloat16RenderTargets = + ( D3D()->CheckDeviceFormat( nAdapter, DX8_DEVTYPE, + D3DFMT_X8R8G8B8, D3DUSAGE_QUERY_POSTPIXELSHADER_BLENDING | D3DUSAGE_RENDERTARGET, + D3DRTYPE_TEXTURE, D3DFMT_A16B16G16R16F ) == S_OK ); + + // Essentially a proxy for a DX10 device running DX9 code path + pCaps->m_bSupportsFloat32RenderTargets = ( D3D()->CheckDeviceFormat( nAdapter, DX8_DEVTYPE, + D3DFMT_X8R8G8B8, D3DUSAGE_QUERY_POSTPIXELSHADER_BLENDING | D3DUSAGE_RENDERTARGET, + D3DRTYPE_TEXTURE, D3DFMT_A32B32G32R32F ) == S_OK ); + + pCaps->m_bFogColorSpecifiedInLinearSpace = false; + pCaps->m_bFogColorAlwaysLinearSpace = false; + + // Assume not DX10. Check below. + pCaps->m_bDX10Card = false; + pCaps->m_bDX10Blending = false; + + if ( IsOpenGL() && ( pCaps->m_VendorID == 1 ) ) + { + // Linux/Win OpenGL - always assume the device supports DX10 style blending + pCaps->m_bFogColorAlwaysLinearSpace = true; + pCaps->m_bDX10Card = true; + pCaps->m_bDX10Blending = true; + } + + // NVidia wants fog color to be specified in linear space + if ( IsPC() && pCaps->m_SupportsSRGB ) + { + if ( pCaps->m_VendorID == VENDORID_NVIDIA ) + { + pCaps->m_bFogColorSpecifiedInLinearSpace = true; + + if ( IsOpenGL() ) + { + // If we're not the Quadro 4500 or GeForce 7x000, we're an NVIDIA DX10 part on MacOS + if ( !( (pCaps->m_DeviceID == 0x009d) || ( (pCaps->m_DeviceID >= 0x0391) && (pCaps->m_DeviceID <= 0x0395) ) ) ) + { + pCaps->m_bFogColorAlwaysLinearSpace = true; + pCaps->m_bDX10Card = true; + pCaps->m_bDX10Blending = true; + } + } + else + { + // On G80 and later, always specify in linear space + if ( pCaps->m_bSupportsFloat32RenderTargets ) + { + pCaps->m_bFogColorAlwaysLinearSpace = true; + pCaps->m_bDX10Card = true; + pCaps->m_bDX10Blending = true; + } + } + } + else if ( pCaps->m_VendorID == VENDORID_ATI ) + { + if ( IsOpenGL() ) + { + // If we're not a Radeon X1x00 (device IDs in this range), we're a DX10 chip + if ( !( (pCaps->m_DeviceID >= 0x7109) && (pCaps->m_DeviceID <= 0x7291) ) ) + { + pCaps->m_bFogColorSpecifiedInLinearSpace = true; + pCaps->m_bFogColorAlwaysLinearSpace = true; + pCaps->m_bDX10Card = true; + pCaps->m_bDX10Blending = true; + } + } + else + { + // Check for DX10 part + pCaps->m_bDX10Card = pCaps->m_SupportsShaderModel_3_0 && + ( pCaps->m_MaxVertexShader30InstructionSlots > 1024 ) && + ( pCaps->m_MaxPixelShader30InstructionSlots > 512 ) ; + + // On ATI, DX10 card means DX10 blending + pCaps->m_bDX10Blending = pCaps->m_bDX10Card; + + if( pCaps->m_bDX10Blending ) + { + pCaps->m_bFogColorSpecifiedInLinearSpace = true; + pCaps->m_bFogColorAlwaysLinearSpace = true; + } + } + } + else if ( pCaps->m_VendorID == VENDORID_INTEL ) + { + // Intel does not have performant vertex textures + pCaps->m_bDX10Card = false; + + // Intel supports DX10 SRGB on Broadwater and better + // The two checks are for devices from GMA generation (0x29A2-0x2A43) and HD graphics (0x0042-0x2500) + pCaps->m_bDX10Blending = ( ( pCaps->m_DeviceID >= 0x29A2 ) && ( pCaps->m_DeviceID <= 0x2A43 ) ) || + ( ( pCaps->m_DeviceID >= 0x0042 ) && ( pCaps->m_DeviceID <= 0x2500 ) ); + + if( pCaps->m_bDX10Blending ) + { + pCaps->m_bFogColorSpecifiedInLinearSpace = true; + pCaps->m_bFogColorAlwaysLinearSpace = true; + } + } + } + + // Do we have everything necessary to run with integer HDR? Note that + // even if we don't support integer 16-bit/component textures, we + // can still run in this mode if fp16 textures are supported. + bool bSupportsIntegerHDR = pCaps->m_SupportsPixelShaders_2_0 && + pCaps->m_SupportsVertexShaders_2_0 && + // (caps.Caps3 & D3DCAPS3_ALPHA_FULLSCREEN_FLIP_OR_DISCARD) && + // (caps.PrimitiveMiscCaps & D3DPMISCCAPS_SEPARATEALPHABLEND) && + ( bSupportsInteger16Textures || bSupportsFloat16Textures ) && + pCaps->m_SupportsSRGB; + + // Do we have everything necessary to run with float HDR? + bool bSupportsFloatHDR = pCaps->m_SupportsShaderModel_3_0 && + // (caps.Caps3 & D3DCAPS3_ALPHA_FULLSCREEN_FLIP_OR_DISCARD) && + // (caps.PrimitiveMiscCaps & D3DPMISCCAPS_SEPARATEALPHABLEND) && + bSupportsFloat16Textures && + bSupportsFloat16RenderTargets && + pCaps->m_SupportsSRGB && + !IsX360(); + + pCaps->m_MaxHDRType = HDR_TYPE_NONE; + if ( bSupportsFloatHDR ) + pCaps->m_MaxHDRType = HDR_TYPE_FLOAT; + else + if ( bSupportsIntegerHDR ) + pCaps->m_MaxHDRType = HDR_TYPE_INTEGER; + + if ( bSupportsFloatHDR && ( mat_hdr_level.GetInt() == 3 ) ) + { + pCaps->m_HDRType = HDR_TYPE_FLOAT; + } + else if ( bSupportsIntegerHDR ) + { + pCaps->m_HDRType = HDR_TYPE_INTEGER; + } + else + { + pCaps->m_HDRType = HDR_TYPE_NONE; + } + + pCaps->m_bColorOnSecondStream = caps.MaxStreams > 1; + + pCaps->m_bSupportsStreamOffset = ( ( caps.DevCaps2 & D3DDEVCAPS2_STREAMOFFSET ) && // Tie these caps together since we want to filter out + pCaps->m_SupportsPixelShaders_2_0 ); // any DX8 parts which export D3DDEVCAPS2_STREAMOFFSET + + pCaps->m_flMinGammaControlPoint = 0.0f; + pCaps->m_flMaxGammaControlPoint = 65535.0f; + pCaps->m_nGammaControlPointCount = 256; + + // Compute the effective DX support level based on all the other caps + ComputeDXSupportLevel( *pCaps ); + int nCmdlineMaxDXLevel = CommandLine()->ParmValue( "-maxdxlevel", 0 ); + if ( IsOpenGL() && ( nCmdlineMaxDXLevel > 0 ) ) + { + // Prevent customers from slamming us below DX level 90 in OpenGL mode. + nCmdlineMaxDXLevel = MAX( nCmdlineMaxDXLevel, 90 ); + } + if( nCmdlineMaxDXLevel > 0 ) + { + pCaps->m_nMaxDXSupportLevel = min( pCaps->m_nMaxDXSupportLevel, nCmdlineMaxDXLevel ); + } + pCaps->m_nDXSupportLevel = pCaps->m_nMaxDXSupportLevel; + + int nModelIndex = pCaps->m_nDXSupportLevel < 90 ? VERTEX_SHADER_MODEL - 10 : VERTEX_SHADER_MODEL; + pCaps->m_MaxVertexShaderBlendMatrices = (pCaps->m_NumVertexShaderConstants - nModelIndex) / 3; + + if ( pCaps->m_MaxVertexShaderBlendMatrices > NUM_MODEL_TRANSFORMS ) + { + pCaps->m_MaxVertexShaderBlendMatrices = NUM_MODEL_TRANSFORMS; + } + + CheckBorderColorSupport( pCaps, nAdapter ); + + // This may get more complex if we start using multiple flavors of compressed vertex - for now it's "on or off" + pCaps->m_SupportsCompressedVertices = ( pCaps->m_nDXSupportLevel >= 90 ) && ( pCaps->m_CanDoSRGBReadFromRTs ) ? VERTEX_COMPRESSION_ON : VERTEX_COMPRESSION_NONE; + if ( CommandLine()->CheckParm( "-no_compressed_verts" ) ) // m_CanDoSRGBReadFromRTs limits us to Snow Leopard or later on OSX + { + pCaps->m_SupportsCompressedVertices = VERTEX_COMPRESSION_NONE; + } + + // Various vendor-dependent checks... + CheckVendorDependentAlphaToCoverage( pCaps, nAdapter ); + CheckVendorDependentShadowMappingSupport( pCaps, nAdapter ); + + // If we're not on a 3.0 part, these values are more appropriate (X800 & X850 parts from ATI do shadow mapping but not 3.0 ) + if ( !IsOpenGL() ) + { + if ( !pCaps->m_SupportsShaderModel_3_0 ) + { + mat_slopescaledepthbias_shadowmap.SetValue( 5.9f ); + mat_depthbias_shadowmap.SetValue( 0.003f ); + } + } + + if( pCaps->m_MaxUserClipPlanes == 0 ) + { + pCaps->m_UseFastClipping = true; + } + + pCaps->m_MaxSimultaneousRenderTargets = caps.NumSimultaneousRTs; + + return true; +} + +//----------------------------------------------------------------------------- +// Compute the effective DX support level based on all the other caps +//----------------------------------------------------------------------------- +void CShaderDeviceMgrDx8::ComputeDXSupportLevel( HardwareCaps_t &caps ) +{ + // NOTE: Support level is actually DX level * 10 + subversion + // So, 70 = DX7, 80 = DX8, 81 = DX8 w/ 1.4 pixel shaders + // 90 = DX9 w/ 2.0 pixel shaders + // 95 = DX9 w/ 3.0 pixel shaders and vertex textures + // 98 = DX9 XBox360 + // NOTE: 82 = NVidia nv3x cards, which can't run dx9 fast + + // FIXME: Improve this!! There should be a whole list of features + // we require in order to be considered a DX7 board, DX8 board, etc. + + if ( IsX360() ) + { + caps.m_nMaxDXSupportLevel = 98; + return; + } + + bool bIsOpenGL = IsOpenGL(); + + if ( caps.m_SupportsShaderModel_3_0 && !bIsOpenGL ) // Note that we don't tie vertex textures to 30 shaders anymore + { + caps.m_nMaxDXSupportLevel = 95; + return; + } + + // NOTE: sRGB is currently required for DX90 because it isn't doing + // gamma correctly if that feature doesn't exist + if ( caps.m_SupportsVertexShaders_2_0 && caps.m_SupportsPixelShaders_2_0 && caps.m_SupportsSRGB ) + { + caps.m_nMaxDXSupportLevel = 90; + return; + } + + if ( caps.m_SupportsPixelShaders && caps.m_SupportsVertexShaders )// && caps.m_bColorOnSecondStream) + { + if (caps.m_SupportsPixelShaders_1_4) + { + caps.m_nMaxDXSupportLevel = 81; + return; + } + caps.m_nMaxDXSupportLevel = 80; + return; + } + + if( caps.m_SupportsCubeMaps && ( caps.m_MaxBlendMatrices >= 2 ) ) + { + caps.m_nMaxDXSupportLevel = 70; + return; + } + + if ( ( caps.m_NumSamplers >= 2) && caps.m_SupportsMipmapping ) + { + caps.m_nMaxDXSupportLevel = 60; + return; + } + + Assert( 0 ); + // we don't support this! + caps.m_nMaxDXSupportLevel = 50; +} + + + +//----------------------------------------------------------------------------- +// Gets the number of adapters... +//----------------------------------------------------------------------------- +int CShaderDeviceMgrDx8::GetAdapterCount() const +{ + // FIXME: Remove call to InitAdapterInfo once Steam startup issues are resolved. + const_cast( this )->InitAdapterInfo(); + + return m_Adapters.Count(); +} + + +//----------------------------------------------------------------------------- +// Returns info about each adapter +//----------------------------------------------------------------------------- +void CShaderDeviceMgrDx8::GetAdapterInfo( int nAdapter, MaterialAdapterInfo_t& info ) const +{ + // FIXME: Remove call to InitAdapterInfo once Steam startup issues are resolved. + const_cast( this )->InitAdapterInfo(); + + Assert( ( nAdapter >= 0 ) && ( nAdapter < m_Adapters.Count() ) ); + const HardwareCaps_t &caps = m_Adapters[ nAdapter ].m_ActualCaps; + memcpy( &info, &caps, sizeof(MaterialAdapterInfo_t) ); +} + + +//----------------------------------------------------------------------------- +// Sets the adapter +//----------------------------------------------------------------------------- +bool CShaderDeviceMgrDx8::SetAdapter( int nAdapter, int nAdapterFlags ) +{ + LOCK_SHADERAPI(); + + // FIXME: + // g_pShaderDeviceDx8->m_bReadPixelsEnabled = (nAdapterFlags & MATERIAL_INIT_READ_PIXELS_ENABLED) != 0; + + // Set up hardware information for this adapter... + g_pShaderDeviceDx8->m_DeviceType = (nAdapterFlags & MATERIAL_INIT_REFERENCE_RASTERIZER) ? + D3DDEVTYPE_REF : D3DDEVTYPE_HAL; + + g_pShaderDeviceDx8->m_DisplayAdapter = nAdapter; + if ( g_pShaderDeviceDx8->m_DisplayAdapter >= (UINT)GetAdapterCount() ) + { + g_pShaderDeviceDx8->m_DisplayAdapter = 0; + } + +#ifdef NVPERFHUD + // hack for nvperfhud + g_pShaderDeviceDx8->m_DisplayAdapter = m_pD3D->GetAdapterCount() - 1; + g_pShaderDeviceDx8->m_DeviceType = D3DDEVTYPE_REF; +#endif + + // backward compat + if ( !g_pShaderDeviceDx8->OnAdapterSet() ) + return false; + +// if ( !g_pShaderDeviceDx8->Init() ) +// { +// Warning( "Unable to initialize dx8 device!\n" ); +// return false; +// } + + g_pShaderDevice = g_pShaderDeviceDx8; + + return true; +} + + +//----------------------------------------------------------------------------- +// Returns the number of modes +//----------------------------------------------------------------------------- +int CShaderDeviceMgrDx8::GetModeCount( int nAdapter ) const +{ + LOCK_SHADERAPI(); + Assert( m_pD3D && (nAdapter < GetAdapterCount() ) ); + +#if !defined( _X360 ) + // fixme - what format should I use here? + return m_pD3D->GetAdapterModeCount( nAdapter, D3DFMT_X8R8G8B8 ); +#else + return 1; // Only one mode, which is the current mode set in the 360 dashboard. Going to fill it in with exactly what the 360 is set to. +#endif +} + + +//----------------------------------------------------------------------------- +// Returns mode information.. +//----------------------------------------------------------------------------- +void CShaderDeviceMgrDx8::GetModeInfo( ShaderDisplayMode_t* pInfo, int nAdapter, int nMode ) const +{ + Assert( pInfo->m_nVersion == SHADER_DISPLAY_MODE_VERSION ); + + LOCK_SHADERAPI(); + Assert( m_pD3D && (nAdapter < GetAdapterCount() ) ); + Assert( nMode < GetModeCount( nAdapter ) ); + +#if !defined( _X360 ) + HRESULT hr; + D3DDISPLAYMODE d3dInfo; + + // fixme - what format should I use here? + hr = D3D()->EnumAdapterModes( nAdapter, D3DFMT_X8R8G8B8, nMode, &d3dInfo ); + Assert( !FAILED(hr) ); + + pInfo->m_nWidth = d3dInfo.Width; + pInfo->m_nHeight = d3dInfo.Height; + pInfo->m_Format = ImageLoader::D3DFormatToImageFormat( d3dInfo.Format ); + pInfo->m_nRefreshRateNumerator = d3dInfo.RefreshRate; + pInfo->m_nRefreshRateDenominator = 1; +#else + pInfo->m_Format = ImageLoader::D3DFormatToImageFormat( D3DFMT_X8R8G8B8 ); + pInfo->m_nRefreshRateNumerator = 60; + pInfo->m_nRefreshRateDenominator = 1; + + pInfo->m_nWidth = GetSystemMetrics( SM_CXSCREEN ); + pInfo->m_nHeight = GetSystemMetrics( SM_CYSCREEN ); +#endif +} + + +//----------------------------------------------------------------------------- +// Returns the current mode information for an adapter +//----------------------------------------------------------------------------- +void CShaderDeviceMgrDx8::GetCurrentModeInfo( ShaderDisplayMode_t* pInfo, int nAdapter ) const +{ + Assert( pInfo->m_nVersion == SHADER_DISPLAY_MODE_VERSION ); + + LOCK_SHADERAPI(); + Assert( D3D() ); + + HRESULT hr; + D3DDISPLAYMODE mode = { 0 }; +#if !defined( _X360 ) + hr = D3D()->GetAdapterDisplayMode( nAdapter, &mode ); + Assert( !FAILED(hr) ); +#else + if ( !g_pD3DDevice ) + { + // the console has no prior display or mode until its created + mode.Width = GetSystemMetrics( SM_CXSCREEN ); + mode.Height = GetSystemMetrics( SM_CYSCREEN ); + mode.RefreshRate = 60; + mode.Format = D3DFMT_X8R8G8B8; + } + else + { + hr = g_pD3DDevice->GetDisplayMode( 0, &mode ); + Assert( !FAILED(hr) ); + } +#endif + + pInfo->m_nWidth = mode.Width; + pInfo->m_nHeight = mode.Height; + pInfo->m_Format = ImageLoader::D3DFormatToImageFormat( mode.Format ); + pInfo->m_nRefreshRateNumerator = mode.RefreshRate; + pInfo->m_nRefreshRateDenominator = 1; +} + + +//----------------------------------------------------------------------------- +// Sets the video mode +//----------------------------------------------------------------------------- +CreateInterfaceFn CShaderDeviceMgrDx8::SetMode( void *hWnd, int nAdapter, const ShaderDeviceInfo_t& mode ) +{ + LOCK_SHADERAPI(); + + Assert( nAdapter < GetAdapterCount() ); + int nDXLevel = mode.m_nDXLevel != 0 ? mode.m_nDXLevel : m_Adapters[nAdapter].m_ActualCaps.m_nDXSupportLevel; + if ( m_bObeyDxCommandlineOverride ) + { + nDXLevel = CommandLine()->ParmValue( "-dxlevel", nDXLevel ); + m_bObeyDxCommandlineOverride = false; + } + if ( nDXLevel > m_Adapters[nAdapter].m_ActualCaps.m_nMaxDXSupportLevel ) + { + nDXLevel = m_Adapters[nAdapter].m_ActualCaps.m_nMaxDXSupportLevel; + } + nDXLevel = GetClosestActualDXLevel( nDXLevel ); + + if ( nDXLevel >= 100 ) + return NULL; + + bool bReacquireResourcesNeeded = false; + if ( g_pShaderDevice ) + { + bReacquireResourcesNeeded = IsPC(); + g_pShaderDevice->ReleaseResources(); + } + + if ( g_pShaderAPI ) + { + g_pShaderAPI->OnDeviceShutdown(); + g_pShaderAPI = NULL; + } + + if ( g_pShaderDevice ) + { + g_pShaderDevice->ShutdownDevice(); + g_pShaderDevice = NULL; + } + + g_pShaderShadow = NULL; + + ShaderDeviceInfo_t adjustedMode = mode; + adjustedMode.m_nDXLevel = nDXLevel; + if ( !g_pShaderDeviceDx8->InitDevice( hWnd, nAdapter, adjustedMode ) ) + return NULL; + + if ( !g_pShaderAPIDX8->OnDeviceInit() ) + return NULL; + + g_pShaderDevice = g_pShaderDeviceDx8; + g_pShaderAPI = g_pShaderAPIDX8; + g_pShaderShadow = g_pShaderShadowDx8; + + if ( bReacquireResourcesNeeded ) + { + g_pShaderDevice->ReacquireResources(); + } + + return ShaderInterfaceFactory; +} + + +//----------------------------------------------------------------------------- +// Validates the mode... +//----------------------------------------------------------------------------- +bool CShaderDeviceMgrDx8::ValidateMode( int nAdapter, const ShaderDeviceInfo_t &info ) const +{ + if ( nAdapter >= (int)D3D()->GetAdapterCount() ) + return false; + + ShaderDisplayMode_t displayMode; + + if ( info.m_bWindowed ) + { + // windowed mode always appears on the primary display, so we should use that adapter's + // settings + GetCurrentModeInfo( &displayMode, 0 ); + + // make sure the window fits within the current video mode + if ( ( info.m_DisplayMode.m_nWidth > displayMode.m_nWidth ) || + ( info.m_DisplayMode.m_nHeight > displayMode.m_nHeight ) ) + return false; + } + else + { + GetCurrentModeInfo( &displayMode, nAdapter ); + } + + // Make sure the image format requested is valid + ImageFormat backBufferFormat = FindNearestSupportedBackBufferFormat( nAdapter, + DX8_DEVTYPE, displayMode.m_Format, info.m_DisplayMode.m_Format, info.m_bWindowed ); + return ( backBufferFormat != IMAGE_FORMAT_UNKNOWN ); +} + + +//----------------------------------------------------------------------------- +// Returns the amount of video memory in bytes for a particular adapter +//----------------------------------------------------------------------------- +int CShaderDeviceMgrDx8::GetVidMemBytes( int nAdapter ) const +{ +#if defined( _X360 ) + return 256*1024*1024; +#elif defined (DX_TO_GL_ABSTRACTION) + D3DADAPTER_IDENTIFIER9 devIndentifier; + D3D()->GetAdapterIdentifier( nAdapter, D3DENUM_WHQL_LEVEL, &devIndentifier ); + return devIndentifier.VideoMemory; +#else + // FIXME: This currently ignores the adapter + uint64 nBytes = ::GetVidMemBytes(); + if ( nBytes > INT_MAX ) + return INT_MAX; + return nBytes; +#endif +} + + + +//----------------------------------------------------------------------------- +// +// Shader device +// +//----------------------------------------------------------------------------- + +#if 0 +// FIXME: Enable after I've separated it out from shaderapidx8 a little better +static CShaderDeviceDx8 s_ShaderDeviceDX8; +CShaderDeviceDx8* g_pShaderDeviceDx8 = &s_ShaderDeviceDX8; +#endif + + +//----------------------------------------------------------------------------- +// Constructor, destructor +//----------------------------------------------------------------------------- +CShaderDeviceDx8::CShaderDeviceDx8() +{ + g_pD3DDevice = NULL; + for ( int i = 0; i < ARRAYSIZE(m_pFrameSyncQueryObject); i++ ) + { + m_pFrameSyncQueryObject[i] = NULL; + m_bQueryIssued[i] = false; + } + m_pFrameSyncTexture = NULL; + m_bQueuedDeviceLost = false; + m_DeviceState = DEVICE_STATE_OK; + m_bOtherAppInitializing = false; + m_IsResizing = false; + m_bPendingVideoModeChange = false; + m_DeviceSupportsCreateQuery = -1; + m_bUsingStencil = false; + m_bResourcesReleased = false; + m_iStencilBufferBits = 0; + m_NonInteractiveRefresh.m_Mode = MATERIAL_NON_INTERACTIVE_MODE_NONE; + m_NonInteractiveRefresh.m_pVertexShader = NULL; + m_NonInteractiveRefresh.m_pPixelShader = NULL; + m_NonInteractiveRefresh.m_pPixelShaderStartup = NULL; + m_NonInteractiveRefresh.m_pPixelShaderStartupPass2 = NULL; + m_NonInteractiveRefresh.m_pVertexDecl = NULL; + m_NonInteractiveRefresh.m_nPacifierFrame = 0; + m_numReleaseResourcesRefCount = 0; +} + +CShaderDeviceDx8::~CShaderDeviceDx8() +{ +} + + +//----------------------------------------------------------------------------- +// Computes device creation paramters +//----------------------------------------------------------------------------- +static DWORD ComputeDeviceCreationFlags( D3DCAPS& caps, bool bSoftwareVertexProcessing ) +{ + // Find out what type of device to make + bool bPureDeviceSupported = (caps.DevCaps & D3DDEVCAPS_PUREDEVICE) != 0; + + DWORD nDeviceCreationFlags; + if ( !bSoftwareVertexProcessing ) + { + nDeviceCreationFlags = D3DCREATE_HARDWARE_VERTEXPROCESSING; + if ( bPureDeviceSupported ) + { + nDeviceCreationFlags |= D3DCREATE_PUREDEVICE; + } + } + else + { + nDeviceCreationFlags = D3DCREATE_SOFTWARE_VERTEXPROCESSING; + } + nDeviceCreationFlags |= D3DCREATE_FPU_PRESERVE; + +#ifdef _X360 + nDeviceCreationFlags |= D3DCREATE_BUFFER_2_FRAMES; +#endif + + return nDeviceCreationFlags; +} + + +//----------------------------------------------------------------------------- +// Computes the supersample flags +//----------------------------------------------------------------------------- +D3DMULTISAMPLE_TYPE CShaderDeviceDx8::ComputeMultisampleType( int nSampleCount ) +{ + switch (nSampleCount) + { +#if !defined( _X360 ) + case 2: return D3DMULTISAMPLE_2_SAMPLES; + case 3: return D3DMULTISAMPLE_3_SAMPLES; + case 4: return D3DMULTISAMPLE_4_SAMPLES; + case 5: return D3DMULTISAMPLE_5_SAMPLES; + case 6: return D3DMULTISAMPLE_6_SAMPLES; + case 7: return D3DMULTISAMPLE_7_SAMPLES; + case 8: return D3DMULTISAMPLE_8_SAMPLES; + case 9: return D3DMULTISAMPLE_9_SAMPLES; + case 10: return D3DMULTISAMPLE_10_SAMPLES; + case 11: return D3DMULTISAMPLE_11_SAMPLES; + case 12: return D3DMULTISAMPLE_12_SAMPLES; + case 13: return D3DMULTISAMPLE_13_SAMPLES; + case 14: return D3DMULTISAMPLE_14_SAMPLES; + case 15: return D3DMULTISAMPLE_15_SAMPLES; + case 16: return D3DMULTISAMPLE_16_SAMPLES; +#else + case 2: return D3DMULTISAMPLE_2_SAMPLES; + case 4: return D3DMULTISAMPLE_4_SAMPLES; +#endif + default: + case 0: + case 1: + return D3DMULTISAMPLE_NONE; + } +} + + +//----------------------------------------------------------------------------- +// Sets the present parameters +//----------------------------------------------------------------------------- +void CShaderDeviceDx8::SetPresentParameters( void* hWnd, int nAdapter, const ShaderDeviceInfo_t &info ) +{ + ShaderDisplayMode_t mode; + g_pShaderDeviceMgr->GetCurrentModeInfo( &mode, nAdapter ); + + HRESULT hr; + ZeroMemory( &m_PresentParameters, sizeof(m_PresentParameters) ); + + m_PresentParameters.Windowed = info.m_bWindowed; + m_PresentParameters.SwapEffect = info.m_bUsingMultipleWindows ? D3DSWAPEFFECT_COPY : D3DSWAPEFFECT_DISCARD; + + // for 360, we want to create it ourselves for hierarchical z support + m_PresentParameters.EnableAutoDepthStencil = IsX360() ? FALSE : TRUE; + + // What back-buffer format should we use? + ImageFormat backBufferFormat = FindNearestSupportedBackBufferFormat( nAdapter, + DX8_DEVTYPE, m_AdapterFormat, info.m_DisplayMode.m_Format, info.m_bWindowed ); + + // What depth format should we use? + m_bUsingStencil = info.m_bUseStencil; + if ( info.m_nDXLevel >= 80 ) + { + // always stencil for dx9/hdr + m_bUsingStencil = true; + } +#if defined( _X360 ) + D3DFORMAT nDepthFormat = ReverseDepthOnX360() ? D3DFMT_D24FS8 : D3DFMT_D24S8; +#else + D3DFORMAT nDepthFormat = m_bUsingStencil ? D3DFMT_D24S8 : D3DFMT_D24X8; +#endif + m_PresentParameters.AutoDepthStencilFormat = FindNearestSupportedDepthFormat( + nAdapter, m_AdapterFormat, backBufferFormat, nDepthFormat ); + m_PresentParameters.hDeviceWindow = (VD3DHWND)hWnd; + + // store how many stencil buffer bits we have available with the depth/stencil buffer + switch( m_PresentParameters.AutoDepthStencilFormat ) + { + case D3DFMT_D24S8: + m_iStencilBufferBits = 8; + break; +#if defined( _X360 ) + case D3DFMT_D24FS8: + m_iStencilBufferBits = 8; + break; +#else + case D3DFMT_D24X4S4: + m_iStencilBufferBits = 4; + break; + case D3DFMT_D15S1: + m_iStencilBufferBits = 1; + break; +#endif + default: + m_iStencilBufferBits = 0; + m_bUsingStencil = false; //couldn't acquire a stencil buffer + }; + + if ( IsX360() || !info.m_bWindowed ) + { + bool useDefault = ( info.m_DisplayMode.m_nWidth == 0 ) || ( info.m_DisplayMode.m_nHeight == 0 ); + m_PresentParameters.BackBufferCount = 1; + m_PresentParameters.BackBufferWidth = useDefault ? mode.m_nWidth : info.m_DisplayMode.m_nWidth; + m_PresentParameters.BackBufferHeight = useDefault ? mode.m_nHeight : info.m_DisplayMode.m_nHeight; + m_PresentParameters.BackBufferFormat = ImageLoader::ImageFormatToD3DFormat( backBufferFormat ); +#if defined( _X360 ) + m_PresentParameters.FrontBufferFormat = D3DFMT_LE_X8R8G8B8; +#endif + if ( !info.m_bWaitForVSync || CommandLine()->FindParm( "-forcenovsync" ) ) + { + m_PresentParameters.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE; + } + else + { + m_PresentParameters.PresentationInterval = D3DPRESENT_INTERVAL_ONE; + } + + m_PresentParameters.FullScreen_RefreshRateInHz = info.m_DisplayMode.m_nRefreshRateDenominator ? + info.m_DisplayMode.m_nRefreshRateNumerator / info.m_DisplayMode.m_nRefreshRateDenominator : D3DPRESENT_RATE_DEFAULT; + +#if defined( _X360 ) + XVIDEO_MODE videoMode; + XGetVideoMode( &videoMode ); + + // want 30 for 60Hz, and 25 for 50Hz (PAL) + int nNewFpsMax = ( ( int )( videoMode.RefreshRate + 0.5f ) ) >> 1; + // slam to either 30 or 25 so that we don't end up with any other cases. + if( nNewFpsMax < 26 ) + { + nNewFpsMax = 25; + } + else + { + nNewFpsMax = 30; + } + DevMsg( "*******Monitor refresh is %f, setting fps_max to %d*********\n", videoMode.RefreshRate, nNewFpsMax ); + ConVarRef fps_max( "fps_max" ); + fps_max.SetValue( nNewFpsMax ); + + // setup hardware scaling - should be native 720p upsampling to 1080i + if ( info.m_bScaleToOutputResolution ) + { + m_PresentParameters.VideoScalerParameters.ScalerSourceRect.x2 = m_PresentParameters.BackBufferWidth; + m_PresentParameters.VideoScalerParameters.ScalerSourceRect.y2 = m_PresentParameters.BackBufferHeight; + m_PresentParameters.VideoScalerParameters.ScaledOutputWidth = videoMode.dwDisplayWidth; + m_PresentParameters.VideoScalerParameters.ScaledOutputHeight = videoMode.dwDisplayHeight; + DevMsg( "VIDEO SCALING: scaling from %dx%d to %dx%d\n", ( int )m_PresentParameters.BackBufferWidth, ( int )m_PresentParameters.BackBufferHeight, + ( int )videoMode.dwDisplayWidth, ( int )videoMode.dwDisplayHeight ); + } + else + { + DevMsg( "VIDEO SCALING: No scaling: %dx%d\n", ( int )m_PresentParameters.BackBufferWidth, ( int )m_PresentParameters.BackBufferHeight ); + } +#endif + } + else + { + // NJS: We are seeing a lot of time spent in present in some cases when this isn't set. + m_PresentParameters.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE; + if ( info.m_bResizing ) + { + if ( info.m_bLimitWindowedSize && + ( info.m_nWindowedSizeLimitWidth < mode.m_nWidth || info.m_nWindowedSizeLimitHeight < mode.m_nHeight ) ) + { + // When using material system in windowed resizing apps, it's + // sometimes not a good idea to allocate stuff as big as the screen + // video cards can soo run out of resources + m_PresentParameters.BackBufferWidth = info.m_nWindowedSizeLimitWidth; + m_PresentParameters.BackBufferHeight = info.m_nWindowedSizeLimitHeight; + } + else + { + // When in resizing windowed mode, + // we want to allocate enough memory to deal with any resizing... + m_PresentParameters.BackBufferWidth = mode.m_nWidth; + m_PresentParameters.BackBufferHeight = mode.m_nHeight; + } + } + else + { + m_PresentParameters.BackBufferWidth = info.m_DisplayMode.m_nWidth; + m_PresentParameters.BackBufferHeight = info.m_DisplayMode.m_nHeight; + } + m_PresentParameters.BackBufferFormat = ImageLoader::ImageFormatToD3DFormat( backBufferFormat ); + m_PresentParameters.BackBufferCount = 1; + } + + if ( info.m_nAASamples > 0 && ( m_PresentParameters.SwapEffect == D3DSWAPEFFECT_DISCARD ) ) + { + D3DMULTISAMPLE_TYPE multiSampleType = ComputeMultisampleType( info.m_nAASamples ); + DWORD nQualityLevel; + + // FIXME: Should we add the quality level to the ShaderAdapterMode_t struct? + // 16x on nVidia refers to CSAA or "Coverage Sampled Antialiasing" + const HardwareCaps_t &adapterCaps = g_ShaderDeviceMgrDx8.GetHardwareCaps( nAdapter ); + if ( ( info.m_nAASamples == 16 ) && ( adapterCaps.m_VendorID == VENDORID_NVIDIA ) ) + { + multiSampleType = ComputeMultisampleType(4); + hr = D3D()->CheckDeviceMultiSampleType( nAdapter, DX8_DEVTYPE, + m_PresentParameters.BackBufferFormat, m_PresentParameters.Windowed, + multiSampleType, &nQualityLevel ); // 4x at highest quality level + + if ( !FAILED( hr ) && ( nQualityLevel == 16 ) ) + { + nQualityLevel = nQualityLevel - 1; // Highest quality level triggers 16x CSAA + } + else + { + nQualityLevel = 0; // No CSAA + } + } + else // Regular MSAA on any old vendor + { + hr = D3D()->CheckDeviceMultiSampleType( nAdapter, DX8_DEVTYPE, + m_PresentParameters.BackBufferFormat, m_PresentParameters.Windowed, + multiSampleType, &nQualityLevel ); + + nQualityLevel = 0; + } + + if ( !FAILED( hr ) ) + { + m_PresentParameters.MultiSampleType = multiSampleType; + m_PresentParameters.MultiSampleQuality = nQualityLevel; + } + } + else + { + m_PresentParameters.MultiSampleType = D3DMULTISAMPLE_NONE; + m_PresentParameters.MultiSampleQuality = 0; + } +} + + +//----------------------------------------------------------------------------- +// Initializes, shuts down the D3D device +//----------------------------------------------------------------------------- +bool CShaderDeviceDx8::InitDevice( void* hwnd, int nAdapter, const ShaderDeviceInfo_t &info ) +{ + //Debugger(); + + // good place to run some self tests. + //#if OSX + //{ + // extern void GLMgrSelfTests( void ); + // GLMgrSelfTests(); + //} + //#endif + + // windowed + if ( !CreateD3DDevice( (VD3DHWND)hwnd, nAdapter, info ) ) + return false; + + // Hook up our own windows proc to get at messages to tell us when + // other instances of the material system are trying to set the mode + InstallWindowHook( (VD3DHWND)m_hWnd ); + return true; +} + +void CShaderDeviceDx8::ShutdownDevice() +{ + if ( IsPC() && IsActive() ) + { + Dx9Device()->Release(); + +#ifdef STUBD3D + delete ( CStubD3DDevice * )Dx9Device(); +#endif + + g_pD3DDevice = NULL; + + RemoveWindowHook( (VD3DHWND)m_hWnd ); + m_hWnd = 0; + } +} + + +//----------------------------------------------------------------------------- +// Are we using graphics? +//----------------------------------------------------------------------------- +bool CShaderDeviceDx8::IsUsingGraphics() const +{ + //*****LOCK_SHADERAPI(); + return IsActive(); +} + + +//----------------------------------------------------------------------------- +// Returns the current adapter in use +//----------------------------------------------------------------------------- +int CShaderDeviceDx8::GetCurrentAdapter() const +{ + LOCK_SHADERAPI(); + return m_DisplayAdapter; +} + + +//----------------------------------------------------------------------------- +// Returns the current adapter in use +//----------------------------------------------------------------------------- +char *CShaderDeviceDx8::GetDisplayDeviceName() +{ + if( m_sDisplayDeviceName.IsEmpty() ) + { + D3DADAPTER_IDENTIFIER9 ident; + // On Win10, this function is getting called with m_nAdapter still initialized to -1. + // It's failing, and m_sDisplayDeviceName has garbage, and tf2 fails to launch. + // To repro this, run "hl2.exe -dev -fullscreen -game tf" on Win10. + HRESULT hr = D3D()->GetAdapterIdentifier( Max( m_nAdapter, 0 ), 0, &ident ); + if ( FAILED(hr) ) + { + Assert( false ); + ident.DeviceName[0] = 0; + } + m_sDisplayDeviceName = ident.DeviceName; + } + return m_sDisplayDeviceName.GetForModify(); +} + + +//----------------------------------------------------------------------------- +// Use this to spew information about the 3D layer +//----------------------------------------------------------------------------- +void CShaderDeviceDx8::SpewDriverInfo() const +{ + LOCK_SHADERAPI(); + HRESULT hr; + D3DCAPS caps; + D3DADAPTER_IDENTIFIER9 ident; + + RECORD_COMMAND( DX8_GET_DEVICE_CAPS, 0 ); + + RECORD_COMMAND( DX8_GET_ADAPTER_IDENTIFIER, 2 ); + RECORD_INT( m_nAdapter ); + RECORD_INT( 0 ); + + Dx9Device()->GetDeviceCaps( &caps ); + hr = D3D()->GetAdapterIdentifier( m_nAdapter, D3DENUM_WHQL_LEVEL, &ident ); + + Warning("Shader API Driver Info:\n\nDriver : %s Version : %lld\n", + ident.Driver, ident.DriverVersion.QuadPart ); + Warning("Driver Description : %s\n", ident.Description ); + Warning("Chipset version %d %d %d %d\n\n", + ident.VendorId, ident.DeviceId, ident.SubSysId, ident.Revision ); + + ShaderDisplayMode_t mode; + g_pShaderDeviceMgr->GetCurrentModeInfo( &mode, m_nAdapter ); + Warning("Display mode : %d x %d (%s)\n", + mode.m_nWidth, mode.m_nHeight, ImageLoader::GetName( mode.m_Format ) ); + Warning("Vertex Shader Version : %d.%d Pixel Shader Version : %d.%d\n", + (caps.VertexShaderVersion >> 8) & 0xFF, caps.VertexShaderVersion & 0xFF, + (caps.PixelShaderVersion >> 8) & 0xFF, caps.PixelShaderVersion & 0xFF); + Warning("\nDevice Caps :\n"); + Warning("CANBLTSYSTONONLOCAL %s CANRENDERAFTERFLIP %s HWRASTERIZATION %s\n", + (caps.DevCaps & D3DDEVCAPS_CANBLTSYSTONONLOCAL) ? " Y " : " N ", + (caps.DevCaps & D3DDEVCAPS_CANRENDERAFTERFLIP) ? " Y " : " N ", + (caps.DevCaps & D3DDEVCAPS_HWRASTERIZATION) ? " Y " : "*N*" ); + Warning("HWTRANSFORMANDLIGHT %s NPATCHES %s PUREDEVICE %s\n", + (caps.DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT) ? " Y " : " N ", + (caps.DevCaps & D3DDEVCAPS_NPATCHES) ? " Y " : " N ", + (caps.DevCaps & D3DDEVCAPS_PUREDEVICE) ? " Y " : " N " ); + Warning("SEPARATETEXTUREMEMORIES %s TEXTURENONLOCALVIDMEM %s TEXTURESYSTEMMEMORY %s\n", + (caps.DevCaps & D3DDEVCAPS_SEPARATETEXTUREMEMORIES) ? "*Y*" : " N ", + (caps.DevCaps & D3DDEVCAPS_TEXTURENONLOCALVIDMEM) ? " Y " : " N ", + (caps.DevCaps & D3DDEVCAPS_TEXTURESYSTEMMEMORY) ? " Y " : " N " ); + Warning("TEXTUREVIDEOMEMORY %s TLVERTEXSYSTEMMEMORY %s TLVERTEXVIDEOMEMORY %s\n", + (caps.DevCaps & D3DDEVCAPS_TEXTUREVIDEOMEMORY) ? " Y " : "*N*", + (caps.DevCaps & D3DDEVCAPS_TLVERTEXSYSTEMMEMORY) ? " Y " : "*N*", + (caps.DevCaps & D3DDEVCAPS_TLVERTEXVIDEOMEMORY) ? " Y " : " N " ); + + Warning("\nPrimitive Caps :\n"); + Warning("BLENDOP %s CLIPPLANESCALEDPOINTS %s CLIPTLVERTS %s\n", + (caps.PrimitiveMiscCaps & D3DPMISCCAPS_BLENDOP) ? " Y " : " N ", + (caps.PrimitiveMiscCaps & D3DPMISCCAPS_CLIPPLANESCALEDPOINTS) ? " Y " : " N ", + (caps.PrimitiveMiscCaps & D3DPMISCCAPS_CLIPTLVERTS) ? " Y " : " N " ); + Warning("COLORWRITEENABLE %s MASKZ %s TSSARGTEMP %s\n", + (caps.PrimitiveMiscCaps & D3DPMISCCAPS_COLORWRITEENABLE) ? " Y " : " N ", + (caps.PrimitiveMiscCaps & D3DPMISCCAPS_MASKZ) ? " Y " : "*N*", + (caps.PrimitiveMiscCaps & D3DPMISCCAPS_TSSARGTEMP) ? " Y " : " N " ); + + Warning("\nRaster Caps :\n"); + Warning("FOGRANGE %s FOGTABLE %s FOGVERTEX %s ZFOG %s WFOG %s\n", + (caps.RasterCaps & D3DPRASTERCAPS_FOGRANGE) ? " Y " : " N ", + (caps.RasterCaps & D3DPRASTERCAPS_FOGTABLE) ? " Y " : " N ", + (caps.RasterCaps & D3DPRASTERCAPS_FOGVERTEX) ? " Y " : " N ", + (caps.RasterCaps & D3DPRASTERCAPS_ZFOG) ? " Y " : " N ", + (caps.RasterCaps & D3DPRASTERCAPS_WFOG) ? " Y " : " N " ); + Warning("MIPMAPLODBIAS %s WBUFFER %s ZBIAS %s ZTEST %s\n", + (caps.RasterCaps & D3DPRASTERCAPS_MIPMAPLODBIAS) ? " Y " : " N ", + (caps.RasterCaps & D3DPRASTERCAPS_WBUFFER) ? " Y " : " N ", + (caps.RasterCaps & D3DPRASTERCAPS_DEPTHBIAS) ? " Y " : " N ", + (caps.RasterCaps & D3DPRASTERCAPS_ZTEST) ? " Y " : "*N*" ); + + Warning("Size of Texture Memory : %d kb\n", g_pHardwareConfig->Caps().m_TextureMemorySize / 1024 ); + Warning("Max Texture Dimensions : %d x %d\n", + caps.MaxTextureWidth, caps.MaxTextureHeight ); + if (caps.MaxTextureAspectRatio != 0) + Warning("Max Texture Aspect Ratio : *%d*\n", caps.MaxTextureAspectRatio ); + Warning("Max Textures : %d Max Stages : %d\n", + caps.MaxSimultaneousTextures, caps.MaxTextureBlendStages ); + + Warning("\nTexture Caps :\n"); + Warning("ALPHA %s CUBEMAP %s MIPCUBEMAP %s SQUAREONLY %s\n", + (caps.TextureCaps & D3DPTEXTURECAPS_ALPHA) ? " Y " : " N ", + (caps.TextureCaps & D3DPTEXTURECAPS_CUBEMAP) ? " Y " : " N ", + (caps.TextureCaps & D3DPTEXTURECAPS_MIPCUBEMAP) ? " Y " : " N ", + (caps.TextureCaps & D3DPTEXTURECAPS_SQUAREONLY) ? "*Y*" : " N " ); + + Warning( "vendor id: 0x%x\n", g_pHardwareConfig->ActualCaps().m_VendorID ); + Warning( "device id: 0x%x\n", g_pHardwareConfig->ActualCaps().m_DeviceID ); + + Warning( "SHADERAPI CAPS:\n" ); + Warning( "m_NumSamplers: %d\n", g_pHardwareConfig->Caps().m_NumSamplers ); + Warning( "m_NumTextureStages: %d\n", g_pHardwareConfig->Caps().m_NumTextureStages ); + Warning( "m_HasSetDeviceGammaRamp: %s\n", g_pHardwareConfig->Caps().m_HasSetDeviceGammaRamp ? "yes" : "no" ); + Warning( "m_SupportsVertexShaders (1.1): %s\n", g_pHardwareConfig->Caps().m_SupportsVertexShaders ? "yes" : "no" ); + Warning( "m_SupportsVertexShaders_2_0: %s\n", g_pHardwareConfig->Caps().m_SupportsVertexShaders_2_0 ? "yes" : "no" ); + Warning( "m_SupportsPixelShaders (1.1): %s\n", g_pHardwareConfig->Caps().m_SupportsPixelShaders ? "yes" : "no" ); + Warning( "m_SupportsPixelShaders_1_4: %s\n", g_pHardwareConfig->Caps().m_SupportsPixelShaders_1_4 ? "yes" : "no" ); + Warning( "m_SupportsPixelShaders_2_0: %s\n", g_pHardwareConfig->Caps().m_SupportsPixelShaders_2_0 ? "yes" : "no" ); + Warning( "m_SupportsPixelShaders_2_b: %s\n", g_pHardwareConfig->Caps().m_SupportsPixelShaders_2_b ? "yes" : "no" ); + Warning( "m_SupportsShaderModel_3_0: %s\n", g_pHardwareConfig->Caps().m_SupportsShaderModel_3_0 ? "yes" : "no" ); + + switch( g_pHardwareConfig->Caps().m_SupportsCompressedTextures ) + { + case COMPRESSED_TEXTURES_ON: + Warning( "m_SupportsCompressedTextures: COMPRESSED_TEXTURES_ON\n" ); + break; + case COMPRESSED_TEXTURES_OFF: + Warning( "m_SupportsCompressedTextures: COMPRESSED_TEXTURES_ON\n" ); + break; + case COMPRESSED_TEXTURES_NOT_INITIALIZED: + Warning( "m_SupportsCompressedTextures: COMPRESSED_TEXTURES_NOT_INITIALIZED\n" ); + break; + default: + Assert( 0 ); + break; + } + Warning( "m_SupportsCompressedVertices: %d\n", g_pHardwareConfig->Caps().m_SupportsCompressedVertices ); + Warning( "m_bSupportsAnisotropicFiltering: %s\n", g_pHardwareConfig->Caps().m_bSupportsAnisotropicFiltering ? "yes" : "no" ); + Warning( "m_nMaxAnisotropy: %d\n", g_pHardwareConfig->Caps().m_nMaxAnisotropy ); + Warning( "m_MaxTextureWidth: %d\n", g_pHardwareConfig->Caps().m_MaxTextureWidth ); + Warning( "m_MaxTextureHeight: %d\n", g_pHardwareConfig->Caps().m_MaxTextureHeight ); + Warning( "m_MaxTextureAspectRatio: %d\n", g_pHardwareConfig->Caps().m_MaxTextureAspectRatio ); + Warning( "m_MaxPrimitiveCount: %d\n", g_pHardwareConfig->Caps().m_MaxPrimitiveCount ); + Warning( "m_ZBiasAndSlopeScaledDepthBiasSupported: %s\n", g_pHardwareConfig->Caps().m_ZBiasAndSlopeScaledDepthBiasSupported ? "yes" : "no" ); + Warning( "m_SupportsMipmapping: %s\n", g_pHardwareConfig->Caps().m_SupportsMipmapping ? "yes" : "no" ); + Warning( "m_SupportsOverbright: %s\n", g_pHardwareConfig->Caps().m_SupportsOverbright ? "yes" : "no" ); + Warning( "m_SupportsCubeMaps: %s\n", g_pHardwareConfig->Caps().m_SupportsCubeMaps ? "yes" : "no" ); + Warning( "m_NumPixelShaderConstants: %d\n", g_pHardwareConfig->Caps().m_NumPixelShaderConstants ); + Warning( "m_NumVertexShaderConstants: %d\n", g_pHardwareConfig->Caps().m_NumVertexShaderConstants ); + Warning( "m_NumBooleanVertexShaderConstants: %d\n", g_pHardwareConfig->Caps().m_NumBooleanVertexShaderConstants ); + Warning( "m_NumIntegerVertexShaderConstants: %d\n", g_pHardwareConfig->Caps().m_NumIntegerVertexShaderConstants ); + Warning( "m_TextureMemorySize: %d\n", g_pHardwareConfig->Caps().m_TextureMemorySize ); + Warning( "m_MaxNumLights: %d\n", g_pHardwareConfig->Caps().m_MaxNumLights ); + Warning( "m_SupportsHardwareLighting: %s\n", g_pHardwareConfig->Caps().m_SupportsHardwareLighting ? "yes" : "no" ); + Warning( "m_MaxBlendMatrices: %d\n", g_pHardwareConfig->Caps().m_MaxBlendMatrices ); + Warning( "m_MaxBlendMatrixIndices: %d\n", g_pHardwareConfig->Caps().m_MaxBlendMatrixIndices ); + Warning( "m_MaxVertexShaderBlendMatrices: %d\n", g_pHardwareConfig->Caps().m_MaxVertexShaderBlendMatrices ); + Warning( "m_SupportsMipmappedCubemaps: %s\n", g_pHardwareConfig->Caps().m_SupportsMipmappedCubemaps ? "yes" : "no" ); + Warning( "m_SupportsNonPow2Textures: %s\n", g_pHardwareConfig->Caps().m_SupportsNonPow2Textures ? "yes" : "no" ); + Warning( "m_nDXSupportLevel: %d\n", g_pHardwareConfig->Caps().m_nDXSupportLevel ); + Warning( "m_PreferDynamicTextures: %s\n", g_pHardwareConfig->Caps().m_PreferDynamicTextures ? "yes" : "no" ); + Warning( "m_HasProjectedBumpEnv: %s\n", g_pHardwareConfig->Caps().m_HasProjectedBumpEnv ? "yes" : "no" ); + Warning( "m_MaxUserClipPlanes: %d\n", g_pHardwareConfig->Caps().m_MaxUserClipPlanes ); + Warning( "m_SupportsSRGB: %s\n", g_pHardwareConfig->Caps().m_SupportsSRGB ? "yes" : "no" ); + switch( g_pHardwareConfig->Caps().m_HDRType ) + { + case HDR_TYPE_NONE: + Warning( "m_HDRType: HDR_TYPE_NONE\n" ); + break; + case HDR_TYPE_INTEGER: + Warning( "m_HDRType: HDR_TYPE_INTEGER\n" ); + break; + case HDR_TYPE_FLOAT: + Warning( "m_HDRType: HDR_TYPE_FLOAT\n" ); + break; + default: + Assert( 0 ); + break; + } + Warning( "m_bSupportsSpheremapping: %s\n", g_pHardwareConfig->Caps().m_bSupportsSpheremapping ? "yes" : "no" ); + Warning( "m_UseFastClipping: %s\n", g_pHardwareConfig->Caps().m_UseFastClipping ? "yes" : "no" ); + Warning( "m_pShaderDLL: %s\n", g_pHardwareConfig->Caps().m_pShaderDLL ); + Warning( "m_bNeedsATICentroidHack: %s\n", g_pHardwareConfig->Caps().m_bNeedsATICentroidHack ? "yes" : "no" ); + Warning( "m_bDisableShaderOptimizations: %s\n", g_pHardwareConfig->Caps().m_bDisableShaderOptimizations ? "yes" : "no" ); + Warning( "m_bColorOnSecondStream: %s\n", g_pHardwareConfig->Caps().m_bColorOnSecondStream ? "yes" : "no" ); + Warning( "m_MaxSimultaneousRenderTargets: %d\n", g_pHardwareConfig->Caps().m_MaxSimultaneousRenderTargets ); +} + + +//----------------------------------------------------------------------------- +// Back buffer information +//----------------------------------------------------------------------------- +ImageFormat CShaderDeviceDx8::GetBackBufferFormat() const +{ + return ImageLoader::D3DFormatToImageFormat( m_PresentParameters.BackBufferFormat ); +} + +void CShaderDeviceDx8::GetBackBufferDimensions( int& width, int& height ) const +{ + width = m_PresentParameters.BackBufferWidth; + height = m_PresentParameters.BackBufferHeight; +} + + +//----------------------------------------------------------------------------- +// Detects support for CreateQuery +//----------------------------------------------------------------------------- +void CShaderDeviceDx8::DetectQuerySupport( IDirect3DDevice9 *pD3DDevice ) +{ + // Do I need to detect whether this device supports CreateQuery before creating it? + if ( m_DeviceSupportsCreateQuery != -1 ) + return; + + IDirect3DQuery9 *pQueryObject = NULL; + + // Detect whether query is supported by creating and releasing: + HRESULT hr = pD3DDevice->CreateQuery( D3DQUERYTYPE_EVENT, &pQueryObject ); + if ( !FAILED(hr) && pQueryObject ) + { + pQueryObject->Release(); + m_DeviceSupportsCreateQuery = 1; + } + else + { + m_DeviceSupportsCreateQuery = 0; + } +} + + +const char *GetD3DErrorText( HRESULT hr ) +{ + const char *pszMoreInfo = NULL; + +#if defined( _WIN32 ) && !defined(DX_TO_GL_ABSTRACTION) + switch ( hr ) + { + case D3DERR_WRONGTEXTUREFORMAT: + pszMoreInfo = "D3DERR_WRONGTEXTUREFORMAT: The pixel format of the texture surface is not valid."; + break; + case D3DERR_UNSUPPORTEDCOLOROPERATION: + pszMoreInfo = "D3DERR_UNSUPPORTEDCOLOROPERATION: The device does not support a specified texture-blending operation for color values."; + break; + case D3DERR_UNSUPPORTEDCOLORARG: + pszMoreInfo = "D3DERR_UNSUPPORTEDCOLORARG: The device does not support a specified texture-blending argument for color values."; + break; + case D3DERR_UNSUPPORTEDALPHAOPERATION: + pszMoreInfo = "D3DERR_UNSUPPORTEDALPHAOPERATION: The device does not support a specified texture-blending operation for the alpha channel."; + break; + case D3DERR_UNSUPPORTEDALPHAARG: + pszMoreInfo = "D3DERR_UNSUPPORTEDALPHAARG: The device does not support a specified texture-blending argument for the alpha channel."; + break; + case D3DERR_TOOMANYOPERATIONS: + pszMoreInfo = "D3DERR_TOOMANYOPERATIONS: The application is requesting more texture-filtering operations than the device supports."; + break; + case D3DERR_CONFLICTINGTEXTUREFILTER: + pszMoreInfo = "D3DERR_CONFLICTINGTEXTUREFILTER: The current texture filters cannot be used together."; + break; + case D3DERR_UNSUPPORTEDFACTORVALUE: + pszMoreInfo = "D3DERR_UNSUPPORTEDFACTORVALUE: The device does not support the specified texture factor value."; + break; + case D3DERR_CONFLICTINGRENDERSTATE: + pszMoreInfo = "D3DERR_CONFLICTINGRENDERSTATE: The currently set render states cannot be used together."; + break; + case D3DERR_UNSUPPORTEDTEXTUREFILTER: + pszMoreInfo = "D3DERR_UNSUPPORTEDTEXTUREFILTER: The device does not support the specified texture filter."; + break; + case D3DERR_CONFLICTINGTEXTUREPALETTE: + pszMoreInfo = "D3DERR_CONFLICTINGTEXTUREPALETTE: The current textures cannot be used simultaneously."; + break; + case D3DERR_DRIVERINTERNALERROR: + pszMoreInfo = "D3DERR_DRIVERINTERNALERROR: Internal driver error."; + break; + case D3DERR_NOTFOUND: + pszMoreInfo = "D3DERR_NOTFOUND: The requested item was not found."; + break; + case D3DERR_DEVICELOST: + pszMoreInfo = "D3DERR_DEVICELOST: The device has been lost but cannot be reset at this time. Therefore, rendering is not possible."; + break; + case D3DERR_DEVICENOTRESET: + pszMoreInfo = "D3DERR_DEVICENOTRESET: The device has been lost."; + break; + case D3DERR_NOTAVAILABLE: + pszMoreInfo = "D3DERR_NOTAVAILABLE: This device does not support the queried technique."; + break; + case D3DERR_OUTOFVIDEOMEMORY: + pszMoreInfo = "D3DERR_OUTOFVIDEOMEMORY: Direct3D does not have enough display memory to perform the operation. The device is using more resources in a single scene than can fit simultaneously into video memory."; + break; + case D3DERR_INVALIDDEVICE: + pszMoreInfo = "D3DERR_INVALIDDEVICE: The requested device type is not valid."; + break; + case D3DERR_INVALIDCALL: + pszMoreInfo = "D3DERR_INVALIDCALL: The method call is invalid."; + break; + case D3DERR_DRIVERINVALIDCALL: + pszMoreInfo = "D3DERR_DRIVERINVALIDCALL"; + break; + case D3DERR_WASSTILLDRAWING: + pszMoreInfo = "D3DERR_WASSTILLDRAWING: The previous blit operation that is transferring information to or from this surface is incomplete."; + break; + } +#endif // _WIN32 + + return pszMoreInfo; +} + + +//----------------------------------------------------------------------------- +// Actually creates the D3D Device once the present parameters are set up +//----------------------------------------------------------------------------- +IDirect3DDevice9* CShaderDeviceDx8::InvokeCreateDevice( void* hWnd, int nAdapter, DWORD deviceCreationFlags ) +{ + IDirect3DDevice9 *pD3DDevice = NULL; + D3DDEVTYPE devType = DX8_DEVTYPE; + +#if NVPERFHUD + nAdapter = D3D()->GetAdapterCount()-1; + devType = D3DDEVTYPE_REF; + deviceCreationFlags = D3DCREATE_FPU_PRESERVE | D3DCREATE_HARDWARE_VERTEXPROCESSING; +#endif + +#if 1 // with the changes for opengl to enable threading, we no longer need the d3d device to have threading guards +#ifndef _X360 + // Create the device with multi-threaded safeguards if we're using mat_queue_mode 2. + // The logic to enable multithreaded rendering happens well after the device has been created, + // so we replicate some of that logic here. + ConVarRef mat_queue_mode( "mat_queue_mode" ); + if ( mat_queue_mode.GetInt() == 2 || + ( mat_queue_mode.GetInt() == -2 && GetCPUInformation()->m_nPhysicalProcessors >= 2 ) || + ( mat_queue_mode.GetInt() == -1 && GetCPUInformation()->m_nPhysicalProcessors >= 2 ) ) + { + deviceCreationFlags |= D3DCREATE_MULTITHREADED; + } +#endif +#endif + +#ifdef ENABLE_NULLREF_DEVICE_SUPPORT + devType = CommandLine()->FindParm( "-nulldevice" ) ? D3DDEVTYPE_NULLREF: devType; +#endif + + HRESULT hr = D3D()->CreateDevice( nAdapter, devType, + (VD3DHWND)hWnd, deviceCreationFlags, &m_PresentParameters, &pD3DDevice ); + + if ( !FAILED( hr ) && pD3DDevice ) + return pD3DDevice; + + if ( !IsPC() ) + return NULL; + + // try again, other applications may be taking their time + Sleep( 1000 ); + hr = D3D()->CreateDevice( nAdapter, devType, + (VD3DHWND)hWnd, deviceCreationFlags, &m_PresentParameters, &pD3DDevice ); + if ( !FAILED( hr ) && pD3DDevice ) + return pD3DDevice; + + // in this case, we actually are allocating too much memory.... + // This will cause us to use less buffers... + if ( m_PresentParameters.Windowed ) + { + m_PresentParameters.SwapEffect = D3DSWAPEFFECT_COPY; + m_PresentParameters.BackBufferCount = 0; + hr = D3D()->CreateDevice( nAdapter, devType, + (VD3DHWND)hWnd, deviceCreationFlags, &m_PresentParameters, &pD3DDevice ); + } + if ( !FAILED( hr ) && pD3DDevice ) + return pD3DDevice; + + const char *pszMoreInfo = NULL; + switch ( hr ) + { +#ifdef _WIN32 + case D3DERR_INVALIDCALL: + // Override the error text for this error since it has a known meaning for CreateDevice failures. + pszMoreInfo = "D3DERR_INVALIDCALL: The device or the device driver may not support Direct3D or may not support the resolution or color depth specified."; + break; +#endif // _WIN32 + default: + pszMoreInfo = GetD3DErrorText( hr ); + break; + } + + // Otherwise we failed, show a message and shutdown + if ( pszMoreInfo ) + { + DWarning( "init", 0, "Failed to create %s device!\nError 0x%lX: %s\n\nPlease see the following for more info.\n" + "http://support.steampowered.com/cgi-bin/steampowered.cfg/php/enduser/std_adp.php?p_faqid=772\n", IsOpenGL() ? "OpenGL" : "D3D", hr, pszMoreInfo ); + } + else + { + DWarning( "init", 0, "Failed to create %s device!\nError 0x%lX.\n\nPlease see the following for more info.\n" + "http://support.steampowered.com/cgi-bin/steampowered.cfg/php/enduser/std_adp.php?p_faqid=772\n", IsOpenGL() ? "OpenGL" : "D3D", hr ); + } + + return NULL; +} + + +//----------------------------------------------------------------------------- +// Creates the D3D Device +//----------------------------------------------------------------------------- +bool CShaderDeviceDx8::CreateD3DDevice( void* pHWnd, int nAdapter, const ShaderDeviceInfo_t &info ) +{ + Assert( info.m_nVersion == SHADER_DEVICE_INFO_VERSION ); + + MEM_ALLOC_CREDIT_( __FILE__ ": D3D Device" ); + + VD3DHWND hWnd = (VD3DHWND)pHWnd; + +#if ( !defined( PIX_INSTRUMENTATION ) && !defined( _X360 ) && !defined( NVPERFHUD ) ) + D3DPERF_SetOptions(1); // Explicitly disallow PIX instrumented profiling in external builds +#endif + + // Get some caps.... + D3DCAPS caps; + HRESULT hr = D3D()->GetDeviceCaps( nAdapter, DX8_DEVTYPE, &caps ); + if ( FAILED( hr ) ) + return false; + + // Determine the adapter format + ShaderDisplayMode_t mode; + g_pShaderDeviceMgrDx8->GetCurrentModeInfo( &mode, nAdapter ); + m_AdapterFormat = mode.m_Format; + + // FIXME: Need to do this prior to SetPresentParameters. Fix. + // Make it part of HardwareCaps_t + InitializeColorInformation( nAdapter, DX8_DEVTYPE, m_AdapterFormat ); + + const HardwareCaps_t &adapterCaps = g_ShaderDeviceMgrDx8.GetHardwareCaps( nAdapter ); + DWORD deviceCreationFlags = ComputeDeviceCreationFlags( caps, adapterCaps.m_bSoftwareVertexProcessing ); + SetPresentParameters( hWnd, nAdapter, info ); + + // Tell all other instances of the material system to let go of memory + SendIPCMessage( RELEASE_MESSAGE ); + + // Creates the device + IDirect3DDevice9 *pD3DDevice = InvokeCreateDevice( pHWnd, nAdapter, deviceCreationFlags ); + + if ( !pD3DDevice ) + return false; + + // Check to see if query is supported + DetectQuerySupport( pD3DDevice ); + +#ifdef STUBD3D + Dx9Device() = new CStubD3DDevice( pD3DDevice, g_pFullFileSystem ); +#else + g_pD3DDevice = pD3DDevice; +#endif + +#if defined( _X360 ) + // Create the depth buffer, created manually to enable hierarchical z + { + D3DSURFACE_PARAMETERS DepthStencilParams; + + // Depth is immediately after the back buffer in EDRAM + // allocate the hierarchical z tiles at the end of the area so all other allocations can trivially allocate at 0 + DepthStencilParams.Base = XGSurfaceSize( + m_PresentParameters.BackBufferWidth, + m_PresentParameters.BackBufferHeight, + m_PresentParameters.BackBufferFormat, + m_PresentParameters.MultiSampleType ); + DepthStencilParams.ColorExpBias = 0; + DepthStencilParams.HierarchicalZBase = GPU_HIERARCHICAL_Z_TILES - XGHierarchicalZSize( m_PresentParameters.BackBufferWidth, m_PresentParameters.BackBufferHeight, m_PresentParameters.MultiSampleType ); + + IDirect3DSurface *pDepthStencilSurface = NULL; + hr = Dx9Device()->CreateDepthStencilSurface( + m_PresentParameters.BackBufferWidth, + m_PresentParameters.BackBufferHeight, + m_PresentParameters.AutoDepthStencilFormat, + m_PresentParameters.MultiSampleType, + m_PresentParameters.MultiSampleQuality, + TRUE, + &pDepthStencilSurface, + &DepthStencilParams ); + Assert( SUCCEEDED( hr ) ); + if ( FAILED( hr ) ) + return false; + + hr = Dx9Device()->SetDepthStencilSurface( pDepthStencilSurface ); + Assert( SUCCEEDED( hr ) ); + if ( FAILED( hr ) ) + return false; + } + + // Initialize XUI, needed for TTF font rasterization + // xui requires and shares our d3d device + { + hr = XuiRenderInitShared( pD3DDevice, &m_PresentParameters, XuiD3DXTextureLoader ); + if ( FAILED( hr ) ) + return false; + + XUIInitParams xuiInit; + XUI_INIT_PARAMS( xuiInit ); + xuiInit.dwFlags = XUI_INIT_PARAMS_FLAGS_NONE; + xuiInit.pHooks = NULL; + hr = XuiInit( &xuiInit ); + if ( FAILED( hr ) ) + return false; + + hr = XuiRenderCreateDC( &m_hDC ); + if ( FAILED( hr ) ) + return false; + } +#endif + + // CheckDeviceLost(); + + // Tell all other instances of the material system it's ok to grab memory + SendIPCMessage( REACQUIRE_MESSAGE ); + + m_hWnd = pHWnd; + m_nAdapter = m_DisplayAdapter = nAdapter; + m_DeviceState = DEVICE_STATE_OK; + m_bIsMinimized = false; + m_bQueuedDeviceLost = false; + + m_IsResizing = info.m_bWindowed && info.m_bResizing; + + // This is our current view. + m_ViewHWnd = hWnd; + GetWindowSize( m_nWindowWidth, m_nWindowHeight ); + + g_pHardwareConfig->SetupHardwareCaps( info, g_ShaderDeviceMgrDx8.GetHardwareCaps( nAdapter ) ); + + // FIXME: Bake this into hardware config + // What texture formats do we support? + if ( D3DSupportsCompressedTextures() ) + { + g_pHardwareConfig->ActualCapsForEdit().m_SupportsCompressedTextures = COMPRESSED_TEXTURES_ON; + g_pHardwareConfig->CapsForEdit().m_SupportsCompressedTextures = COMPRESSED_TEXTURES_ON; + } + else + { + g_pHardwareConfig->ActualCapsForEdit().m_SupportsCompressedTextures = COMPRESSED_TEXTURES_OFF; + g_pHardwareConfig->CapsForEdit().m_SupportsCompressedTextures = COMPRESSED_TEXTURES_OFF; + } + + return ( !FAILED( hr ) ); +} + + +//----------------------------------------------------------------------------- +// Frame sync +//----------------------------------------------------------------------------- +void CShaderDeviceDx8::AllocFrameSyncTextureObject() +{ + if ( IsX360() ) + return; + + FreeFrameSyncTextureObject(); + + // Create a tiny managed texture. + HRESULT hr = Dx9Device()->CreateTexture( + 1, 1, // width, height + 0, // levels + D3DUSAGE_DYNAMIC, // usage + D3DFMT_A8R8G8B8, // format + D3DPOOL_DEFAULT, + &m_pFrameSyncTexture, + NULL ); + if ( FAILED( hr ) ) + { + m_pFrameSyncTexture = NULL; + } +} + +void CShaderDeviceDx8::FreeFrameSyncTextureObject() +{ + if ( IsX360() ) + return; + + if ( m_pFrameSyncTexture ) + { + m_pFrameSyncTexture->Release(); + m_pFrameSyncTexture = NULL; + } +} + +void CShaderDeviceDx8::AllocFrameSyncObjects( void ) +{ + if ( IsX360() ) + return; + + if ( mat_debugalttab.GetBool() ) + { + Warning( "mat_debugalttab: CShaderAPIDX8::AllocFrameSyncObjects\n" ); + } + + // Allocate the texture for frame syncing in case we force that to be on. + AllocFrameSyncTextureObject(); + + if ( m_DeviceSupportsCreateQuery == 0 ) + { + for ( int i = 0; i < ARRAYSIZE(m_pFrameSyncQueryObject); i++ ) + { + m_pFrameSyncQueryObject[i] = NULL; + m_bQueryIssued[i] = false; + } + return; + } + + // FIXME FIXME FIXME!!!!! Need to record this. + for ( int i = 0; i < ARRAYSIZE(m_pFrameSyncQueryObject); i++ ) + { + HRESULT hr = Dx9Device()->CreateQuery( D3DQUERYTYPE_EVENT, &m_pFrameSyncQueryObject[i] ); + if( hr == D3DERR_NOTAVAILABLE ) + { + Warning( "D3DQUERYTYPE_EVENT not available on this driver\n" ); + Assert( m_pFrameSyncQueryObject[i] == NULL ); + } + else + { + Assert( hr == D3D_OK ); + Assert( m_pFrameSyncQueryObject[i] ); + m_pFrameSyncQueryObject[i]->Issue( D3DISSUE_END ); + m_bQueryIssued[i] = true; + } + } +} + +void CShaderDeviceDx8::FreeFrameSyncObjects( void ) +{ + if ( IsX360() ) + return; + + if ( mat_debugalttab.GetBool() ) + { + Warning( "mat_debugalttab: CShaderAPIDX8::FreeFrameSyncObjects\n" ); + } + + FreeFrameSyncTextureObject(); + + // FIXME FIXME FIXME!!!!! Need to record this. + for ( int i = 0; i < ARRAYSIZE(m_pFrameSyncQueryObject); i++ ) + { + if ( m_pFrameSyncQueryObject[i] ) + { + if ( m_bQueryIssued[i] ) + { + tmZone( TELEMETRY_LEVEL1, TMZF_NONE, "D3DQueryGetData %t", tmSendCallStack( TELEMETRY_LEVEL0, 0 ) ); + + double flStartTime = Plat_FloatTime(); + BOOL dummyData = 0; + HRESULT hr = S_OK; + + // Make every attempt (within 2 seconds) to get the result from the query. Doing so may prevent + // crashes in the driver if we try to release outstanding queries. + do + { + hr = m_pFrameSyncQueryObject[i]->GetData( &dummyData, sizeof( dummyData ), D3DGETDATA_FLUSH ); + double flCurrTime = Plat_FloatTime(); + + // don't wait more than 2 seconds for these + if ( flCurrTime - flStartTime > 2.00 ) + break; + } while ( hr == S_FALSE ); + } +#ifdef DBGFLAG_ASSERT + int nRetVal = +#endif + m_pFrameSyncQueryObject[i]->Release(); + Assert( nRetVal == 0 ); + m_pFrameSyncQueryObject[i] = NULL; + m_bQueryIssued[i] = false; + } + } +} + + +//----------------------------------------------------------------------------- +// Occurs when another application is initializing +//----------------------------------------------------------------------------- +void CShaderDeviceDx8::OtherAppInitializing( bool initializing ) +{ + if ( !ThreadOwnsDevice() || !ThreadInMainThread() ) + { + if ( initializing ) + { + ShaderUtil()->OnThreadEvent( SHADER_THREAD_OTHER_APP_START ); + } + else + { + ShaderUtil()->OnThreadEvent( SHADER_THREAD_OTHER_APP_END ); + } + return; + } + Assert( m_bOtherAppInitializing != initializing ); + + if ( !IsDeactivated() ) + { + Dx9Device()->EndScene(); + } + + // NOTE: OtherApp is set in this way because we need to know we're + // active as we release and restore everything + CheckDeviceLost( initializing ); + + if ( !IsDeactivated() ) + { + Dx9Device()->BeginScene(); + } +} + + +void CShaderDeviceDx8::HandleThreadEvent( uint32 threadEvent ) +{ + Assert(ThreadOwnsDevice()); + switch ( threadEvent ) + { + case SHADER_THREAD_OTHER_APP_START: + OtherAppInitializing(true); + break; + case SHADER_THREAD_RELEASE_RESOURCES: + ReleaseResources(); + break; + case SHADER_THREAD_EVICT_RESOURCES: + EvictManagedResourcesInternal(); + break; + case SHADER_THREAD_RESET_RENDER_STATE: + ResetRenderState(); + break; + case SHADER_THREAD_ACQUIRE_RESOURCES: + ReacquireResources(); + break; + case SHADER_THREAD_OTHER_APP_END: + OtherAppInitializing(false); + break; + + } +} + +//----------------------------------------------------------------------------- +// We lost the device, but we have a chance to recover +//----------------------------------------------------------------------------- +bool CShaderDeviceDx8::TryDeviceReset() +{ + if ( IsX360() ) + return true; + + // Don't try to reset the device until we're sure our resources have been released + if ( !m_bResourcesReleased ) + { + return false; + } + + // FIXME: Make this rebuild the Dx9Device from scratch! + // Helps with compatibility + HRESULT hr = Dx9Device()->Reset( &m_PresentParameters ); + bool bResetSuccess = !FAILED(hr); + +#if defined(IS_WINDOWS_PC) && defined(SHADERAPIDX9) + if ( bResetSuccess && g_ShaderDeviceUsingD3D9Ex ) + { + bResetSuccess = SUCCEEDED( Dx9Device()->TestCooperativeLevel() ); + if ( bResetSuccess ) + { + Warning("video driver has crashed and been reset, re-uploading resources now"); + } + } +#endif + + if ( bResetSuccess ) + m_bResourcesReleased = false; + return bResetSuccess; +} + + +//----------------------------------------------------------------------------- +// Release, reacquire resources +//----------------------------------------------------------------------------- +void CShaderDeviceDx8::ReleaseResources() +{ + if ( !ThreadOwnsDevice() || !ThreadInMainThread() ) + { + // Set our resources as not being released yet. + // We reset this in two places since release resources can be called without a call to TryDeviceReset. + m_bResourcesReleased = false; + ShaderUtil()->OnThreadEvent( SHADER_THREAD_RELEASE_RESOURCES ); + return; + } + + // Only the initial "ReleaseResources" actually has effect + if ( m_numReleaseResourcesRefCount ++ != 0 ) + { + Warning( "ReleaseResources has no effect, now at level %d.\n", m_numReleaseResourcesRefCount ); + DevWarning( "ReleaseResources called twice is a bug: use IsDeactivated to check for a valid device.\n" ); + Assert( 0 ); + return; + } + + LOCK_SHADERAPI(); + CPixEvent( PIX_VALVE_ORANGE, "ReleaseResources" ); + + FreeFrameSyncObjects(); + FreeNonInteractiveRefreshObjects(); + ShaderUtil()->ReleaseShaderObjects(); + MeshMgr()->ReleaseBuffers(); + g_pShaderAPI->ReleaseShaderObjects(); + +#ifdef _DEBUG + if ( MeshMgr()->BufferCount() != 0 ) + { + for( int i = 0; i < MeshMgr()->BufferCount(); i++ ) + { + } + } +#endif + + // All meshes cleaned up? + Assert( MeshMgr()->BufferCount() == 0 ); + + // Signal that our resources have been released so that we can try to reset the device + m_bResourcesReleased = true; +} + + +void CShaderDeviceDx8::ReacquireResources() +{ + ReacquireResourcesInternal(); +} + +void CShaderDeviceDx8::ReacquireResourcesInternal( bool bResetState, bool bForceReacquire, char const *pszForceReason ) +{ + if ( !ThreadOwnsDevice() || !ThreadInMainThread() ) + { + if ( bResetState ) + { + ShaderUtil()->OnThreadEvent( SHADER_THREAD_RESET_RENDER_STATE ); + } + ShaderUtil()->OnThreadEvent( SHADER_THREAD_ACQUIRE_RESOURCES ); + return; + } + if ( bForceReacquire ) + { + // If we are forcing reacquire then warn if release calls are remaining unpaired + if ( m_numReleaseResourcesRefCount > 1 ) + { + Warning( "Forcefully resetting device (%s), resources release level was %d.\n", pszForceReason ? pszForceReason : "unspecified", m_numReleaseResourcesRefCount ); + Assert( 0 ); + } + m_numReleaseResourcesRefCount = 0; + } + else + { + // Only the final "ReacquireResources" actually has effect + if ( -- m_numReleaseResourcesRefCount != 0 ) + { + Warning( "ReacquireResources has no effect, now at level %d.\n", m_numReleaseResourcesRefCount ); + DevWarning( "ReacquireResources being discarded is a bug: use IsDeactivated to check for a valid device.\n" ); + Assert( 0 ); + + if ( m_numReleaseResourcesRefCount < 0 ) + { + m_numReleaseResourcesRefCount = 0; + } + + return; + } + } + + if ( bResetState ) + { + ResetRenderState(); + } + + LOCK_SHADERAPI(); + CPixEvent event( PIX_VALVE_ORANGE, "ReacquireResources" ); + + g_pShaderAPI->RestoreShaderObjects(); + AllocFrameSyncObjects(); + AllocNonInteractiveRefreshObjects(); + MeshMgr()->RestoreBuffers(); + ShaderUtil()->RestoreShaderObjects( CShaderDeviceMgrBase::ShaderInterfaceFactory ); +} + + +//----------------------------------------------------------------------------- +// Changes the window size +//----------------------------------------------------------------------------- +bool CShaderDeviceDx8::ResizeWindow( const ShaderDeviceInfo_t &info ) +{ + if ( IsX360() ) + return false; + + m_bPendingVideoModeChange = false; + + // We don't need to do crap if the window was set up to set up + // to be resizing... + if ( info.m_bResizing ) + return false; + + g_pShaderDeviceMgr->InvokeModeChangeCallbacks(); + + ReleaseResources(); + + SetPresentParameters( (VD3DHWND)m_hWnd, m_DisplayAdapter, info ); + HRESULT hr = Dx9Device()->Reset( &m_PresentParameters ); + if ( FAILED( hr ) ) + { + Warning( "ResizeWindow: Reset failed, hr = 0x%08lX.\n", hr ); + return false; + } + else + { + ReacquireResourcesInternal( true, true, "ResizeWindow" ); + } + + return true; +} + + +//----------------------------------------------------------------------------- +// Queue up the fact that the device was lost +//----------------------------------------------------------------------------- +void CShaderDeviceDx8::MarkDeviceLost( ) +{ + if ( IsX360() ) + return; + + m_bQueuedDeviceLost = true; +} + + +//----------------------------------------------------------------------------- +// Checks if the device was lost +//----------------------------------------------------------------------------- +#if defined( _DEBUG ) && !defined( _X360 ) +ConVar mat_forcelostdevice( "mat_forcelostdevice", "0" ); +#endif + +void CShaderDeviceDx8::CheckDeviceLost( bool bOtherAppInitializing ) +{ +#if !defined( _X360 ) + // FIXME: We could also queue up if WM_SIZE changes and look at that + // but that seems to only make sense if we have resizable windows where + // we do *not* allocate buffers as large as the entire current video mode + // which we're not doing +#ifdef _WIN32 + m_bIsMinimized = ( static_cast(IsIconic( ( HWND )m_hWnd )) == (BOOL)TRUE ); +#else + m_bIsMinimized = ( IsIconic( (VD3DHWND)m_hWnd ) == TRUE ); +#endif + m_bOtherAppInitializing = bOtherAppInitializing; + +#ifdef _DEBUG + if ( mat_forcelostdevice.GetBool() ) + { + mat_forcelostdevice.SetValue( 0 ); + MarkDeviceLost(); + } +#endif + + HRESULT hr = D3D_OK; +#if defined(IS_WINDOWS_PC) && defined(SHADERAPIDX9) + if ( g_ShaderDeviceUsingD3D9Ex && m_DeviceState == DEVICE_STATE_OK ) + { + // Steady state - PresentEx return value will mark us lost if necessary. + // We do not care if we are minimized in this state. + m_bIsMinimized = false; + } + else +#endif + { + RECORD_COMMAND( DX8_TEST_COOPERATIVE_LEVEL, 0 ); + hr = Dx9Device()->TestCooperativeLevel(); + } + + // If some other call returned device lost previously in the frame, spoof the return value from TCL + if ( m_bQueuedDeviceLost ) + { + hr = (hr != D3D_OK) ? hr : D3DERR_DEVICENOTRESET; + m_bQueuedDeviceLost = false; + } + + if ( m_DeviceState == DEVICE_STATE_OK ) + { + // We can transition out of ok if bOtherAppInitializing is set + // or if we become minimized, or if TCL returns anything other than D3D_OK. + if ( ( hr != D3D_OK ) || m_bIsMinimized ) + { + // purge unreferenced materials + g_pShaderUtil->UncacheUnusedMaterials( true ); + + // We were ok, now we're not. Release resources + ReleaseResources(); + m_DeviceState = DEVICE_STATE_LOST_DEVICE; + } + else if ( bOtherAppInitializing ) + { + // purge unreferenced materials + g_pShaderUtil->UncacheUnusedMaterials( true ); + + // We were ok, now we're not. Release resources + ReleaseResources(); + m_DeviceState = DEVICE_STATE_OTHER_APP_INIT; + } + } + + // Immediately checking devicelost after ok helps in the case where we got D3DERR_DEVICENOTRESET + // in which case we want to immdiately try to switch out of DEVICE_STATE_LOST and into DEVICE_STATE_NEEDS_RESET + if ( m_DeviceState == DEVICE_STATE_LOST_DEVICE ) + { + // We can only try to reset if we're not minimized and not lost + if ( !m_bIsMinimized && (hr != D3DERR_DEVICELOST) ) + { + m_DeviceState = DEVICE_STATE_NEEDS_RESET; + } + } + + // Immediately checking needs reset also helps for the case where we got D3DERR_DEVICENOTRESET + if ( m_DeviceState == DEVICE_STATE_NEEDS_RESET ) + { + if ( ( hr == D3DERR_DEVICELOST ) || m_bIsMinimized ) + { + m_DeviceState = DEVICE_STATE_LOST_DEVICE; + } + else + { + bool bResetSucceeded = TryDeviceReset(); + if ( bResetSucceeded ) + { + if ( !bOtherAppInitializing ) + { + m_DeviceState = DEVICE_STATE_OK; + + // We were bad, now we're ok. Restore resources and reset render state. + ReacquireResourcesInternal( true, true, "NeedsReset" ); + } + else + { + m_DeviceState = DEVICE_STATE_OTHER_APP_INIT; + } + } + } + } + + if ( m_DeviceState == DEVICE_STATE_OTHER_APP_INIT ) + { + if ( ( hr != D3D_OK ) || m_bIsMinimized ) + { + m_DeviceState = DEVICE_STATE_LOST_DEVICE; + } + else if ( !bOtherAppInitializing ) + { + m_DeviceState = DEVICE_STATE_OK; + + // We were bad, now we're ok. Restore resources and reset render state. + ReacquireResourcesInternal( true, true, "OtherAppInit" ); + } + } + + // Do mode change if we have a video mode change. + if ( m_bPendingVideoModeChange && !IsDeactivated() ) + { +#ifdef _DEBUG + Warning( "mode change!\n" ); +#endif + // now purge unreferenced materials + g_pShaderUtil->UncacheUnusedMaterials( true ); + + ResizeWindow( m_PendingVideoModeChangeConfig ); + } +#endif +} + + +//----------------------------------------------------------------------------- +// Special method to refresh the screen on the XBox360 +//----------------------------------------------------------------------------- +bool CShaderDeviceDx8::AllocNonInteractiveRefreshObjects() +{ +#if defined( _X360 ) + + const char *strVertexShaderProgram = + " float4x4 matWVP : register(c0);" + " struct VS_IN" + " {" + " float4 ObjPos : POSITION;" + " float2 TexCoord : TEXCOORD;" + " };" + " struct VS_OUT" + " {" + " float4 ProjPos : POSITION;" + " float2 TexCoord : TEXCOORD;" + " };" + " VS_OUT main( VS_IN In )" + " {" + " VS_OUT Out; " + " Out.ProjPos = mul( matWVP, In.ObjPos );" + " Out.TexCoord = In.TexCoord;" + " return Out;" + " }"; + + const char *strPixelShaderProgram = + " struct PS_IN" + " {" + " float2 TexCoord : TEXCOORD;" + " };" + " sampler detail : register( s0 );" + " float4 main( PS_IN In ) : COLOR" + " {" + " return tex2D( detail, In.TexCoord );" + " }"; + + const char *strPixelShaderProgram2 = + " struct PS_IN" + " {" + " float2 TexCoord : TEXCOORD;" + " };" + " sampler detail : register( s0 );" + " float4 main( PS_IN In ) : COLOR" + " {" + " return tex2D( detail, In.TexCoord );" + " }"; + + const char *strPixelShaderProgram3 = + " struct PS_IN" + " {" + " float2 TexCoord : TEXCOORD;" + " };" + " float SrgbGammaToLinear( float flSrgbGammaValue )" + " {" + " float x = saturate( flSrgbGammaValue );" + " return ( x <= 0.04045f ) ? ( x / 12.92f ) : ( pow( ( x + 0.055f ) / 1.055f, 2.4f ) );" + " }" + "float X360LinearToGamma( float flLinearValue )" + "{" + " float fl360GammaValue;" + "" + " flLinearValue = saturate( flLinearValue );" + " if ( flLinearValue < ( 128.0f / 1023.0f ) )" + " {" + " if ( flLinearValue < ( 64.0f / 1023.0f ) )" + " {" + " fl360GammaValue = flLinearValue * ( 1023.0f * ( 1.0f / 255.0f ) );" + " }" + " else" + " {" + " fl360GammaValue = flLinearValue * ( ( 1023.0f / 2.0f ) * ( 1.0f / 255.0f ) ) + ( 32.0f / 255.0f );" + " }" + " }" + " else" + " {" + " if ( flLinearValue < ( 512.0f / 1023.0f ) )" + " {" + " fl360GammaValue = flLinearValue * ( ( 1023.0f / 4.0f ) * ( 1.0f / 255.0f ) ) + ( 64.0f / 255.0f );" + " }" + " else" + " {" + " fl360GammaValue = flLinearValue * ( ( 1023.0f /8.0f ) * ( 1.0f / 255.0f ) ) + ( 128.0f /255.0f );" + " if ( fl360GammaValue > 1.0f )" + " {" + " fl360GammaValue = 1.0f;" + " }" + " }" + " }" + "" + " fl360GammaValue = saturate( fl360GammaValue );" + " return fl360GammaValue;" + "}" + " sampler detail : register( s0 );" + " float4 main( PS_IN In ) : COLOR" + " {" + " float4 vTextureColor = tex2D( detail, In.TexCoord );" + " vTextureColor.r = X360LinearToGamma( SrgbGammaToLinear( vTextureColor.r ) );" + " vTextureColor.g = X360LinearToGamma( SrgbGammaToLinear( vTextureColor.g ) );" + " vTextureColor.b = X360LinearToGamma( SrgbGammaToLinear( vTextureColor.b ) );" + " return vTextureColor;" + " }"; + + D3DVERTEXELEMENT9 VertexElements[4] = + { + { 0, 0, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0 }, + { 0, 12, D3DDECLTYPE_FLOAT2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 0 }, + D3DDECL_END() + }; + + ID3DXBuffer *pErrorMsg = NULL; + ID3DXBuffer *pShaderCode = NULL; + + HRESULT hr = D3DXCompileShader( strVertexShaderProgram, (UINT)strlen( strVertexShaderProgram ), NULL, NULL, "main", "vs_2_0", 0, &pShaderCode, &pErrorMsg, NULL ); + if ( FAILED( hr ) ) + return false; + + Dx9Device()->CreateVertexShader( (DWORD*)pShaderCode->GetBufferPointer(), &m_NonInteractiveRefresh.m_pVertexShader ); + pShaderCode->Release(); + pShaderCode = NULL; + if ( pErrorMsg ) + { + pErrorMsg->Release(); + pErrorMsg = NULL; + } + + hr = D3DXCompileShader( strPixelShaderProgram, (UINT)strlen( strPixelShaderProgram ), NULL, NULL, "main", "ps_2_0", 0, &pShaderCode, &pErrorMsg, NULL ); + if ( FAILED(hr) ) + return false; + + Dx9Device()->CreatePixelShader( (DWORD*)pShaderCode->GetBufferPointer(), &m_NonInteractiveRefresh.m_pPixelShader ); + pShaderCode->Release(); + if ( pErrorMsg ) + { + pErrorMsg->Release(); + pErrorMsg = NULL; + } + + hr = D3DXCompileShader( strPixelShaderProgram3, (UINT)strlen( strPixelShaderProgram3 ), NULL, NULL, "main", "ps_2_0", 0, &pShaderCode, &pErrorMsg, NULL ); + if ( FAILED(hr) ) + return false; + + Dx9Device()->CreatePixelShader( (DWORD*)pShaderCode->GetBufferPointer(), &m_NonInteractiveRefresh.m_pPixelShaderStartup ); + pShaderCode->Release(); + if ( pErrorMsg ) + { + pErrorMsg->Release(); + pErrorMsg = NULL; + } + + hr = D3DXCompileShader( strPixelShaderProgram2, (UINT)strlen( strPixelShaderProgram2 ), NULL, NULL, "main", "ps_2_0", 0, &pShaderCode, &pErrorMsg, NULL ); + if ( FAILED(hr) ) + return false; + + Dx9Device()->CreatePixelShader( (DWORD*)pShaderCode->GetBufferPointer(), &m_NonInteractiveRefresh.m_pPixelShaderStartupPass2 ); + pShaderCode->Release(); + if ( pErrorMsg ) + { + pErrorMsg->Release(); + pErrorMsg = NULL; + } + + // Create a vertex declaration from the element descriptions. + Dx9Device()->CreateVertexDeclaration( VertexElements, &m_NonInteractiveRefresh.m_pVertexDecl ); + +#endif + + return true; +} + +void CShaderDeviceDx8::FreeNonInteractiveRefreshObjects() +{ + if ( m_NonInteractiveRefresh.m_pVertexShader ) + { + m_NonInteractiveRefresh.m_pVertexShader->Release(); + m_NonInteractiveRefresh.m_pVertexShader = NULL; + } + + if ( m_NonInteractiveRefresh.m_pPixelShader ) + { + m_NonInteractiveRefresh.m_pPixelShader->Release(); + m_NonInteractiveRefresh.m_pPixelShader = NULL; + } + + if ( m_NonInteractiveRefresh.m_pPixelShaderStartup ) + { + m_NonInteractiveRefresh.m_pPixelShaderStartup->Release(); + m_NonInteractiveRefresh.m_pPixelShaderStartup = NULL; + } + + if ( m_NonInteractiveRefresh.m_pPixelShaderStartupPass2 ) + { + m_NonInteractiveRefresh.m_pPixelShaderStartupPass2->Release(); + m_NonInteractiveRefresh.m_pPixelShaderStartupPass2 = NULL; + } + + if ( m_NonInteractiveRefresh.m_pVertexDecl ) + { + m_NonInteractiveRefresh.m_pVertexDecl->Release(); + m_NonInteractiveRefresh.m_pVertexDecl = NULL; + } +} + +bool CShaderDeviceDx8::InNonInteractiveMode() const +{ + return m_NonInteractiveRefresh.m_Mode != MATERIAL_NON_INTERACTIVE_MODE_NONE; +} + +void CShaderDeviceDx8::EnableNonInteractiveMode( MaterialNonInteractiveMode_t mode, ShaderNonInteractiveInfo_t *pInfo ) +{ + if ( !IsX360() ) + return; + if ( pInfo && ( pInfo->m_hTempFullscreenTexture == INVALID_SHADERAPI_TEXTURE_HANDLE ) ) + { + mode = MATERIAL_NON_INTERACTIVE_MODE_NONE; + } + m_NonInteractiveRefresh.m_Mode = mode; + if ( pInfo ) + { + m_NonInteractiveRefresh.m_Info = *pInfo; + } + m_NonInteractiveRefresh.m_nPacifierFrame = 0; + + if ( mode != MATERIAL_NON_INTERACTIVE_MODE_NONE ) + { + ConVarRef mat_monitorgamma( "mat_monitorgamma" ); + ConVarRef mat_monitorgamma_tv_range_min( "mat_monitorgamma_tv_range_min" ); + ConVarRef mat_monitorgamma_tv_range_max( "mat_monitorgamma_tv_range_max" ); + ConVarRef mat_monitorgamma_tv_exp( "mat_monitorgamma_tv_exp" ); + ConVarRef mat_monitorgamma_tv_enabled( "mat_monitorgamma_tv_enabled" ); + SetHardwareGammaRamp( mat_monitorgamma.GetFloat(), mat_monitorgamma_tv_range_min.GetFloat(), mat_monitorgamma_tv_range_max.GetFloat(), + mat_monitorgamma_tv_exp.GetFloat(), mat_monitorgamma_tv_enabled.GetBool() ); + } + +#ifdef _X360 + if ( mode != MATERIAL_NON_INTERACTIVE_MODE_NONE ) + { + // HACK: VSync off (prevents us wasting time blocking on VSync due to our irregular present intervals) + Dx9Device()->SetRenderState( D3DRS_PRESENTINTERVAL, D3DPRESENT_INTERVAL_IMMEDIATE ); + } + else + { + // HACK: VSync on (defaulting to on on 360 is fine, but really should save+restore this state) + Dx9Device()->SetRenderState( D3DRS_PRESENTINTERVAL, D3DPRESENT_INTERVAL_ONE ); + } +#endif + +// Msg( "Time elapsed: %.3f Peak %.3f Ave %.5f Count %d Count Above %d\n", Plat_FloatTime() - m_NonInteractiveRefresh.m_flStartTime, +// m_NonInteractiveRefresh.m_flPeakDt, m_NonInteractiveRefresh.m_flTotalDt / m_NonInteractiveRefresh.m_nSamples, m_NonInteractiveRefresh.m_nSamples, m_NonInteractiveRefresh.m_nCountAbove66 ); + + m_NonInteractiveRefresh.m_flStartTime = m_NonInteractiveRefresh.m_flLastPresentTime = + m_NonInteractiveRefresh.m_flLastPacifierTime = Plat_FloatTime(); + m_NonInteractiveRefresh.m_flPeakDt = 0.0f; + m_NonInteractiveRefresh.m_flTotalDt = 0.0f; + m_NonInteractiveRefresh.m_nSamples = 0; + m_NonInteractiveRefresh.m_nCountAbove66 = 0; +} + +void CShaderDeviceDx8::UpdatePresentStats() +{ + float t = Plat_FloatTime(); + float flActualDt = t - m_NonInteractiveRefresh.m_flLastPresentTime; + if ( flActualDt > m_NonInteractiveRefresh.m_flPeakDt ) + { + m_NonInteractiveRefresh.m_flPeakDt = flActualDt; + } + if ( flActualDt > 0.066 ) + { + ++m_NonInteractiveRefresh.m_nCountAbove66; + } + + m_NonInteractiveRefresh.m_flTotalDt += flActualDt; + ++m_NonInteractiveRefresh.m_nSamples; + + t = Plat_FloatTime(); + m_NonInteractiveRefresh.m_flLastPresentTime = t; +} + +void CShaderDeviceDx8::RefreshFrontBufferNonInteractive() +{ + if ( !IsX360() || !InNonInteractiveMode() ) + return; + + // Other code should not be talking to D3D at the same time as this + AUTO_LOCK( m_nonInteractiveModeMutex ); + +#ifdef _X360 + g_pShaderAPI->OwnGPUResources( false ); + IDirect3DBaseTexture *pTexture = g_pShaderAPI->GetD3DTexture( m_NonInteractiveRefresh.m_Info.m_hTempFullscreenTexture ); + + int w, h; + g_pShaderAPI->GetBackBufferDimensions( w, h ); + XMMATRIX matWVP = XMMatrixOrthographicOffCenterLH( 0, (FLOAT)w, (FLOAT)h, 0, 0, 1 ); + + // Structure to hold vertex data. + struct TEXVERTEX + { + FLOAT Position[3]; + FLOAT TexCoord[2]; + }; + TEXVERTEX Vertices[4]; + + Vertices[0].Position[0] = -0.5f; + Vertices[0].Position[1] = -0.5f; + Vertices[0].Position[2] = 0; + Vertices[0].TexCoord[0] = 0; + Vertices[0].TexCoord[1] = 0; + + Vertices[1].Position[0] = w-0.5f; + Vertices[1].Position[1] = -0.5f; + Vertices[1].Position[2] = 0; + Vertices[1].TexCoord[0] = 1; + Vertices[1].TexCoord[1] = 0; + + Vertices[2].Position[0] = w-0.5f; + Vertices[2].Position[1] = h-0.5f; + Vertices[2].Position[2] = 0; + Vertices[2].TexCoord[0] = 1; + Vertices[2].TexCoord[1] = 1; + + Vertices[3].Position[0] = -0.5f; + Vertices[3].Position[1] = h-0.5f; + Vertices[3].Position[2] = 0; + Vertices[3].TexCoord[0] = 0; + Vertices[3].TexCoord[1] = 1; + + D3DVIEWPORT9 viewport; + viewport.X = viewport.Y = 0; + viewport.Width = w; viewport.Height = h; + viewport.MinZ = ReverseDepthOnX360() ? 1.0f : 0.0f; + viewport.MaxZ = 1.0f - viewport.MinZ; + + bool bInStartupMode = ( m_NonInteractiveRefresh.m_Mode == MATERIAL_NON_INTERACTIVE_MODE_STARTUP ); + + float flDepth = (ShaderUtil()->GetConfig().bReverseDepth ^ ReverseDepthOnX360()) ? 0.0f : 1.0f; + Dx9Device()->Clear( 0, NULL, D3DCLEAR_ZBUFFER, 0, flDepth, 0L ); + + Dx9Device()->SetViewport( &viewport ); + Dx9Device()->SetTexture( 0, pTexture ); + Dx9Device()->SetVertexShader( m_NonInteractiveRefresh.m_pVertexShader ); + Dx9Device()->SetPixelShader( bInStartupMode ? m_NonInteractiveRefresh.m_pPixelShaderStartup : m_NonInteractiveRefresh.m_pPixelShader ); + Dx9Device()->SetVertexShaderConstantF( 0, (FLOAT*)&matWVP, 4 ); + Dx9Device()->SetVertexDeclaration( m_NonInteractiveRefresh.m_pVertexDecl ); + Dx9Device()->SetSamplerState( 0, D3DSAMP_ADDRESSU, D3DTADDRESS_CLAMP ); + Dx9Device()->SetSamplerState( 0, D3DSAMP_ADDRESSV, D3DTADDRESS_CLAMP ); + Dx9Device()->SetSamplerState( 0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR ); + Dx9Device()->SetSamplerState( 0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR ); + + tmZone( TELEMETRY_LEVEL1, TMZF_NONE, "%s", __FUNCTION__ ); + + Dx9Device()->DrawPrimitiveUP( D3DPT_QUADLIST, 1, Vertices, sizeof( TEXVERTEX ) ); + + if ( bInStartupMode ) + { + float flXPos = m_NonInteractiveRefresh.m_Info.m_flNormalizedX; + float flYPos = m_NonInteractiveRefresh.m_Info.m_flNormalizedY; + float flHeight = m_NonInteractiveRefresh.m_Info.m_flNormalizedSize; + int nSize = h * flHeight; + int x = w * flXPos - nSize * 0.5f; + int y = h * flYPos - nSize * 0.5f; + w = h = nSize; + + Vertices[0].Position[0] = x - 0.5f; + Vertices[0].Position[1] = y - 0.5f; + Vertices[1].Position[0] = x+w-0.5f; + Vertices[1].Position[1] = y - 0.5f; + Vertices[2].Position[0] = x+w-0.5f; + Vertices[2].Position[1] = y+h-0.5f; + Vertices[3].Position[0] = x - 0.5f; + Vertices[3].Position[1] = y+h-0.5f; + + float t = Plat_FloatTime(); + float flDt = t - m_NonInteractiveRefresh.m_flLastPacifierTime; + if ( flDt > 0.030f ) + { + if ( ++m_NonInteractiveRefresh.m_nPacifierFrame >= m_NonInteractiveRefresh.m_Info.m_nPacifierCount ) + { + m_NonInteractiveRefresh.m_nPacifierFrame = 0; + } + m_NonInteractiveRefresh.m_flLastPacifierTime = t; + } + + pTexture = g_pShaderAPI->GetD3DTexture( m_NonInteractiveRefresh.m_Info.m_pPacifierTextures[ m_NonInteractiveRefresh.m_nPacifierFrame ] ); + Dx9Device()->SetRenderState( D3DRS_ALPHABLENDENABLE, 1 ); + Dx9Device()->SetRenderState( D3DRS_SRCBLEND, D3DBLEND_SRCALPHA ); + Dx9Device()->SetRenderState( D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA ); + Dx9Device()->SetTexture( 0, pTexture ); + Dx9Device()->SetPixelShader( m_NonInteractiveRefresh.m_pPixelShaderStartupPass2 ); + Dx9Device()->DrawPrimitiveUP( D3DPT_QUADLIST, 1, Vertices, sizeof( TEXVERTEX ) ); + } + + Dx9Device()->SetVertexShader( NULL ); + Dx9Device()->SetPixelShader( NULL ); + Dx9Device()->SetTexture( 0, NULL ); + Dx9Device()->SetVertexDeclaration( NULL ); + + tmZone( TELEMETRY_LEVEL1, TMZF_NONE, "D3DPresent" ); + + Dx9Device()->Present( 0, 0, 0, 0 ); + g_pShaderAPI->QueueResetRenderState(); + g_pShaderAPI->OwnGPUResources( true ); + + UpdatePresentStats(); +#endif +} + + +//----------------------------------------------------------------------------- +// Page flip +//----------------------------------------------------------------------------- +void CShaderDeviceDx8::Present() +{ + LOCK_SHADERAPI(); + + // need to flush the dynamic buffer + g_pShaderAPI->FlushBufferedPrimitives(); + + if ( !IsDeactivated() ) + { + Dx9Device()->EndScene(); + } + + HRESULT hr = S_OK; + + // if we're in queued mode, don't present if the device is already lost + bool bValidPresent = true; + bool bInMainThread = ThreadInMainThread(); + if ( !bInMainThread ) + { + // don't present if the device is in an invalid state and in queued mode + if ( m_DeviceState != DEVICE_STATE_OK ) + { + bValidPresent = false; + } + // check for lost device early in threaded mode + CheckDeviceLost( m_bOtherAppInitializing ); + if ( m_DeviceState != DEVICE_STATE_OK ) + { + bValidPresent = false; + } + } + // Copy the back buffer into the non-interactive temp buffer + if ( m_NonInteractiveRefresh.m_Mode == MATERIAL_NON_INTERACTIVE_MODE_LEVEL_LOAD ) + { + g_pShaderAPI->CopyRenderTargetToTextureEx( m_NonInteractiveRefresh.m_Info.m_hTempFullscreenTexture, 0, NULL, NULL ); + } + + // If we're not iconified, try to present (without this check, we can flicker when Alt-Tabbed away) +#ifdef _WIN32 + if ( IsX360() || (IsIconic( ( HWND )m_hWnd ) == 0 && bValidPresent) ) +#else + if ( IsX360() || (IsIconic( (VD3DHWND)m_hWnd ) == 0 && bValidPresent) ) +#endif + { + if ( IsPC() && ( m_IsResizing || ( m_ViewHWnd != (VD3DHWND)m_hWnd ) ) ) + { + RECT destRect; + #ifndef DX_TO_GL_ABSTRACTION + GetClientRect( ( HWND )m_ViewHWnd, &destRect ); + #else + toglGetClientRect( (VD3DHWND)m_ViewHWnd, &destRect ); + #endif + + ShaderViewport_t viewport; + g_pShaderAPI->GetViewports( &viewport, 1 ); + + RECT srcRect; + srcRect.left = viewport.m_nTopLeftX; + srcRect.right = viewport.m_nTopLeftX + viewport.m_nWidth; + srcRect.top = viewport.m_nTopLeftY; + srcRect.bottom = viewport.m_nTopLeftY + viewport.m_nHeight; + + hr = Dx9Device()->Present( &srcRect, &destRect, (VD3DHWND)m_ViewHWnd, 0 ); + } + else + { + g_pShaderAPI->OwnGPUResources( false ); + hr = Dx9Device()->Present( 0, 0, 0, 0 ); + } + } + + UpdatePresentStats(); + + if ( IsWindows() ) + { + if ( hr == D3DERR_DRIVERINTERNALERROR ) + { + /* Usually this bug means that the driver has run out of internal video + memory, due to leaking it slowly over several application restarts. + As of summer 2007, IE in particular seemed to leak a lot of driver + memory for every image context it created in the browser window. A + reboot clears out the leaked memory and will generally allow the game + to be run again; occasionally (but not frequently) it's necessary to + reduce video settings in the game as well to run. But, this is too + fine a distinction to explain in a dialog, so place the guilt on the + user and ask them to reduce video settings regardless. + */ + + Error( "Internal driver error at Present.\n" + "You're likely out of OS Paged Pool Memory! For more info, see\n" + "http://support.steampowered.com/cgi-bin/steampowered.cfg/php/enduser/std_adp.php?p_faqid=150\n" ); + } + if ( hr == D3DERR_DEVICELOST ) + { + MarkDeviceLost(); + } + } + + MeshMgr()->DiscardVertexBuffers(); + + if ( bInMainThread ) + { + CheckDeviceLost( m_bOtherAppInitializing ); + } + + if ( IsX360() ) + { + // according to docs - "Mandatory Reset of GPU Registers" + // 360 must force the cached state to be dirty after any present() + g_pShaderAPI->ResetRenderState( false ); + } + +#ifdef RECORD_KEYFRAMES + static int frame = 0; + ++frame; + if (frame == KEYFRAME_INTERVAL) + { + RECORD_COMMAND( DX8_KEYFRAME, 0 ); + + g_pShaderAPI->ResetRenderState(); + frame = 0; + } +#endif + + g_pShaderAPI->AdvancePIXFrame(); + + if ( !IsDeactivated() ) + { +#ifndef DX_TO_GL_ABSTRACTION + if ( ( ShaderUtil()->GetConfig().bMeasureFillRate || ShaderUtil()->GetConfig().bVisualizeFillRate ) ) + { + g_pShaderAPI->ClearBuffers( true, true, true, -1, -1 ); + } +#endif + + Dx9Device()->BeginScene(); + } +} + + +// We need to scale our colors to the range [16, 235] to keep our colors within TV standards. Some colors might +// still be out of gamut if any of the R, G, or B channels are more than 191 units apart from each other in +// the 0-255 scale, but it looks like the 360 deals with this for us by lowering the bright saturated color components. +// NOTE: I'm leaving the max at 255 to retain whiter than whites. On most TV's, we seems a little dark in the bright colors +// compared to TV and movies when played in the same conditions. This keeps out brights on par with what customers are +// used to seeing. +// 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 + +void CShaderDeviceDx8::SetHardwareGammaRamp( float fGamma, float fGammaTVRangeMin, float fGammaTVRangeMax, float fGammaTVExponent, bool bTVEnabled ) +{ + DevMsg( 2, "SetHardwareGammaRamp( %f )\n", fGamma ); + + Assert( Dx9Device() ); + if( !Dx9Device() ) + return; + + D3DGAMMARAMP gammaRamp; + for ( int i = 0; i < 256; i++ ) + { + float flInputValue = float( i ) / 255.0f; + + // Since the 360's sRGB read/write is a piecewise linear approximation, we need to correct for the difference in gamma space here + float flSrgbGammaValue; + if ( IsX360() ) // Should we also do this for the PS3? + { + // First undo the 360 broken sRGB curve by bringing the value back into linear space + float flLinearValue = X360GammaToLinear( flInputValue ); + flLinearValue = clamp( flLinearValue, 0.0f, 1.0f ); + + // Now apply a true sRGB curve to mimic PC hardware + flSrgbGammaValue = SrgbLinearToGamma( flLinearValue ); // ( flLinearValue <= 0.0031308f ) ? ( flLinearValue * 12.92f ) : ( 1.055f * powf( flLinearValue, ( 1.0f / 2.4f ) ) ) - 0.055f; + flSrgbGammaValue = clamp( flSrgbGammaValue, 0.0f, 1.0f ); + } + else + { + flSrgbGammaValue = flInputValue; + } + + // Apply the user controlled exponent curve + float flCorrection = pow( flSrgbGammaValue, ( fGamma / 2.2f ) ); + flCorrection = clamp( flCorrection, 0.0f, 1.0f ); + + // TV adjustment - Apply an exp and a scale and bias + if ( bTVEnabled ) + { + // Adjust for TV gamma of 2.5 by applying an exponent of 2.2 / 2.5 = 0.88 + flCorrection = pow( flCorrection, 2.2f / fGammaTVExponent ); + flCorrection = clamp( flCorrection, 0.0f, 1.0f ); + + // Scale and bias to fit into the 16-235 range for TV's + flCorrection = ( flCorrection * ( fGammaTVRangeMax - fGammaTVRangeMin ) / 255.0f ) + ( fGammaTVRangeMin / 255.0f ); + flCorrection = clamp( flCorrection, 0.0f, 1.0f ); + } + + // Generate final int value + unsigned int val = ( int )( flCorrection * 65535.0f ); + gammaRamp.red[i] = val; + gammaRamp.green[i] = val; + gammaRamp.blue[i] = val; + } + + Dx9Device()->SetGammaRamp( 0, D3DSGR_NO_CALIBRATION, &gammaRamp ); +} + + +//----------------------------------------------------------------------------- +// Shader compilation +//----------------------------------------------------------------------------- +IShaderBuffer* CShaderDeviceDx8::CompileShader( const char *pProgram, size_t nBufLen, const char *pShaderVersion ) +{ + return ShaderManager()->CompileShader( pProgram, nBufLen, pShaderVersion ); +} + +VertexShaderHandle_t CShaderDeviceDx8::CreateVertexShader( IShaderBuffer *pBuffer ) +{ + return ShaderManager()->CreateVertexShader( pBuffer ); +} + +void CShaderDeviceDx8::DestroyVertexShader( VertexShaderHandle_t hShader ) +{ + ShaderManager()->DestroyVertexShader( hShader ); +} + +GeometryShaderHandle_t CShaderDeviceDx8::CreateGeometryShader( IShaderBuffer* pShaderBuffer ) +{ + Assert( 0 ); + return GEOMETRY_SHADER_HANDLE_INVALID; +} + +void CShaderDeviceDx8::DestroyGeometryShader( GeometryShaderHandle_t hShader ) +{ + Assert( hShader == GEOMETRY_SHADER_HANDLE_INVALID ); +} + +PixelShaderHandle_t CShaderDeviceDx8::CreatePixelShader( IShaderBuffer *pBuffer ) +{ + return ShaderManager()->CreatePixelShader( pBuffer ); +} + +void CShaderDeviceDx8::DestroyPixelShader( PixelShaderHandle_t hShader ) +{ + ShaderManager()->DestroyPixelShader( hShader ); +} + +#ifdef DX_TO_GL_ABSTRACTION +void CShaderDeviceDx8::DoStartupShaderPreloading( void ) +{ + ShaderManager()->DoStartupShaderPreloading(); +} +#endif + + +//----------------------------------------------------------------------------- +// Creates/destroys Mesh +// NOTE: Will be deprecated soon! +//----------------------------------------------------------------------------- +IMesh* CShaderDeviceDx8::CreateStaticMesh( VertexFormat_t vertexFormat, const char *pTextureBudgetGroup, IMaterial * pMaterial ) +{ + LOCK_SHADERAPI(); + return MeshMgr()->CreateStaticMesh( vertexFormat, pTextureBudgetGroup, pMaterial ); +} + +void CShaderDeviceDx8::DestroyStaticMesh( IMesh* pMesh ) +{ + LOCK_SHADERAPI(); + MeshMgr()->DestroyStaticMesh( pMesh ); +} + + +//----------------------------------------------------------------------------- +// Creates/destroys vertex buffers + index buffers +//----------------------------------------------------------------------------- +IVertexBuffer *CShaderDeviceDx8::CreateVertexBuffer( ShaderBufferType_t type, VertexFormat_t fmt, int nVertexCount, const char *pBudgetGroup ) +{ + LOCK_SHADERAPI(); + return MeshMgr()->CreateVertexBuffer( type, fmt, nVertexCount, pBudgetGroup ); +} + +void CShaderDeviceDx8::DestroyVertexBuffer( IVertexBuffer *pVertexBuffer ) +{ + LOCK_SHADERAPI(); + MeshMgr()->DestroyVertexBuffer( pVertexBuffer ); +} + +IIndexBuffer *CShaderDeviceDx8::CreateIndexBuffer( ShaderBufferType_t bufferType, MaterialIndexFormat_t fmt, int nIndexCount, const char *pBudgetGroup ) +{ + LOCK_SHADERAPI(); + return MeshMgr()->CreateIndexBuffer( bufferType, fmt, nIndexCount, pBudgetGroup ); +} + +void CShaderDeviceDx8::DestroyIndexBuffer( IIndexBuffer *pIndexBuffer ) +{ + LOCK_SHADERAPI(); + MeshMgr()->DestroyIndexBuffer( pIndexBuffer ); +} + +IVertexBuffer *CShaderDeviceDx8::GetDynamicVertexBuffer( int streamID, VertexFormat_t vertexFormat, bool bBuffered ) +{ + LOCK_SHADERAPI(); + return MeshMgr()->GetDynamicVertexBuffer( streamID, vertexFormat, bBuffered ); +} + +IIndexBuffer *CShaderDeviceDx8::GetDynamicIndexBuffer( MaterialIndexFormat_t fmt, bool bBuffered ) +{ + LOCK_SHADERAPI(); + return MeshMgr()->GetDynamicIndexBuffer( fmt, bBuffered ); +} + +#ifdef _X360 +void CShaderDeviceDx8::SpewVideoInfo360( const CCommand &args ) +{ + XVIDEO_MODE videoMode; + XGetVideoMode( &videoMode ); + + Warning( "back buffer size: %dx%d\n", m_PresentParameters.BackBufferWidth, m_PresentParameters.BackBufferHeight ); + Warning( "display resolution: %dx%d %s\n", videoMode.dwDisplayWidth, videoMode.dwDisplayHeight, videoMode.fIsInterlaced ? "interlaced" : "progressive" ); + Warning( "refresh rate: %f\n", videoMode.RefreshRate ); + Warning( "aspect: %s\n", videoMode.fIsWideScreen ? "16x9 (widescreen)" : "4x3 (normal)" ); + Warning( "%s\n", videoMode.fIsHiDef ? "hidef" : "lodef" ); + switch( videoMode.VideoStandard ) + { + case XC_VIDEO_STANDARD_NTSC_M: + Warning( "video standard: NTSC_M\n" ); + break; + case XC_VIDEO_STANDARD_NTSC_J: + Warning( "video standard: NTSC_J\n" ); + break; + case XC_VIDEO_STANDARD_PAL_I: + Warning( "video standard: PAL_I\n" ); + break; + default: + Warning( "error: UNKNOWN VIDEO STANDARD!\n" ); + Assert( 0 ); + break; + } + ConVarRef fps_max( "fps_max" ); + Warning( "fps_max: %f\n", fps_max.GetFloat() ); + switch( m_PresentParameters.MultiSampleType ) + { + case D3DMULTISAMPLE_NONE: + Warning( "multisample type: D3DMULTISAMPLE_NONE\n" ); + break; + case D3DMULTISAMPLE_2_SAMPLES: + Warning( "multisample type: D3DMULTISAMPLE_2_SAMPLES\n" ); + break; + case D3DMULTISAMPLE_4_SAMPLES: + Warning( "multisample type: D3DMULTISAMPLE_4_SAMPLES\n" ); + break; + } +} +#endif diff --git a/materialsystem/shaderapidx9/shaderdevicedx8.h b/materialsystem/shaderapidx9/shaderdevicedx8.h new file mode 100644 index 0000000..4e20efe --- /dev/null +++ b/materialsystem/shaderapidx9/shaderdevicedx8.h @@ -0,0 +1,382 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//===========================================================================// + +#ifndef SHADERDEVICEDX8_H +#define SHADERDEVICEDX8_H + +#ifdef _WIN32 +#pragma once +#endif + + +#include "shaderdevicebase.h" +#include "shaderapidx8_global.h" +#include "tier1/utlvector.h" + + + +//----------------------------------------------------------------------------- +// Describes which D3DDEVTYPE to use +//----------------------------------------------------------------------------- +#ifndef USE_REFERENCE_RASTERIZER +#define DX8_DEVTYPE D3DDEVTYPE_HAL +#else +#define DX8_DEVTYPE D3DDEVTYPE_REF +#endif + + +// PC: By default, PIX profiling is explicitly disallowed using the D3DPERF_SetOptions(1) API on PC +// X360: PIX_INSTRUMENTATION will only generate PIX events in RELEASE builds on 360 +// Uncomment to use PIX instrumentation: +#if PIX_ENABLE +#define PIX_INSTRUMENTATION +#endif + +#define MAX_PIX_ERRORS 3 + +#if defined( PIX_INSTRUMENTATION ) && defined ( DX_TO_GL_ABSTRACTION ) && defined( _WIN32 ) +typedef int (WINAPI *D3DPERF_BeginEvent_FuncPtr)( D3DCOLOR col, LPCWSTR wszName ); +typedef int (WINAPI *D3DPERF_EndEvent_FuncPtr)( void ); +typedef void (WINAPI *D3DPERF_SetMarker_FuncPtr)( D3DCOLOR col, LPCWSTR wszName ); +typedef void (WINAPI *D3DPERF_SetOptions_FuncPtr)( DWORD dwOptions ); +#endif + +//----------------------------------------------------------------------------- +// The Base implementation of the shader device +//----------------------------------------------------------------------------- +class CShaderDeviceMgrDx8 : public CShaderDeviceMgrBase +{ + typedef CShaderDeviceMgrBase BaseClass; + +public: + // constructor, destructor + CShaderDeviceMgrDx8(); + virtual ~CShaderDeviceMgrDx8(); + + // Methods of IAppSystem + virtual bool Connect( CreateInterfaceFn factory ); + virtual void Disconnect(); + virtual InitReturnVal_t Init(); + virtual void Shutdown(); + + // Methods of IShaderDevice + virtual int GetAdapterCount() const; + virtual void GetAdapterInfo( int adapter, MaterialAdapterInfo_t& info ) const; + virtual int GetModeCount( int nAdapter ) const; + virtual void GetModeInfo( ShaderDisplayMode_t* pInfo, int nAdapter, int mode ) const; + virtual void GetCurrentModeInfo( ShaderDisplayMode_t* pInfo, int nAdapter ) const; + virtual bool SetAdapter( int nAdapter, int nFlags ); + virtual CreateInterfaceFn SetMode( void *hWnd, int nAdapter, const ShaderDeviceInfo_t& mode ); + + // Determines hardware caps from D3D + bool ComputeCapsFromD3D( HardwareCaps_t *pCaps, int nAdapter ); + + // Forces caps to a specific dx level + void ForceCapsToDXLevel( HardwareCaps_t *pCaps, int nDxLevel, const HardwareCaps_t &actualCaps ); + + // Validates the mode... + bool ValidateMode( int nAdapter, const ShaderDeviceInfo_t &info ) const; + + // Returns the amount of video memory in bytes for a particular adapter + virtual int GetVidMemBytes( int nAdapter ) const; + +#if !defined( _X360 ) + FORCEINLINE IDirect3D9 *D3D() const + { + return m_pD3D; + } +#endif + +#if defined( PIX_INSTRUMENTATION ) && defined ( DX_TO_GL_ABSTRACTION ) && defined( _WIN32 ) + HMODULE m_hD3D9; + D3DPERF_BeginEvent_FuncPtr m_pBeginEvent; + D3DPERF_EndEvent_FuncPtr m_pEndEvent; + D3DPERF_SetMarker_FuncPtr m_pSetMarker; + D3DPERF_SetOptions_FuncPtr m_pSetOptions; +#endif + +protected: + // Determine capabilities + bool DetermineHardwareCaps( ); + +private: + // Initialize adapter information + void InitAdapterInfo(); + + // Code to detect support for texture border mode (not a simple caps check) + void CheckBorderColorSupport( HardwareCaps_t *pCaps, int nAdapter ); + + // Vendor-dependent code to detect support for various flavors of shadow mapping + void CheckVendorDependentShadowMappingSupport( HardwareCaps_t *pCaps, int nAdapter ); + + // Vendor-dependent code to detect Alpha To Coverage Backdoors + void CheckVendorDependentAlphaToCoverage( HardwareCaps_t *pCaps, int nAdapter ); + + // Compute the effective DX support level based on all the other caps + void ComputeDXSupportLevel( HardwareCaps_t &caps ); + + // Used to enumerate adapters, attach to windows +#if !defined( _X360 ) + IDirect3D9 *m_pD3D; +#endif + + bool m_bObeyDxCommandlineOverride : 1; + bool m_bAdapterInfoIntialized : 1; +}; + +extern CShaderDeviceMgrDx8* g_pShaderDeviceMgrDx8; + + +//----------------------------------------------------------------------------- +// IDirect3D accessor +//----------------------------------------------------------------------------- +#if defined( _X360 ) + +extern IDirect3D9 *m_pD3D; +inline IDirect3D9* D3D() +{ + return m_pD3D; +} + +#else + +inline IDirect3D9* D3D() +{ + return g_pShaderDeviceMgrDx8->D3D(); +} + +#endif + +#define NUM_FRAME_SYNC_QUERIES 2 +#define NUM_FRAME_SYNC_FRAMES_LATENCY 0 + +//----------------------------------------------------------------------------- +// The Dx8implementation of the shader device +//----------------------------------------------------------------------------- +class CShaderDeviceDx8 : public CShaderDeviceBase +{ + // Methods of IShaderDevice +public: + virtual bool IsUsingGraphics() const; + virtual ImageFormat GetBackBufferFormat() const; + virtual void GetBackBufferDimensions( int& width, int& height ) const; + virtual void Present(); + virtual IShaderBuffer* CompileShader( const char *pProgram, size_t nBufLen, const char *pShaderVersion ); + virtual VertexShaderHandle_t CreateVertexShader( IShaderBuffer *pBuffer ); + virtual void DestroyVertexShader( VertexShaderHandle_t hShader ); + virtual GeometryShaderHandle_t CreateGeometryShader( IShaderBuffer* pShaderBuffer ); + virtual void DestroyGeometryShader( GeometryShaderHandle_t hShader ); + virtual PixelShaderHandle_t CreatePixelShader( IShaderBuffer* pShaderBuffer ); + virtual void DestroyPixelShader( PixelShaderHandle_t hShader ); + virtual void ReleaseResources(); + virtual void ReacquireResources(); + virtual IMesh* CreateStaticMesh( VertexFormat_t format, const char *pBudgetGroup, IMaterial * pMaterial = NULL ); + virtual void DestroyStaticMesh( IMesh* mesh ); + virtual IVertexBuffer *CreateVertexBuffer( ShaderBufferType_t type, VertexFormat_t fmt, int nVertexCount, const char *pBudgetGroup ); + virtual void DestroyVertexBuffer( IVertexBuffer *pVertexBuffer ); + virtual IIndexBuffer *CreateIndexBuffer( ShaderBufferType_t bufferType, MaterialIndexFormat_t fmt, int nIndexCount, const char *pBudgetGroup ); + virtual void DestroyIndexBuffer( IIndexBuffer *pIndexBuffer ); + virtual IVertexBuffer *GetDynamicVertexBuffer( int nStreamID, VertexFormat_t vertexFormat, bool bBuffered = true ); + virtual IIndexBuffer *GetDynamicIndexBuffer( MaterialIndexFormat_t fmt, bool bBuffered = true ); + virtual void SetHardwareGammaRamp( float fGamma, float fGammaTVRangeMin, float fGammaTVRangeMax, float fGammaTVExponent, bool bTVEnabled ); + virtual void SpewDriverInfo() const; + virtual int GetCurrentAdapter() const; + virtual void EnableNonInteractiveMode( MaterialNonInteractiveMode_t mode, ShaderNonInteractiveInfo_t *pInfo = NULL ); + virtual void RefreshFrontBufferNonInteractive(); + virtual char *GetDisplayDeviceName() OVERRIDE; + + // Alternative method for ib/vs + // NOTE: If this works, remove GetDynamicVertexBuffer/IndexBuffer + + // Methods of CShaderDeviceBase +public: + virtual bool InitDevice( void* hWnd, int nAdapter, const ShaderDeviceInfo_t &info ); + virtual void ShutdownDevice(); + virtual bool IsDeactivated() const; + + // Other public methods +public: + // constructor, destructor + CShaderDeviceDx8(); + virtual ~CShaderDeviceDx8(); + + // Call this when another app is initializing or finished initializing + virtual void OtherAppInitializing( bool initializing ); + + // This handles any events queued because they were called outside of the owning thread + virtual void HandleThreadEvent( uint32 threadEvent ); + + // FIXME: Make private + // Which device are we using? + UINT m_DisplayAdapter; + D3DDEVTYPE m_DeviceType; + +protected: + enum DeviceState_t + { + DEVICE_STATE_OK = 0, + DEVICE_STATE_OTHER_APP_INIT, + DEVICE_STATE_LOST_DEVICE, + DEVICE_STATE_NEEDS_RESET, + }; + + struct NonInteractiveRefreshState_t + { + IDirect3DVertexShader9 *m_pVertexShader; + IDirect3DPixelShader9 *m_pPixelShader; + IDirect3DPixelShader9 *m_pPixelShaderStartup; + IDirect3DPixelShader9 *m_pPixelShaderStartupPass2; + IDirect3DVertexDeclaration9 *m_pVertexDecl; + ShaderNonInteractiveInfo_t m_Info; + MaterialNonInteractiveMode_t m_Mode; + float m_flLastPacifierTime; + int m_nPacifierFrame; + + float m_flStartTime; + float m_flLastPresentTime; + float m_flPeakDt; + float m_flTotalDt; + int m_nSamples; + int m_nCountAbove66; + }; + +protected: + // Creates the D3D Device + bool CreateD3DDevice( void* pHWnd, int nAdapter, const ShaderDeviceInfo_t &info ); + + // Actually creates the D3D Device once the present parameters are set up + IDirect3DDevice9* InvokeCreateDevice( void* hWnd, int nAdapter, DWORD deviceCreationFlags ); + + // Checks for CreateQuery support + void DetectQuerySupport( IDirect3DDevice9* pD3DDevice ); + + // Computes the presentation parameters + void SetPresentParameters( void* hWnd, int nAdapter, const ShaderDeviceInfo_t &info ); + + // Computes the supersample flags + D3DMULTISAMPLE_TYPE ComputeMultisampleType( int nSampleCount ); + + // Is the device active? + bool IsActive() const; + + // Try to reset the device, returned true if it succeeded + bool TryDeviceReset(); + + // Queue up the fact that the device was lost + void MarkDeviceLost(); + + // Deals with lost devices + void CheckDeviceLost( bool bOtherAppInitializing ); + + // Changes the window size + bool ResizeWindow( const ShaderDeviceInfo_t &info ); + + // Deals with the frame synching object + void AllocFrameSyncObjects( void ); + void FreeFrameSyncObjects( void ); + + // Alloc and free objects that are necessary for frame syncing + void AllocFrameSyncTextureObject(); + void FreeFrameSyncTextureObject(); + + // Alloc and free objects necessary for noninteractive frame refresh on the x360 + bool AllocNonInteractiveRefreshObjects(); + void FreeNonInteractiveRefreshObjects(); + + // FIXME: This is for backward compat; I still haven't solved a way of decoupling this + virtual bool OnAdapterSet() = 0; + virtual void ResetRenderState( bool bFullReset = true ) = 0; + + // For measuring if we meed TCR 022 on the XBox (refreshing often enough) + void UpdatePresentStats(); + + bool InNonInteractiveMode() const; + + void ReacquireResourcesInternal( bool bResetState = false, bool bForceReacquire = false, char const *pszForceReason = NULL ); + +#ifdef DX_TO_GL_ABSTRACTION +public: + virtual void DoStartupShaderPreloading( void ); +protected: +#endif + + D3DPRESENT_PARAMETERS m_PresentParameters; + ImageFormat m_AdapterFormat; + + // Mode info + int m_DeviceSupportsCreateQuery; + + ShaderDeviceInfo_t m_PendingVideoModeChangeConfig; + DeviceState_t m_DeviceState; + + bool m_bOtherAppInitializing : 1; + bool m_bQueuedDeviceLost : 1; + bool m_IsResizing : 1; + bool m_bPendingVideoModeChange : 1; + bool m_bUsingStencil : 1; + bool m_bResourcesReleased : 1; + + // amount of stencil variation we have available + int m_iStencilBufferBits; + +#ifdef _X360 + CON_COMMAND_MEMBER_F( CShaderDeviceDx8, "360vidinfo", SpewVideoInfo360, "Get information on the video mode on the 360", 0 ); +#endif + + // Frame sync objects + IDirect3DQuery9 *m_pFrameSyncQueryObject[NUM_FRAME_SYNC_QUERIES]; + bool m_bQueryIssued[NUM_FRAME_SYNC_QUERIES]; + int m_currentSyncQuery; + IDirect3DTexture9 *m_pFrameSyncTexture; + +#if defined( _X360 ) + HXUIDC m_hDC; +#endif + + CUtlString m_sDisplayDeviceName; + + // Used for x360 only + NonInteractiveRefreshState_t m_NonInteractiveRefresh; + CThreadFastMutex m_nonInteractiveModeMutex; + friend class CShaderDeviceMgrDx8; + + int m_numReleaseResourcesRefCount; // This is holding the number of ReleaseResources calls queued up, + // for every ReleaseResources call there should be a matching call to + // ReacquireResources, only the last top-level ReacquireResources will + // have effect. Nested ReleaseResources calls are bugs. +}; + + +//----------------------------------------------------------------------------- +// Globals +//----------------------------------------------------------------------------- +extern IDirect3DDevice9 *g_pD3DDevice; +FORCEINLINE IDirect3DDevice9 *Dx9Device() +{ + return g_pD3DDevice; +} + +extern CShaderDeviceDx8* g_pShaderDeviceDx8; + + +//----------------------------------------------------------------------------- +// Inline methods +//----------------------------------------------------------------------------- +FORCEINLINE bool CShaderDeviceDx8::IsActive() const +{ + return ( g_pD3DDevice != NULL ); +} + +// used to determine if we're deactivated +FORCEINLINE bool CShaderDeviceDx8::IsDeactivated() const +{ + return ( IsPC() && ( ( m_DeviceState != DEVICE_STATE_OK ) || m_bQueuedDeviceLost || m_numReleaseResourcesRefCount ) ); +} + + +#endif // SHADERDEVICEDX8_H diff --git a/materialsystem/shaderapidx9/shadershadowdx10.cpp b/materialsystem/shaderapidx9/shadershadowdx10.cpp new file mode 100644 index 0000000..d67cc4b --- /dev/null +++ b/materialsystem/shaderapidx9/shadershadowdx10.cpp @@ -0,0 +1,227 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//===========================================================================// + +#include "shadershadowdx10.h" +#include "utlvector.h" +#include "materialsystem/imaterialsystem.h" +#include "IHardwareConfigInternal.h" +#include "shadersystem.h" +#include "shaderapi/ishaderutil.h" +#include "materialsystem/imesh.h" +#include "tier0/dbg.h" +#include "materialsystem/idebugtextureinfo.h" + + +//----------------------------------------------------------------------------- +// Class Factory +//----------------------------------------------------------------------------- +static CShaderShadowDx10 s_ShaderShadow; +CShaderShadowDx10 *g_pShaderShadowDx10 = &s_ShaderShadow; + +EXPOSE_SINGLE_INTERFACE_GLOBALVAR( CShaderShadowDx10, IShaderShadow, + SHADERSHADOW_INTERFACE_VERSION, s_ShaderShadow ) + +//----------------------------------------------------------------------------- +// The shader shadow interface +//----------------------------------------------------------------------------- +CShaderShadowDx10::CShaderShadowDx10() +{ + m_IsTranslucent = false; + m_IsAlphaTested = false; + m_bIsDepthWriteEnabled = true; + m_bUsesVertexAndPixelShaders = false; +} + +CShaderShadowDx10::~CShaderShadowDx10() +{ +} + +// Sets the default *shadow* state +void CShaderShadowDx10::SetDefaultState() +{ + m_IsTranslucent = false; + m_IsAlphaTested = false; + m_bIsDepthWriteEnabled = true; + m_bUsesVertexAndPixelShaders = false; +} + +// Methods related to depth buffering +void CShaderShadowDx10::DepthFunc( ShaderDepthFunc_t depthFunc ) +{ +} + +void CShaderShadowDx10::EnableDepthWrites( bool bEnable ) +{ + m_bIsDepthWriteEnabled = bEnable; +} + +void CShaderShadowDx10::EnableDepthTest( bool bEnable ) +{ +} + +void CShaderShadowDx10::EnablePolyOffset( PolygonOffsetMode_t nOffsetMode ) +{ +} + +// Suppresses/activates color writing +void CShaderShadowDx10::EnableColorWrites( bool bEnable ) +{ +} + +// Suppresses/activates alpha writing +void CShaderShadowDx10::EnableAlphaWrites( bool bEnable ) +{ +} + +// Methods related to alpha blending +void CShaderShadowDx10::EnableBlending( bool bEnable ) +{ + m_IsTranslucent = bEnable; +} + +void CShaderShadowDx10::BlendFunc( ShaderBlendFactor_t srcFactor, ShaderBlendFactor_t dstFactor ) +{ +} + +void CShaderShadowDx10::BlendOp( ShaderBlendOp_t blendOp ) +{ +} + +void CShaderShadowDx10::BlendOpSeparateAlpha( ShaderBlendOp_t blendOp ) +{ +} + +// A simpler method of dealing with alpha modulation +void CShaderShadowDx10::EnableAlphaPipe( bool bEnable ) +{ +} + +void CShaderShadowDx10::EnableConstantAlpha( bool bEnable ) +{ +} + +void CShaderShadowDx10::EnableVertexAlpha( bool bEnable ) +{ +} + +void CShaderShadowDx10::EnableTextureAlpha( TextureStage_t stage, bool bEnable ) +{ +} + +void CShaderShadowDx10::SetShadowDepthFiltering( Sampler_t stage ) +{ +} + +// Alpha testing +void CShaderShadowDx10::EnableAlphaTest( bool bEnable ) +{ + m_IsAlphaTested = bEnable; +} + +void CShaderShadowDx10::AlphaFunc( ShaderAlphaFunc_t alphaFunc, float alphaRef /* [0-1] */ ) +{ +} + +// Wireframe/filled polygons +void CShaderShadowDx10::PolyMode( ShaderPolyModeFace_t face, ShaderPolyMode_t polyMode ) +{ +} + + +// Back face culling +void CShaderShadowDx10::EnableCulling( bool bEnable ) +{ +} + +// Alpha to coverage +void CShaderShadowDx10::EnableAlphaToCoverage( bool bEnable ) +{ +} + +// constant color + transparency +void CShaderShadowDx10::EnableConstantColor( bool bEnable ) +{ +} + + +// Indicates the vertex format for use with a vertex shader +// The flags to pass in here come from the VertexFormatFlags_t enum +// If pTexCoordDimensions is *not* specified, we assume all coordinates +// are 2-dimensional +void CShaderShadowDx10::VertexShaderVertexFormat( unsigned int flags, + int numTexCoords, int* pTexCoordDimensions, + int userDataSize ) +{ +} + +// Indicates we're going to light the model +void CShaderShadowDx10::EnableLighting( bool bEnable ) +{ +} + +void CShaderShadowDx10::EnableSpecular( bool bEnable ) +{ +} + +// Activate/deactivate skinning +void CShaderShadowDx10::EnableVertexBlend( bool bEnable ) +{ +} + +// per texture unit stuff +void CShaderShadowDx10::OverbrightValue( TextureStage_t stage, float value ) +{ +} + +void CShaderShadowDx10::EnableTexture( Sampler_t stage, bool bEnable ) +{ +} + +void CShaderShadowDx10::EnableCustomPixelPipe( bool bEnable ) +{ +} + +void CShaderShadowDx10::CustomTextureStages( int stageCount ) +{ +} + +void CShaderShadowDx10::CustomTextureOperation( TextureStage_t stage, ShaderTexChannel_t channel, + ShaderTexOp_t op, ShaderTexArg_t arg1, ShaderTexArg_t arg2 ) +{ +} + +void CShaderShadowDx10::EnableTexGen( TextureStage_t stage, bool bEnable ) +{ +} + +void CShaderShadowDx10::TexGen( TextureStage_t stage, ShaderTexGenParam_t param ) +{ +} + +// Sets the vertex and pixel shaders +void CShaderShadowDx10::SetVertexShader( const char *pShaderName, int vshIndex ) +{ + m_bUsesVertexAndPixelShaders = ( pShaderName != NULL ); +} + +void CShaderShadowDx10::EnableBlendingSeparateAlpha( bool bEnable ) +{ +} +void CShaderShadowDx10::SetPixelShader( const char *pShaderName, int pshIndex ) +{ + m_bUsesVertexAndPixelShaders = ( pShaderName != NULL ); +} + +void CShaderShadowDx10::BlendFuncSeparateAlpha( ShaderBlendFactor_t srcFactor, ShaderBlendFactor_t dstFactor ) +{ +} +// indicates what per-vertex data we're providing +void CShaderShadowDx10::DrawFlags( unsigned int drawFlags ) +{ +} + diff --git a/materialsystem/shaderapidx9/shadershadowdx8.cpp b/materialsystem/shaderapidx9/shadershadowdx8.cpp new file mode 100644 index 0000000..8c231c9 --- /dev/null +++ b/materialsystem/shaderapidx9/shadershadowdx8.cpp @@ -0,0 +1,1826 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//===========================================================================// + +#define DISABLE_PROTECTED_THINGS +#include "togl/rendermechanism.h" +#include "shadershadowdx8.h" +#include "locald3dtypes.h" +#include "utlvector.h" +#include "shaderapi/ishaderutil.h" +#include "shaderapidx8_global.h" +#include "shaderapidx8.h" +#include "materialsystem/imaterialsystemhardwareconfig.h" +#include "materialsystem/imaterialsystem.h" +#include "imeshdx8.h" +#include "materialsystem/materialsystem_config.h" +#include "vertexshaderdx8.h" + +// NOTE: This must be the last file included! +#include "tier0/memdbgon.h" + + +//----------------------------------------------------------------------------- +// The DX8 implementation of the shader setup interface +//----------------------------------------------------------------------------- +class CShaderShadowDX8 : public IShaderShadowDX8 +{ +public: + // constructor, destructor + CShaderShadowDX8( ); + virtual ~CShaderShadowDX8(); + + // Initialize render state + void Init( ); + + // Sets the default state + void SetDefaultState(); + + // Methods related to depth buffering + void DepthFunc( ShaderDepthFunc_t depthFunc ); + void EnableDepthWrites( bool bEnable ); + void EnableDepthTest( bool bEnable ); + void EnablePolyOffset( PolygonOffsetMode_t nOffsetMode ); + + // Methods related to stencil. obsolete + virtual void EnableStencil( bool bEnable ) + { + } + virtual void StencilFunc( ShaderStencilFunc_t stencilFunc ) + { + } + virtual void StencilPassOp( ShaderStencilOp_t stencilOp ) + { + } + virtual void StencilFailOp( ShaderStencilOp_t stencilOp ) + { + } + virtual void StencilDepthFailOp( ShaderStencilOp_t stencilOp ) + { + } + virtual void StencilReference( int nReference ) + { + } + virtual void StencilMask( int nMask ) + { + } + virtual void StencilWriteMask( int nMask ) + { + } + + // Suppresses/activates color writing + void EnableColorWrites( bool bEnable ); + void EnableAlphaWrites( bool bEnable ); + + // Methods related to alpha blending + void EnableBlending( bool bEnable ); + + void BlendFunc( ShaderBlendFactor_t srcFactor, ShaderBlendFactor_t dstFactor ); + void BlendOp( ShaderBlendOp_t blendOp ); + void BlendOpSeparateAlpha( ShaderBlendOp_t blendOp ); + + // Alpha testing + void EnableAlphaTest( bool bEnable ); + void AlphaFunc( ShaderAlphaFunc_t alphaFunc, float alphaRef /* [0-1] */ ); + + // Wireframe/filled polygons + void PolyMode( ShaderPolyModeFace_t face, ShaderPolyMode_t polyMode ); + + // Back face culling + void EnableCulling( bool bEnable ); + + // constant color + void EnableConstantColor( bool bEnable ); + + // Indicates we're going to light the model + void EnableLighting( bool bEnable ); + + // Indicates specular lighting is going to be used + void EnableSpecular( bool bEnable ); + + // Convert from linear to gamma color space on writes to frame buffer. + void EnableSRGBWrite( bool bEnable ); + + // Convert from gamma to linear on texture fetch. + void EnableSRGBRead( Sampler_t stage, bool bEnable ); + + // Set up appropriate shadow filtering state (such as Fetch4 on ATI) + void SetShadowDepthFiltering( Sampler_t stage ); + + // Computes the vertex format + virtual void VertexShaderVertexFormat( unsigned int nFlags, + int nTexCoordCount, int* pTexCoordDimensions, int nUserDataSize ); + + // Pixel and vertex shader methods + virtual void SetVertexShader( const char* pFileName, int nStaticVshIndex ); + virtual void SetPixelShader( const char* pFileName, int nStaticPshIndex ); + + // Indicates we're going to be using the ambient cube + void EnableAmbientLightCubeOnStage0( bool bEnable ); + + // Activate/deactivate skinning + void EnableVertexBlend( bool bEnable ); + + // per texture unit stuff + void OverbrightValue( TextureStage_t stage, float value ); + void EnableTexture( Sampler_t stage, bool bEnable ); + void EnableTexGen( TextureStage_t stage, bool bEnable ); + void TexGen( TextureStage_t stage, ShaderTexGenParam_t param ); + void TextureCoordinate( TextureStage_t stage, int useCoord ); + + // alternate method of specifying per-texture unit stuff, more flexible and more complicated + // Can be used to specify different operation per channel (alpha/color)... + void EnableCustomPixelPipe( bool bEnable ); + void CustomTextureStages( int stageCount ); + void CustomTextureOperation( TextureStage_t stage, ShaderTexChannel_t channel, + ShaderTexOp_t op, ShaderTexArg_t arg1, ShaderTexArg_t arg2 ); + + // A simpler method of dealing with alpha modulation + void EnableAlphaPipe( bool bEnable ); + void EnableConstantAlpha( bool bEnable ); + void EnableVertexAlpha( bool bEnable ); + void EnableTextureAlpha( TextureStage_t stage, bool bEnable ); + + // helper functions + void EnableSphereMapping( TextureStage_t stage, bool bEnable ); + + // Last call to be make before snapshotting + void ComputeAggregateShadowState( ); + + // Gets at the shadow state + const ShadowState_t & GetShadowState(); + const ShadowShaderState_t & GetShadowShaderState(); + + // GR - Separate alpha blending + void EnableBlendingSeparateAlpha( bool bEnable ); + void BlendFuncSeparateAlpha( ShaderBlendFactor_t srcFactor, ShaderBlendFactor_t dstFactor ); + + void FogMode( ShaderFogMode_t fogMode ); + void DisableFogGammaCorrection( bool bDisable ); + + void SetDiffuseMaterialSource( ShaderMaterialSource_t materialSource ); + virtual void SetMorphFormat( MorphFormat_t flags ); + + // Alpha to coverage + void EnableAlphaToCoverage( bool bEnable ); + +private: + struct TextureStageState_t + { + int m_TexCoordIndex; + int m_TexCoordinate; + float m_OverbrightVal; + ShaderTexArg_t m_Arg[2][2]; + ShaderTexOp_t m_Op[2]; + unsigned char m_TexGenEnable:1; + unsigned char m_TextureAlphaEnable:1; + }; + + struct SamplerState_t + { + bool m_TextureEnable : 1; + }; + + // Computes the blend factor + D3DBLEND BlendFuncValue( ShaderBlendFactor_t factor ) const; + + // Computes the blend op + D3DBLENDOP BlendOpValue( ShaderBlendOp_t blendOp ) const; + + // Configures the FVF vertex shader + void ConfigureFVFVertexShader( unsigned int flags ); + void ConfigureCustomFVFVertexShader( unsigned int flags ); + + // Configures our texture indices + void ConfigureTextureCoordinates( unsigned int flags ); + + // Returns a blend value based on overbrighting + D3DTEXTUREOP OverbrightBlendValue( TextureStage_t stage ); + + // Sets the desired color and alpha op state + void DrawFlags( unsigned int flags ); + + // Computes a vertex format for the draw flags + VertexFormat_t FlagsToVertexFormat( int flags ) const; + + // Indicates we've got a constant color specified + bool HasConstantColor() const; + + // Configures the alpha pipe + void ConfigureAlphaPipe( unsigned int flags ); + + // returns true if we're using texture coordinates at a given stage + bool IsUsingTextureCoordinates( Sampler_t stage ) const; + + // Recomputes the tex coord index + void RecomputeTexCoordIndex( TextureStage_t stage ); + + + // State needed to create the snapshots + IMaterialSystemHardwareConfig* m_pHardwareConfig; + + // Separate alpha control? + bool m_AlphaPipe; + + // Constant color state + bool m_HasConstantColor; + bool m_HasConstantAlpha; + + // Vertex color state + bool m_HasVertexAlpha; + + // funky custom method of specifying shader state + bool m_CustomTextureStageState; + + // Number of stages used by the custom pipeline + int m_CustomTextureStages; + + // Number of bones... + int m_NumBlendVertices; + + // Draw flags + int m_DrawFlags; + + // Alpha blending... + D3DBLEND m_SrcBlend; + D3DBLEND m_DestBlend; + D3DBLENDOP m_BlendOp; + + // GR - Separate alpha blending... + D3DBLEND m_SrcBlendAlpha; + D3DBLEND m_DestBlendAlpha; + D3DBLENDOP m_BlendOpAlpha; + + // Alpha testing + D3DCMPFUNC m_AlphaFunc; + int m_AlphaRef; + + // Stencil + D3DCMPFUNC m_StencilFunc; + int m_StencilRef; + int m_StencilMask; + DWORD m_StencilFail; + DWORD m_StencilZFail; + DWORD m_StencilPass; + int m_StencilWriteMask; + + // The current shadow state + ShadowState_t m_ShadowState; + ShadowShaderState_t m_ShadowShaderState; + + // State info stores with each texture stage + TextureStageState_t m_TextureStage[MAX_TEXTURE_STAGES]; + SamplerState_t m_SamplerState[MAX_SAMPLERS]; +}; + + +//----------------------------------------------------------------------------- +// Class factory +//----------------------------------------------------------------------------- +static CShaderShadowDX8 g_ShaderShadow; +IShaderShadowDX8 *g_pShaderShadowDx8 = &g_ShaderShadow; + +EXPOSE_SINGLE_INTERFACE_GLOBALVAR( CShaderShadowDX8, IShaderShadow, + SHADERSHADOW_INTERFACE_VERSION, g_ShaderShadow ) + +//----------------------------------------------------------------------------- +// Global instance +//----------------------------------------------------------------------------- +IShaderShadowDX8* ShaderShadow() +{ + return &g_ShaderShadow; +} + + +//----------------------------------------------------------------------------- +// Constructor, destructor +//----------------------------------------------------------------------------- +CShaderShadowDX8::CShaderShadowDX8( ) : + m_DrawFlags(0), m_pHardwareConfig(0), m_HasConstantColor(false) +{ + memset( &m_ShadowState, 0, sizeof(m_ShadowState) ); + memset( &m_TextureStage, 0, sizeof(m_TextureStage) ); +} + +CShaderShadowDX8::~CShaderShadowDX8() +{ +} + + +//----------------------------------------------------------------------------- +// Initialize render state +//----------------------------------------------------------------------------- +void CShaderShadowDX8::Init( ) +{ + m_pHardwareConfig = HardwareConfig(); + + // Clear out the shadow state + memset( &m_ShadowState, 0, sizeof(m_ShadowState) ); + + // No funky custom methods.. + m_CustomTextureStageState = false; + + // No constant color modulation + m_HasConstantColor = false; + m_HasConstantAlpha = false; + m_HasVertexAlpha = false; + + m_ShadowShaderState.m_ModulateConstantColor = false; + + m_ShadowState.m_bDisableFogGammaCorrection = false; + + // By default we're using fixed function + m_ShadowState.m_UsingFixedFunction = true; + + // Lighting off by default + m_ShadowState.m_Lighting = false; + + // Pixel + vertex shaders + m_ShadowShaderState.m_VertexShader = INVALID_SHADER; + m_ShadowShaderState.m_PixelShader = INVALID_SHADER; + m_ShadowShaderState.m_nStaticPshIndex = 0; + m_ShadowShaderState.m_nStaticVshIndex = 0; + m_ShadowShaderState.m_VertexUsage = 0; + + // Drawing nothing.. + m_DrawFlags = 0; + + // No alpha control + m_AlphaPipe = false; + + // Vertex blending + m_NumBlendVertices = 0; + m_ShadowState.m_VertexBlendEnable = false; + + // NOTE: If you change these defaults, change the code in ComputeAggregateShadowState + CreateTransitionTableEntry + int i; + for (i = 0; i < MAX_TEXTURE_STAGES; ++i) + { + m_ShadowState.m_TextureStage[i].m_ColorOp = D3DTOP_DISABLE; + m_ShadowState.m_TextureStage[i].m_ColorArg1 = D3DTA_TEXTURE; + m_ShadowState.m_TextureStage[i].m_ColorArg2 = (i == 0) ? D3DTA_DIFFUSE : D3DTA_CURRENT; + m_ShadowState.m_TextureStage[i].m_AlphaOp = D3DTOP_DISABLE; + m_ShadowState.m_TextureStage[i].m_AlphaArg1 = D3DTA_TEXTURE; + m_ShadowState.m_TextureStage[i].m_AlphaArg2 = (i == 0) ? D3DTA_DIFFUSE : D3DTA_CURRENT; + m_ShadowState.m_TextureStage[i].m_TexCoordIndex = i; + } + + for (i = 0; i < MAX_SAMPLERS; ++i) + { + m_ShadowState.m_SamplerState[i].m_TextureEnable = false; + m_ShadowState.m_SamplerState[i].m_SRGBReadEnable = false; + m_ShadowState.m_SamplerState[i].m_Fetch4Enable = false; +#ifdef DX_TO_GL_ABSTRACTION + m_ShadowState.m_SamplerState[i].m_ShadowFilterEnable = false; +#endif + // A *real* measure if the texture stage is being used. + // we sometimes have to set the shadow state to not mirror this. + m_SamplerState[i].m_TextureEnable = false; + } +} + + +//----------------------------------------------------------------------------- +// Sets the default state +//----------------------------------------------------------------------------- +void CShaderShadowDX8::SetDefaultState() +{ + DepthFunc( SHADER_DEPTHFUNC_NEAREROREQUAL ); + EnableDepthWrites( true ); + EnableDepthTest( true ); + EnableColorWrites( true ); + EnableAlphaWrites( false ); + EnableAlphaTest( false ); + EnableLighting( false ); + EnableConstantColor( false ); + EnableBlending( false ); + BlendFunc( SHADER_BLEND_ONE, SHADER_BLEND_ZERO ); + BlendOp( SHADER_BLEND_OP_ADD ); + // GR - separate alpha + EnableBlendingSeparateAlpha( false ); + BlendFuncSeparateAlpha( SHADER_BLEND_ONE, SHADER_BLEND_ZERO ); + BlendOpSeparateAlpha( SHADER_BLEND_OP_ADD ); + AlphaFunc( SHADER_ALPHAFUNC_GEQUAL, 0.7f ); + PolyMode( SHADER_POLYMODEFACE_FRONT_AND_BACK, SHADER_POLYMODE_FILL ); + EnableCulling( true ); + EnableAlphaToCoverage( false ); + EnablePolyOffset( SHADER_POLYOFFSET_DISABLE ); + EnableVertexBlend( false ); + EnableSpecular( false ); + EnableSRGBWrite( false ); + DrawFlags( SHADER_DRAW_POSITION ); + EnableCustomPixelPipe( false ); + CustomTextureStages( 0 ); + EnableAlphaPipe( false ); + EnableConstantAlpha( false ); + EnableVertexAlpha( false ); + SetVertexShader( NULL, 0 ); + SetPixelShader( NULL, 0 ); + FogMode( SHADER_FOGMODE_DISABLED ); + DisableFogGammaCorrection( false ); + SetDiffuseMaterialSource( SHADER_MATERIALSOURCE_MATERIAL ); + EnableStencil( false ); + StencilFunc( SHADER_STENCILFUNC_ALWAYS ); + StencilPassOp( SHADER_STENCILOP_KEEP ); + StencilFailOp( SHADER_STENCILOP_KEEP ); + StencilDepthFailOp( SHADER_STENCILOP_KEEP ); + StencilReference( 0 ); + StencilMask( 0xFFFFFFFF ); + StencilWriteMask( 0xFFFFFFFF ); + m_ShadowShaderState.m_VertexUsage = 0; + + int i; + int nSamplerCount = HardwareConfig()->GetSamplerCount(); + for( i = 0; i < nSamplerCount; i++ ) + { + EnableTexture( (Sampler_t)i, false ); + EnableSRGBRead( (Sampler_t)i, false ); + } + + int nTextureStageCount = HardwareConfig()->GetTextureStageCount(); + for( i = 0; i < nTextureStageCount; i++ ) + { + EnableTexGen( (TextureStage_t)i, false ); + OverbrightValue( (TextureStage_t)i, 1.0f ); + EnableTextureAlpha( (TextureStage_t)i, false ); + CustomTextureOperation( (TextureStage_t)i, SHADER_TEXCHANNEL_COLOR, + SHADER_TEXOP_DISABLE, SHADER_TEXARG_TEXTURE, SHADER_TEXARG_PREVIOUSSTAGE ); + CustomTextureOperation( (TextureStage_t)i, SHADER_TEXCHANNEL_ALPHA, + SHADER_TEXOP_DISABLE, SHADER_TEXARG_TEXTURE, SHADER_TEXARG_PREVIOUSSTAGE ); + } +} + + +//----------------------------------------------------------------------------- +// Gets at the shadow state +//----------------------------------------------------------------------------- +const ShadowState_t &CShaderShadowDX8::GetShadowState() +{ + return m_ShadowState; +} + +const ShadowShaderState_t &CShaderShadowDX8::GetShadowShaderState() +{ + return m_ShadowShaderState; +} + + +//----------------------------------------------------------------------------- +// Depth functions... +//----------------------------------------------------------------------------- +void CShaderShadowDX8::DepthFunc( ShaderDepthFunc_t depthFunc ) +{ + D3DCMPFUNC zFunc; + + switch( depthFunc ) + { + case SHADER_DEPTHFUNC_NEVER: + zFunc = D3DCMP_NEVER; + break; + case SHADER_DEPTHFUNC_NEARER: + zFunc = (ShaderUtil()->GetConfig().bReverseDepth ^ ReverseDepthOnX360()) ? D3DCMP_GREATER : D3DCMP_LESS; + break; + case SHADER_DEPTHFUNC_EQUAL: + zFunc = D3DCMP_EQUAL; + break; + case SHADER_DEPTHFUNC_NEAREROREQUAL: + zFunc = (ShaderUtil()->GetConfig().bReverseDepth ^ ReverseDepthOnX360()) ? D3DCMP_GREATEREQUAL : D3DCMP_LESSEQUAL; + break; + case SHADER_DEPTHFUNC_FARTHER: + zFunc = (ShaderUtil()->GetConfig().bReverseDepth ^ ReverseDepthOnX360()) ? D3DCMP_LESS : D3DCMP_GREATER; + break; + case SHADER_DEPTHFUNC_NOTEQUAL: + zFunc = D3DCMP_NOTEQUAL; + break; + case SHADER_DEPTHFUNC_FARTHEROREQUAL: + zFunc = (ShaderUtil()->GetConfig().bReverseDepth ^ ReverseDepthOnX360()) ? D3DCMP_LESSEQUAL : D3DCMP_GREATEREQUAL; + break; + case SHADER_DEPTHFUNC_ALWAYS: + zFunc = D3DCMP_ALWAYS; + break; + default: + zFunc = D3DCMP_ALWAYS; + Warning( "DepthFunc: invalid param\n" ); + break; + } + + m_ShadowState.m_ZFunc = zFunc; +} + +void CShaderShadowDX8::EnableDepthWrites( bool bEnable ) +{ + m_ShadowState.m_ZWriteEnable = bEnable; +} + +void CShaderShadowDX8::EnableDepthTest( bool bEnable ) +{ + m_ShadowState.m_ZEnable = bEnable ? D3DZB_TRUE : D3DZB_FALSE; +} + +void CShaderShadowDX8::EnablePolyOffset( PolygonOffsetMode_t nOffsetMode ) +{ + m_ShadowState.m_ZBias = nOffsetMode; +} + +//----------------------------------------------------------------------------- +// Color write state +//----------------------------------------------------------------------------- +void CShaderShadowDX8::EnableColorWrites( bool bEnable ) +{ + if (bEnable) + { + m_ShadowState.m_ColorWriteEnable |= D3DCOLORWRITEENABLE_BLUE | + D3DCOLORWRITEENABLE_GREEN | D3DCOLORWRITEENABLE_RED; + } + else + { + m_ShadowState.m_ColorWriteEnable &= ~( D3DCOLORWRITEENABLE_BLUE | + D3DCOLORWRITEENABLE_GREEN | D3DCOLORWRITEENABLE_RED ); + } +} + +void CShaderShadowDX8::EnableAlphaWrites( bool bEnable ) +{ + if (bEnable) + { + m_ShadowState.m_ColorWriteEnable |= D3DCOLORWRITEENABLE_ALPHA; + } + else + { + m_ShadowState.m_ColorWriteEnable &= ~D3DCOLORWRITEENABLE_ALPHA; + } +} + + +//----------------------------------------------------------------------------- +// Alpha blending states +//----------------------------------------------------------------------------- +void CShaderShadowDX8::EnableBlending( bool bEnable ) +{ + m_ShadowState.m_AlphaBlendEnable = bEnable; +} + +// GR - separate alpha +void CShaderShadowDX8::EnableBlendingSeparateAlpha( bool bEnable ) +{ + m_ShadowState.m_SeparateAlphaBlendEnable = bEnable; +} + +void CShaderShadowDX8::EnableAlphaTest( bool bEnable ) +{ + m_ShadowState.m_AlphaTestEnable = bEnable; +} + +void CShaderShadowDX8::AlphaFunc( ShaderAlphaFunc_t alphaFunc, float alphaRef /* [0-1] */ ) +{ + D3DCMPFUNC d3dCmpFunc; + + switch( alphaFunc ) + { + case SHADER_ALPHAFUNC_NEVER: + d3dCmpFunc = D3DCMP_NEVER; + break; + case SHADER_ALPHAFUNC_LESS: + d3dCmpFunc = D3DCMP_LESS; + break; + case SHADER_ALPHAFUNC_EQUAL: + d3dCmpFunc = D3DCMP_EQUAL; + break; + case SHADER_ALPHAFUNC_LEQUAL: + d3dCmpFunc = D3DCMP_LESSEQUAL; + break; + case SHADER_ALPHAFUNC_GREATER: + d3dCmpFunc = D3DCMP_GREATER; + break; + case SHADER_ALPHAFUNC_NOTEQUAL: + d3dCmpFunc = D3DCMP_NOTEQUAL; + break; + case SHADER_ALPHAFUNC_GEQUAL: + d3dCmpFunc = D3DCMP_GREATEREQUAL; + break; + case SHADER_ALPHAFUNC_ALWAYS: + d3dCmpFunc = D3DCMP_ALWAYS; + break; + default: + Warning( "AlphaFunc: invalid param\n" ); + return; + } + + m_AlphaFunc = d3dCmpFunc; + m_AlphaRef = (int)(alphaRef * 255); +} + +D3DBLEND CShaderShadowDX8::BlendFuncValue( ShaderBlendFactor_t factor ) const +{ + switch( factor ) + { + case SHADER_BLEND_ZERO: + return D3DBLEND_ZERO; + + case SHADER_BLEND_ONE: + return D3DBLEND_ONE; + + case SHADER_BLEND_DST_COLOR: + return D3DBLEND_DESTCOLOR; + + case SHADER_BLEND_ONE_MINUS_DST_COLOR: + return D3DBLEND_INVDESTCOLOR; + + case SHADER_BLEND_SRC_ALPHA: + return D3DBLEND_SRCALPHA; + + case SHADER_BLEND_ONE_MINUS_SRC_ALPHA: + return D3DBLEND_INVSRCALPHA; + + case SHADER_BLEND_DST_ALPHA: + return D3DBLEND_DESTALPHA; + + case SHADER_BLEND_ONE_MINUS_DST_ALPHA: + return D3DBLEND_INVDESTALPHA; + + case SHADER_BLEND_SRC_ALPHA_SATURATE: + return D3DBLEND_SRCALPHASAT; + + case SHADER_BLEND_SRC_COLOR: + return D3DBLEND_SRCCOLOR; + + case SHADER_BLEND_ONE_MINUS_SRC_COLOR: + return D3DBLEND_INVSRCCOLOR; + } + + Warning( "BlendFunc: invalid factor\n" ); + return D3DBLEND_ONE; +} + +D3DBLENDOP CShaderShadowDX8::BlendOpValue( ShaderBlendOp_t blendOp ) const +{ + switch( blendOp ) + { + case SHADER_BLEND_OP_ADD: + return D3DBLENDOP_ADD; + + case SHADER_BLEND_OP_SUBTRACT: + return D3DBLENDOP_SUBTRACT; + + case SHADER_BLEND_OP_REVSUBTRACT: + return D3DBLENDOP_REVSUBTRACT; + + case SHADER_BLEND_OP_MIN: + return D3DBLENDOP_MIN; + + case SHADER_BLEND_OP_MAX: + return D3DBLENDOP_MAX; + } + + Warning( "BlendOp: invalid op\n" ); + return D3DBLENDOP_ADD; +} + +void CShaderShadowDX8::BlendFunc( ShaderBlendFactor_t srcFactor, ShaderBlendFactor_t dstFactor ) +{ + D3DBLEND d3dSrcFactor = BlendFuncValue( srcFactor ); + D3DBLEND d3dDstFactor = BlendFuncValue( dstFactor ); + m_SrcBlend = d3dSrcFactor; + m_DestBlend = d3dDstFactor; +} + +// GR - separate alpha blend +void CShaderShadowDX8::BlendFuncSeparateAlpha( ShaderBlendFactor_t srcFactor, ShaderBlendFactor_t dstFactor ) +{ + D3DBLEND d3dSrcFactor = BlendFuncValue( srcFactor ); + D3DBLEND d3dDstFactor = BlendFuncValue( dstFactor ); + m_SrcBlendAlpha = d3dSrcFactor; + m_DestBlendAlpha = d3dDstFactor; +} + +void CShaderShadowDX8::BlendOp( ShaderBlendOp_t blendOp ) +{ + m_BlendOp = BlendOpValue( blendOp ); +} + +void CShaderShadowDX8::BlendOpSeparateAlpha( ShaderBlendOp_t blendOp ) +{ + m_BlendOpAlpha = BlendOpValue( blendOp ); +} + +//----------------------------------------------------------------------------- +// Polygon fill mode states +//----------------------------------------------------------------------------- +void CShaderShadowDX8::PolyMode( ShaderPolyModeFace_t face, ShaderPolyMode_t polyMode ) +{ + // DX8 can't handle different modes on front and back faces +// FIXME: Assert( face == SHADER_POLYMODEFACE_FRONT_AND_BACK ); + if (face == SHADER_POLYMODEFACE_BACK) + return; + + D3DFILLMODE fillMode; + switch( polyMode ) + { + case SHADER_POLYMODE_POINT: + fillMode = D3DFILL_POINT; + break; + case SHADER_POLYMODE_LINE: + fillMode = D3DFILL_WIREFRAME; + break; + case SHADER_POLYMODE_FILL: + fillMode = D3DFILL_SOLID; + break; + default: + Warning( "PolyMode: invalid poly mode\n" ); + return; + } + + m_ShadowState.m_FillMode = fillMode; +} + + +//----------------------------------------------------------------------------- +// Backface cull states +//----------------------------------------------------------------------------- +void CShaderShadowDX8::EnableCulling( bool bEnable ) +{ + m_ShadowState.m_CullEnable = bEnable; +} + + +//----------------------------------------------------------------------------- +// Alpha to coverage +//----------------------------------------------------------------------------- +void CShaderShadowDX8::EnableAlphaToCoverage( bool bEnable ) +{ + m_ShadowState.m_EnableAlphaToCoverage = bEnable; +} + + +//----------------------------------------------------------------------------- +// Indicates we've got a constant color specified +//----------------------------------------------------------------------------- +bool CShaderShadowDX8::HasConstantColor() const +{ + return m_HasConstantColor; +} + +void CShaderShadowDX8::EnableConstantColor( bool bEnable ) +{ + m_HasConstantColor = bEnable; +} + + +//----------------------------------------------------------------------------- +// A simpler method of dealing with alpha modulation +//----------------------------------------------------------------------------- +void CShaderShadowDX8::EnableAlphaPipe( bool bEnable ) +{ + m_AlphaPipe = bEnable; +} + +void CShaderShadowDX8::EnableConstantAlpha( bool bEnable ) +{ + m_HasConstantAlpha = bEnable; +} + +void CShaderShadowDX8::EnableVertexAlpha( bool bEnable ) +{ + m_HasVertexAlpha = bEnable; +} + +void CShaderShadowDX8::EnableTextureAlpha( TextureStage_t stage, bool bEnable ) +{ + if ( stage < m_pHardwareConfig->GetSamplerCount() ) + { + m_TextureStage[stage].m_TextureAlphaEnable = bEnable; + } +} + + +//----------------------------------------------------------------------------- +// Indicates we're going to light the model +//----------------------------------------------------------------------------- +void CShaderShadowDX8::EnableLighting( bool bEnable ) +{ + m_ShadowState.m_Lighting = bEnable; +} + + +//----------------------------------------------------------------------------- +// Enables specular lighting (lighting has also got to be enabled) +//----------------------------------------------------------------------------- +void CShaderShadowDX8::EnableSpecular( bool bEnable ) +{ + m_ShadowState.m_SpecularEnable = bEnable; +} + +//----------------------------------------------------------------------------- +// Enables auto-conversion from linear to gamma space on write to framebuffer. +//----------------------------------------------------------------------------- +void CShaderShadowDX8::EnableSRGBWrite( bool bEnable ) +{ + if ( m_pHardwareConfig->SupportsSRGB() ) + { + m_ShadowState.m_SRGBWriteEnable = bEnable; + } + else + { + m_ShadowState.m_SRGBWriteEnable = false; + } +} + +//----------------------------------------------------------------------------- +// Activate/deactivate skinning +//----------------------------------------------------------------------------- +void CShaderShadowDX8::EnableVertexBlend( bool bEnable ) +{ + // Activate/deactivate skinning. Indexed blending is automatically + // enabled if it's available for this hardware. When blending is enabled, + // we allocate enough room for 3 weights (max allowed) + if ((m_pHardwareConfig->MaxBlendMatrices() > 0) || (!bEnable)) + { + m_ShadowState.m_VertexBlendEnable = bEnable; + } +} + + +//----------------------------------------------------------------------------- +// Texturemapping state +//----------------------------------------------------------------------------- +void CShaderShadowDX8::EnableTexture( Sampler_t sampler, bool bEnable ) +{ + if ( sampler < m_pHardwareConfig->GetSamplerCount() ) + { + m_SamplerState[sampler].m_TextureEnable = bEnable; + } + else + { + Warning( "Attempting to bind a texture to an invalid sampler (%d)!\n", sampler ); + } +} + +void CShaderShadowDX8::EnableSRGBRead( Sampler_t sampler, bool bEnable ) +{ + if ( !m_pHardwareConfig->SupportsSRGB() ) + { + m_ShadowState.m_SamplerState[sampler].m_SRGBReadEnable = false; + return; + } + + if ( sampler < m_pHardwareConfig->GetSamplerCount() ) + { + m_ShadowState.m_SamplerState[sampler].m_SRGBReadEnable = bEnable; + } + else + { + Warning( "Attempting set SRGBRead state on an invalid sampler (%d)!\n", sampler ); + } +} + +void CShaderShadowDX8::SetShadowDepthFiltering( Sampler_t stage ) +{ +#ifdef DX_TO_GL_ABSTRACTION + if ( stage < m_pHardwareConfig->GetSamplerCount() ) + { + m_ShadowState.m_SamplerState[stage].m_ShadowFilterEnable = true; + return; + } +#else + if ( !m_pHardwareConfig->SupportsFetch4() ) + { + m_ShadowState.m_SamplerState[stage].m_Fetch4Enable = false; + return; + } + + if ( stage < m_pHardwareConfig->GetSamplerCount() ) + { + m_ShadowState.m_SamplerState[stage].m_Fetch4Enable = true; + return; + } +#endif + + Warning( "Attempting set shadow filtering state on an invalid sampler (%d)!\n", stage ); +} + + +//----------------------------------------------------------------------------- +// Binds texture coordinates to a particular stage... +//----------------------------------------------------------------------------- +void CShaderShadowDX8::TextureCoordinate( TextureStage_t stage, int useTexCoord ) +{ + if ( stage < m_pHardwareConfig->GetTextureStageCount() ) + { + m_TextureStage[stage].m_TexCoordinate = useTexCoord; + + // Need to recompute the texCoordIndex, since that's affected by this + RecomputeTexCoordIndex(stage); + } +} + + +//----------------------------------------------------------------------------- +// Automatic texture coordinate generation +//----------------------------------------------------------------------------- +void CShaderShadowDX8::RecomputeTexCoordIndex( TextureStage_t stage ) +{ + int texCoordIndex = m_TextureStage[stage].m_TexCoordinate; + if (m_TextureStage[stage].m_TexGenEnable) + texCoordIndex |= m_TextureStage[stage].m_TexCoordIndex; + m_ShadowState.m_TextureStage[stage].m_TexCoordIndex = texCoordIndex; +} + + +//----------------------------------------------------------------------------- +// Automatic texture coordinate generation +//----------------------------------------------------------------------------- +void CShaderShadowDX8::EnableTexGen( TextureStage_t stage, bool bEnable ) +{ + if ( stage >= m_pHardwareConfig->GetTextureStageCount() ) + { + Assert( 0 ); + return; + } + + m_TextureStage[stage].m_TexGenEnable = bEnable; + RecomputeTexCoordIndex(stage); +} + +//----------------------------------------------------------------------------- +// Automatic texture coordinate generation +//----------------------------------------------------------------------------- +void CShaderShadowDX8::TexGen( TextureStage_t stage, ShaderTexGenParam_t param ) +{ +#ifdef FIXED_FUNCTION_PIPELINE + if ( stage >= m_pHardwareConfig->GetTextureStageCount() ) + return; + + switch( param ) + { + case SHADER_TEXGENPARAM_OBJECT_LINEAR: + m_TextureStage[stage].m_TexCoordIndex = 0; + break; + case SHADER_TEXGENPARAM_EYE_LINEAR: + m_TextureStage[stage].m_TexCoordIndex = D3DTSS_TCI_CAMERASPACEPOSITION; + break; + case SHADER_TEXGENPARAM_SPHERE_MAP: + if ( m_pHardwareConfig->SupportsSpheremapping() ) + { + m_TextureStage[stage].m_TexCoordIndex = D3DTSS_TCI_SPHEREMAP; + } + else + { + m_TextureStage[stage].m_TexCoordIndex = D3DTSS_TCI_CAMERASPACEREFLECTIONVECTOR; + } + break; + case SHADER_TEXGENPARAM_CAMERASPACEREFLECTIONVECTOR: + m_TextureStage[stage].m_TexCoordIndex = D3DTSS_TCI_CAMERASPACEREFLECTIONVECTOR; + break; + case SHADER_TEXGENPARAM_CAMERASPACENORMAL: + m_TextureStage[stage].m_TexCoordIndex = D3DTSS_TCI_CAMERASPACENORMAL; + break; + } + + // Set the board state... + RecomputeTexCoordIndex(stage); +#endif +} + + +//----------------------------------------------------------------------------- +// Overbrighting +//----------------------------------------------------------------------------- +void CShaderShadowDX8::OverbrightValue( TextureStage_t stage, float value ) +{ + if ( m_pHardwareConfig->SupportsOverbright() && + ( stage < m_pHardwareConfig->GetTextureStageCount() ) ) + { + m_TextureStage[stage].m_OverbrightVal = value; + } +} + + + + +//----------------------------------------------------------------------------- +// alternate method of specifying per-texture unit stuff, more flexible and more complicated +// Can be used to specify different operation per channel (alpha/color)... +//----------------------------------------------------------------------------- +void CShaderShadowDX8::EnableCustomPixelPipe( bool bEnable ) +{ + m_CustomTextureStageState = bEnable; +} + +void CShaderShadowDX8::CustomTextureStages( int stageCount ) +{ + m_CustomTextureStages = stageCount; + Assert( stageCount <= m_pHardwareConfig->GetTextureStageCount() ); + if ( stageCount > m_pHardwareConfig->GetTextureStageCount() ) + stageCount = m_pHardwareConfig->GetTextureStageCount(); +} + +void CShaderShadowDX8::CustomTextureOperation( TextureStage_t stage, + ShaderTexChannel_t channel, ShaderTexOp_t op, ShaderTexArg_t arg1, ShaderTexArg_t arg2 ) +{ + m_TextureStage[stage].m_Op[channel]= op; + m_TextureStage[stage].m_Arg[channel][0] = arg1; + m_TextureStage[stage].m_Arg[channel][1] = arg2; +} + + +//----------------------------------------------------------------------------- +// Compute the vertex format from vertex descriptor flags +//----------------------------------------------------------------------------- +void CShaderShadowDX8::VertexShaderVertexFormat( unsigned int nFlags, + int nTexCoordCount, int* pTexCoordDimensions, int nUserDataSize ) +{ + // Code that creates a Mesh should specify whether it contains bone weights+indices, *not* the shader. + Assert( ( nFlags & VERTEX_BONE_INDEX ) == 0 ); + nFlags &= ~VERTEX_BONE_INDEX; + + // This indicates we're using a vertex shader + nFlags |= VERTEX_FORMAT_VERTEX_SHADER; + m_ShadowShaderState.m_VertexUsage = MeshMgr()->ComputeVertexFormat( nFlags, nTexCoordCount, + pTexCoordDimensions, 0, nUserDataSize ); + m_ShadowState.m_UsingFixedFunction = false; + + // Avoid an error if vertex stream 0 is too narrow + if ( CVertexBufferBase::VertexFormatSize( m_ShadowShaderState.m_VertexUsage ) <= 16 ) + { + // FIXME: this is only necessary because we + // (a) put the flex normal/position stream in ALL vertex decls + // (b) bind stream 0's VB to stream 2 if there is no actual flex data + // ...it would be far more sensible to not add stream 2 to all vertex decls. + static bool bComplained = false; + if( !bComplained ) + { + Warning( "ERROR: shader asking for a too-narrow vertex format - you will see errors if running with debug D3D DLLs!\n\tPadding the vertex format with extra texcoords\n\tWill not warn again.\n" ); + bComplained = true; + } + // All vertex formats should contain position... + Assert( nFlags & VERTEX_POSITION ); + nFlags |= VERTEX_POSITION; + // This error should occur only if we have zero texcoords, or if we have a single, 1-D texcoord + Assert( ( nTexCoordCount == 0 ) || + ( ( nTexCoordCount == 1 ) && pTexCoordDimensions && ( pTexCoordDimensions[0] == 1 ) ) ); + nTexCoordCount = 1; + m_ShadowShaderState.m_VertexUsage = MeshMgr()->ComputeVertexFormat( nFlags, nTexCoordCount, NULL, 0, nUserDataSize ); + } +} + + +//----------------------------------------------------------------------------- +// Compute the vertex format from vertex descriptor flags +//----------------------------------------------------------------------------- +void CShaderShadowDX8::SetMorphFormat( MorphFormat_t flags ) +{ + m_ShadowShaderState.m_MorphUsage = flags; +} + +//----------------------------------------------------------------------------- +// Pixel and vertex shader methods +//----------------------------------------------------------------------------- +void CShaderShadowDX8::SetVertexShader( const char* pFileName, int nStaticVshIndex ) +{ + char debugLabel[500] = ""; +#ifdef DX_TO_GL_ABSTRACTION + Q_snprintf( debugLabel, sizeof(debugLabel), "vs-file %s vs-index %d", pFileName, nStaticVshIndex ); +#endif + + m_ShadowShaderState.m_VertexShader = ShaderManager()->CreateVertexShader( pFileName, nStaticVshIndex, debugLabel ); + m_ShadowShaderState.m_nStaticVshIndex = nStaticVshIndex; +} + +void CShaderShadowDX8::SetPixelShader( const char* pFileName, int nStaticPshIndex ) +{ + char debugLabel[500] = ""; +#ifdef DX_TO_GL_ABSTRACTION + Q_snprintf( debugLabel, sizeof(debugLabel), "ps-file %s ps-index %d", pFileName, nStaticPshIndex ); +#endif + + m_ShadowShaderState.m_PixelShader = ShaderManager()->CreatePixelShader( pFileName, nStaticPshIndex, debugLabel ); + m_ShadowShaderState.m_nStaticPshIndex = nStaticPshIndex; +} + + +//----------------------------------------------------------------------------- +// NOTE: See Version 5 of this file for NVidia 8-stage shader stuff +//----------------------------------------------------------------------------- +inline bool CShaderShadowDX8::IsUsingTextureCoordinates( Sampler_t sampler ) const +{ + return m_SamplerState[sampler].m_TextureEnable; +} + +inline D3DTEXTUREOP CShaderShadowDX8::OverbrightBlendValue( TextureStage_t stage ) +{ + D3DTEXTUREOP colorop; + if (m_TextureStage[stage].m_OverbrightVal < 2.0F) + colorop = D3DTOP_MODULATE; + else if (m_TextureStage[stage].m_OverbrightVal < 4.0F) + colorop = D3DTOP_MODULATE2X; + else + colorop = D3DTOP_MODULATE4X; + return colorop; +} + +static inline int ComputeArg( ShaderTexArg_t arg ) +{ + switch(arg) + { + case SHADER_TEXARG_TEXTURE: + return D3DTA_TEXTURE; + + case SHADER_TEXARG_ZERO: + return D3DTA_SPECULAR | D3DTA_COMPLEMENT; + + case SHADER_TEXARG_ONE: + return D3DTA_SPECULAR; + + case SHADER_TEXARG_TEXTUREALPHA: + return D3DTA_TEXTURE | D3DTA_ALPHAREPLICATE; + + case SHADER_TEXARG_INVTEXTUREALPHA: + return D3DTA_TEXTURE | D3DTA_ALPHAREPLICATE | D3DTA_COMPLEMENT; + + case SHADER_TEXARG_NONE: + case SHADER_TEXARG_VERTEXCOLOR: + return D3DTA_DIFFUSE; + + case SHADER_TEXARG_SPECULARCOLOR: + return D3DTA_SPECULAR; + + case SHADER_TEXARG_CONSTANTCOLOR: + return D3DTA_TFACTOR; + + case SHADER_TEXARG_PREVIOUSSTAGE: + return D3DTA_CURRENT; + } + + Assert(0); + return D3DTA_TEXTURE; +} + +static inline D3DTEXTUREOP ComputeOp( ShaderTexOp_t op ) +{ + switch(op) + { + case SHADER_TEXOP_MODULATE: + return D3DTOP_MODULATE; + + case SHADER_TEXOP_MODULATE2X: + return D3DTOP_MODULATE2X; + + case SHADER_TEXOP_MODULATE4X: + return D3DTOP_MODULATE4X; + + case SHADER_TEXOP_SELECTARG1: + return D3DTOP_SELECTARG1; + + case SHADER_TEXOP_SELECTARG2: + return D3DTOP_SELECTARG2; + + case SHADER_TEXOP_ADD: + return D3DTOP_ADD; + + case SHADER_TEXOP_SUBTRACT: + return D3DTOP_SUBTRACT; + + case SHADER_TEXOP_ADDSIGNED2X: + return D3DTOP_ADDSIGNED2X; + + case SHADER_TEXOP_BLEND_CONSTANTALPHA: + return D3DTOP_BLENDFACTORALPHA; + + case SHADER_TEXOP_BLEND_PREVIOUSSTAGEALPHA: + return D3DTOP_BLENDCURRENTALPHA; + + case SHADER_TEXOP_BLEND_TEXTUREALPHA: + return D3DTOP_BLENDTEXTUREALPHA; + + case SHADER_TEXOP_MODULATECOLOR_ADDALPHA: + return D3DTOP_MODULATECOLOR_ADDALPHA; + + case SHADER_TEXOP_MODULATEINVCOLOR_ADDALPHA: + return D3DTOP_MODULATEINVCOLOR_ADDALPHA; + + case SHADER_TEXOP_DOTPRODUCT3: + return D3DTOP_DOTPRODUCT3; + + case SHADER_TEXOP_DISABLE: + return D3DTOP_DISABLE; + } + + Assert(0); + return D3DTOP_MODULATE; +} + +void CShaderShadowDX8::ConfigureCustomFVFVertexShader( unsigned int flags ) +{ + int i; + for ( i = 0; i < m_CustomTextureStages; ++i) + { + m_ShadowState.m_TextureStage[i].m_ColorArg1 = ComputeArg( m_TextureStage[i].m_Arg[0][0] ); + m_ShadowState.m_TextureStage[i].m_ColorArg2 = ComputeArg( m_TextureStage[i].m_Arg[0][1] ); + m_ShadowState.m_TextureStage[i].m_AlphaArg1 = ComputeArg( m_TextureStage[i].m_Arg[1][0] ); + m_ShadowState.m_TextureStage[i].m_AlphaArg2 = ComputeArg( m_TextureStage[i].m_Arg[1][1] ); + m_ShadowState.m_TextureStage[i].m_ColorOp = ComputeOp( m_TextureStage[i].m_Op[0] ); + m_ShadowState.m_TextureStage[i].m_AlphaOp = ComputeOp( m_TextureStage[i].m_Op[1] ); + } + + // Deal with texture stage 1 -> n + for ( i = m_CustomTextureStages; i < m_pHardwareConfig->GetTextureStageCount(); ++i ) + { + m_ShadowState.m_TextureStage[i].m_ColorArg1 = D3DTA_TEXTURE; + m_ShadowState.m_TextureStage[i].m_ColorArg2 = D3DTA_CURRENT; + m_ShadowState.m_TextureStage[i].m_AlphaArg1 = D3DTA_TEXTURE; + m_ShadowState.m_TextureStage[i].m_AlphaArg2 = D3DTA_CURRENT; + m_ShadowState.m_TextureStage[i].m_ColorOp = D3DTOP_DISABLE; + m_ShadowState.m_TextureStage[i].m_AlphaOp = D3DTOP_DISABLE; + } +} + + +//----------------------------------------------------------------------------- +// Sets up the alpha texture stage state +//----------------------------------------------------------------------------- +void CShaderShadowDX8::ConfigureAlphaPipe( unsigned int flags ) +{ + // Are we using color? + bool isUsingVertexAlpha = m_HasVertexAlpha && ((flags & SHADER_DRAW_COLOR) != 0); + bool isUsingConstantAlpha = m_HasConstantAlpha; + + int lastTextureStage = m_pHardwareConfig->GetTextureStageCount() - 1; + while ( lastTextureStage >= 0 ) + { + if ( m_TextureStage[lastTextureStage].m_TextureAlphaEnable ) + break; + --lastTextureStage; + } + + for ( int i = 0; i < m_pHardwareConfig->GetTextureStageCount(); ++i ) + { + m_ShadowState.m_TextureStage[i].m_AlphaOp = D3DTOP_MODULATE; + if ( m_TextureStage[i].m_TextureAlphaEnable ) + { + if (i == 0) + { + m_ShadowState.m_TextureStage[i].m_AlphaArg1 = D3DTA_TEXTURE; + m_ShadowState.m_TextureStage[i].m_AlphaArg2 = + isUsingConstantAlpha ? D3DTA_TFACTOR : D3DTA_DIFFUSE; + if (!isUsingConstantAlpha && !isUsingVertexAlpha) + m_ShadowState.m_TextureStage[i].m_AlphaOp = D3DTOP_SELECTARG1; + if (isUsingConstantAlpha) + isUsingConstantAlpha = false; + else if (isUsingVertexAlpha) + isUsingVertexAlpha = false; + } + else + { + // Deal with texture stage 0 + m_ShadowState.m_TextureStage[i].m_AlphaArg1 = D3DTA_TEXTURE; + m_ShadowState.m_TextureStage[i].m_AlphaArg2 = D3DTA_CURRENT; + } + } + else + { + // Blat out unused stages + if ((i > lastTextureStage) && !isUsingVertexAlpha && !isUsingConstantAlpha) + { + m_ShadowState.m_TextureStage[i].m_AlphaArg1 = D3DTA_TEXTURE; + m_ShadowState.m_TextureStage[i].m_AlphaArg2 = D3DTA_CURRENT; + m_ShadowState.m_TextureStage[i].m_AlphaOp = D3DTOP_DISABLE; + continue; + } + + // No texture coordinates; try to fold in vertex or constant alpha + if (i == 0) + { + m_ShadowState.m_TextureStage[i].m_AlphaArg1 = D3DTA_TFACTOR; + m_ShadowState.m_TextureStage[i].m_AlphaArg2 = D3DTA_DIFFUSE; + if (isUsingVertexAlpha) + { + m_ShadowState.m_TextureStage[i].m_AlphaOp = + isUsingConstantAlpha ? D3DTOP_MODULATE : D3DTOP_SELECTARG2; + } + else + { + m_ShadowState.m_TextureStage[i].m_AlphaOp = D3DTOP_SELECTARG1; + } + isUsingVertexAlpha = false; + isUsingConstantAlpha = false; + } + else + { + m_ShadowState.m_TextureStage[i].m_AlphaArg1 = D3DTA_CURRENT; + if (isUsingConstantAlpha) + { + m_ShadowState.m_TextureStage[i].m_AlphaArg2 = D3DTA_TFACTOR; + isUsingConstantAlpha = false; + } + else if (isUsingVertexAlpha) + { + m_ShadowState.m_TextureStage[i].m_AlphaArg2 = D3DTA_DIFFUSE; + isUsingVertexAlpha = false; + } + else + { + m_ShadowState.m_TextureStage[i].m_AlphaArg2 = D3DTA_DIFFUSE; + m_ShadowState.m_TextureStage[i].m_AlphaOp = D3DTOP_SELECTARG1; + } + } + } + } +} + + +//----------------------------------------------------------------------------- +// Sets up the texture stage state +//----------------------------------------------------------------------------- +void CShaderShadowDX8::ConfigureFVFVertexShader( unsigned int flags ) +{ + // For non-modulation, we can't really use the path below... + if (m_CustomTextureStageState) + { + ConfigureCustomFVFVertexShader( flags ); + return; + } + + // Deal with texture stage 0 + m_ShadowState.m_TextureStage[0].m_ColorArg1 = D3DTA_TEXTURE; + m_ShadowState.m_TextureStage[0].m_ColorArg2 = D3DTA_DIFFUSE; + m_ShadowState.m_TextureStage[0].m_AlphaArg1 = D3DTA_TEXTURE; + m_ShadowState.m_TextureStage[0].m_AlphaArg2 = D3DTA_DIFFUSE; + + // Are we using color? + bool isUsingVertexColor = (flags & SHADER_DRAW_COLOR) != 0; + bool isUsingConstantColor = (flags & SHADER_HAS_CONSTANT_COLOR) != 0; + + // Are we using texture coordinates? + if ( IsUsingTextureCoordinates( SHADER_SAMPLER0 ) ) + { + if (isUsingVertexColor) + { + m_ShadowState.m_TextureStage[0].m_ColorOp = OverbrightBlendValue(SHADER_TEXTURE_STAGE0); + m_ShadowState.m_TextureStage[0].m_AlphaOp = D3DTOP_MODULATE; + } + else + { + // Just blend in the constant color here, and don't blend it in below + m_ShadowState.m_TextureStage[0].m_ColorArg2 = D3DTA_TFACTOR; + m_ShadowState.m_TextureStage[0].m_AlphaArg2 = D3DTA_TFACTOR; + isUsingConstantColor = false; + + m_ShadowState.m_TextureStage[0].m_ColorOp = OverbrightBlendValue(SHADER_TEXTURE_STAGE0); + m_ShadowState.m_TextureStage[0].m_AlphaOp = D3DTOP_MODULATE; + } + } + else + { + // Are we using color? + if (isUsingVertexColor) + { + // Color, but no texture + if ( m_TextureStage[0].m_OverbrightVal < 2.0f ) + { + // Use diffuse * constant color, if we have a constant color + if (isUsingConstantColor) + { + m_ShadowState.m_TextureStage[0].m_ColorArg1 = D3DTA_TFACTOR; + m_ShadowState.m_TextureStage[0].m_AlphaArg1 = D3DTA_TFACTOR; + m_ShadowState.m_TextureStage[0].m_ColorOp = OverbrightBlendValue((TextureStage_t)0); + m_ShadowState.m_TextureStage[0].m_AlphaOp = D3DTOP_MODULATE; + + // This'll make sure we don't apply the constant color again below + isUsingConstantColor = false; + } + else + { + m_ShadowState.m_TextureStage[0].m_ColorOp = D3DTOP_SELECTARG2; + m_ShadowState.m_TextureStage[0].m_AlphaOp = D3DTOP_SELECTARG2; + } + } + else if (m_TextureStage[0].m_OverbrightVal < 4.0f) + { + // Produce diffuse + diffuse + m_ShadowState.m_TextureStage[0].m_ColorArg1 = D3DTA_DIFFUSE; + m_ShadowState.m_TextureStage[0].m_ColorOp = D3DTOP_ADD; + m_ShadowState.m_TextureStage[0].m_AlphaOp = D3DTOP_SELECTARG2; + } + else + { + // no 4x overbright yet! + Assert(0); + } + } + else + { + // No texture, no color + if (isUsingConstantColor) + { + m_ShadowState.m_TextureStage[0].m_ColorArg1 = D3DTA_TFACTOR; + m_ShadowState.m_TextureStage[0].m_AlphaArg1 = D3DTA_TFACTOR; + m_ShadowState.m_TextureStage[0].m_ColorOp = D3DTOP_SELECTARG1; + m_ShadowState.m_TextureStage[0].m_AlphaOp = D3DTOP_SELECTARG1; + + // This'll make sure we don't apply the constant color again below + isUsingConstantColor = false; + } + else + { + // Deal with texture stage 0 + m_ShadowState.m_TextureStage[0].m_ColorArg1 = D3DTA_TFACTOR; + m_ShadowState.m_TextureStage[0].m_AlphaArg1 = D3DTA_TFACTOR; + m_ShadowState.m_TextureStage[0].m_ColorOp = D3DTOP_SELECTARG1; + m_ShadowState.m_TextureStage[0].m_AlphaOp = D3DTOP_SELECTARG1; + } + } + } + + // Deal with texture stage 1 -> n + int lastUsedTextureStage = 0; + for ( int i = 1; i < m_pHardwareConfig->GetTextureStageCount(); ++i ) + { + m_ShadowState.m_TextureStage[i].m_ColorArg1 = D3DTA_TEXTURE; + m_ShadowState.m_TextureStage[i].m_ColorArg2 = D3DTA_CURRENT; + m_ShadowState.m_TextureStage[i].m_AlphaArg1 = D3DTA_TEXTURE; + m_ShadowState.m_TextureStage[i].m_AlphaArg2 = D3DTA_CURRENT; + + // Not doing anything? Disable the stage + if ( !IsUsingTextureCoordinates( (Sampler_t)i ) ) + { + if (m_TextureStage[i].m_OverbrightVal < 2.0f) + { + m_ShadowState.m_TextureStage[i].m_ColorOp = D3DTOP_DISABLE; + m_ShadowState.m_TextureStage[i].m_AlphaOp = D3DTOP_DISABLE; + } + else + { + // Here, we're modulating. Add in the constant color if we need to... + m_ShadowState.m_TextureStage[i].m_ColorArg1 = D3DTA_TFACTOR; + m_ShadowState.m_TextureStage[i].m_AlphaArg1 = D3DTA_TFACTOR; + + m_ShadowState.m_TextureStage[i].m_ColorOp = OverbrightBlendValue((TextureStage_t)i); + m_ShadowState.m_TextureStage[i].m_AlphaOp = D3DTOP_MODULATE; + + isUsingConstantColor = false; + lastUsedTextureStage = i; + } + } + else + { + // Here, we're modulating. Keep track of the last modulation stage, + // cause the constant color modulation comes in the stage after that + lastUsedTextureStage = i; + m_ShadowState.m_TextureStage[i].m_ColorOp = OverbrightBlendValue((TextureStage_t)i); + m_ShadowState.m_TextureStage[i].m_AlphaOp = D3DTOP_MODULATE; + } + } + + // massive amounts of suck: gotta overbright here if we really + // wanted to overbright stage0 but couldn't because of the add. + // This isn't totally correct, but there's no way around putting it here + // because we can't texture out of stage2 on low or medium end hardware + m_ShadowShaderState.m_ModulateConstantColor = false; + if (isUsingConstantColor) + { + ++lastUsedTextureStage; + + if (isUsingConstantColor && + (lastUsedTextureStage >= m_pHardwareConfig->GetTextureStageCount())) + { + // This is the case where we'd want to modulate in a particular texture + // stage, but we can't because there aren't enough. In this case, we're gonna + // need to do the modulation in the per-vertex color. + m_ShadowShaderState.m_ModulateConstantColor = true; + } + else + { + AssertOnce (lastUsedTextureStage < 2); + + // Here, we've got enough texture stages to do the modulation + m_ShadowState.m_TextureStage[lastUsedTextureStage].m_ColorArg1 = D3DTA_TFACTOR; + m_ShadowState.m_TextureStage[lastUsedTextureStage].m_ColorArg2 = D3DTA_CURRENT; + m_ShadowState.m_TextureStage[lastUsedTextureStage].m_AlphaArg1 = D3DTA_TFACTOR; + m_ShadowState.m_TextureStage[lastUsedTextureStage].m_AlphaArg2 = D3DTA_CURRENT; + m_ShadowState.m_TextureStage[lastUsedTextureStage].m_ColorOp = D3DTOP_MODULATE; + m_ShadowState.m_TextureStage[lastUsedTextureStage].m_AlphaOp = D3DTOP_MODULATE; + } + } + + // Overwrite the alpha stuff if we asked to independently control it + if (m_AlphaPipe) + { + ConfigureAlphaPipe( flags ); + } +} + + +//----------------------------------------------------------------------------- +// Makes sure we report if we're getting garbage. +//----------------------------------------------------------------------------- +void CShaderShadowDX8::DrawFlags( unsigned int flags ) +{ + m_DrawFlags = flags; + m_ShadowState.m_UsingFixedFunction = true; +} + + +//----------------------------------------------------------------------------- +// Compute texture coordinates +//----------------------------------------------------------------------------- +void CShaderShadowDX8::ConfigureTextureCoordinates( unsigned int flags ) +{ + // default... + for (int i = 0; i < m_pHardwareConfig->GetTextureStageCount(); ++i) + { + TextureCoordinate( (TextureStage_t)i, i ); + } + + if (flags & SHADER_DRAW_TEXCOORD0) + { + Assert( (flags & SHADER_DRAW_LIGHTMAP_TEXCOORD0) == 0 ); + TextureCoordinate( SHADER_TEXTURE_STAGE0, 0 ); + } + else if (flags & SHADER_DRAW_LIGHTMAP_TEXCOORD0) + { + TextureCoordinate( SHADER_TEXTURE_STAGE0, 1 ); + } + else if (flags & SHADER_DRAW_SECONDARY_TEXCOORD0 ) + { + TextureCoordinate( SHADER_TEXTURE_STAGE0, 2 ); + } + + if (flags & SHADER_DRAW_TEXCOORD1) + { + Assert( (flags & SHADER_DRAW_LIGHTMAP_TEXCOORD1) == 0 ); + TextureCoordinate( SHADER_TEXTURE_STAGE1, 0 ); + } + else if (flags & SHADER_DRAW_LIGHTMAP_TEXCOORD1) + { + TextureCoordinate( SHADER_TEXTURE_STAGE1, 1 ); + } + else if (flags & SHADER_DRAW_SECONDARY_TEXCOORD1 ) + { + TextureCoordinate( SHADER_TEXTURE_STAGE1, 2 ); + } +} + + +//----------------------------------------------------------------------------- +// Converts draw flags into vertex format +//----------------------------------------------------------------------------- +VertexFormat_t CShaderShadowDX8::FlagsToVertexFormat( int flags ) const +{ + // Flags -1 occurs when there's an error condition; + // we'll just give em the max space and let them fill it in. + int formatFlags = 0; + int texCoordSize[VERTEX_MAX_TEXTURE_COORDINATES] = { 0, 0, 0, 0, 0, 0, 0, 0 }; + int userDataSize = 0; + int numBones = 0; + + // Flags -1 occurs when there's an error condition; + // we'll just give em the max space and let them fill it in. + if (flags == -1) + { + formatFlags = VERTEX_POSITION | VERTEX_NORMAL | VERTEX_COLOR | + VERTEX_TANGENT_S | VERTEX_TANGENT_T; + texCoordSize[0] = texCoordSize[1] = texCoordSize[2] = 2; + } + else + { + if (flags & SHADER_DRAW_POSITION) + formatFlags |= VERTEX_POSITION; + + if (flags & SHADER_DRAW_NORMAL) + formatFlags |= VERTEX_NORMAL; + + if (flags & SHADER_DRAW_COLOR) + formatFlags |= VERTEX_COLOR; + + if( flags & SHADER_DRAW_SPECULAR ) + formatFlags |= VERTEX_SPECULAR; + + if (flags & SHADER_TEXCOORD_MASK) + { + // normal texture coords into texture 0 + texCoordSize[0] = 2; + } + + if (flags & SHADER_LIGHTMAP_TEXCOORD_MASK) + { + // lightmaps go into texcoord 1 + texCoordSize[1] = 2; + } + + if (flags & SHADER_SECONDARY_TEXCOORD_MASK) + { + // any texgen, or secondary texture coordinate is put into texcoord 2 + texCoordSize[2] = 2; + } + } + + // Hardware skinning... always store space for up to 3 bones + // and always assume index blend enabled if available + if (m_ShadowState.m_VertexBlendEnable) + { + if (HardwareConfig()->MaxBlendMatrixIndices() > 0) + formatFlags |= VERTEX_BONE_INDEX; + + if (HardwareConfig()->MaxBlendMatrices() > 2) + numBones = 2; // the third bone weight is implied + else + numBones = HardwareConfig()->MaxBlendMatrices() - 1; + } + + return MeshMgr()->ComputeVertexFormat( formatFlags, VERTEX_MAX_TEXTURE_COORDINATES, + texCoordSize, numBones, userDataSize ); +} + + +//----------------------------------------------------------------------------- +// Computes shadow state based on bunches of other parameters +//----------------------------------------------------------------------------- +void CShaderShadowDX8::ComputeAggregateShadowState( ) +{ + unsigned int flags = 0; + + // Initialize the texture stage usage; this may get changed later + for (int i = 0; i < m_pHardwareConfig->GetSamplerCount(); ++i) + { + m_ShadowState.m_SamplerState[i].m_TextureEnable = + IsUsingTextureCoordinates( (Sampler_t)i ); + + // Deal with the alpha pipe + if ( m_ShadowState.m_UsingFixedFunction && m_AlphaPipe ) + { + if ( m_TextureStage[i].m_TextureAlphaEnable ) + { + m_ShadowState.m_SamplerState[i].m_TextureEnable = true; + } + } + } + + // Always use the same alpha src + dest if it's disabled + // NOTE: This is essential for stateblocks to work + if ( m_ShadowState.m_AlphaBlendEnable ) + { + m_ShadowState.m_SrcBlend = m_SrcBlend; + m_ShadowState.m_DestBlend = m_DestBlend; + m_ShadowState.m_BlendOp = m_BlendOp; + } + else + { + m_ShadowState.m_SrcBlend = D3DBLEND_ONE; + m_ShadowState.m_DestBlend = D3DBLEND_ZERO; + m_ShadowState.m_BlendOp = D3DBLENDOP_ADD; + } + + // GR + if (m_ShadowState.m_SeparateAlphaBlendEnable) + { + m_ShadowState.m_SrcBlendAlpha = m_SrcBlendAlpha; + m_ShadowState.m_DestBlendAlpha = m_DestBlendAlpha; + m_ShadowState.m_BlendOpAlpha = m_BlendOpAlpha; + } + else + { + m_ShadowState.m_SrcBlendAlpha = D3DBLEND_ONE; + m_ShadowState.m_DestBlendAlpha = D3DBLEND_ZERO; + m_ShadowState.m_BlendOpAlpha = D3DBLENDOP_ADD; + } + + // Use the same func if it's disabled + if (m_ShadowState.m_AlphaTestEnable) + { + // If alpha test is enabled, just use the values set + m_ShadowState.m_AlphaFunc = m_AlphaFunc; + m_ShadowState.m_AlphaRef = m_AlphaRef; + } + else + { + // A default value + m_ShadowState.m_AlphaFunc = D3DCMP_GREATEREQUAL; + m_ShadowState.m_AlphaRef = 0; + + // If not alpha testing and doing a standard alpha blend, force on alpha testing + if ( m_ShadowState.m_AlphaBlendEnable ) + { + if ( ( m_ShadowState.m_SrcBlend == D3DBLEND_SRCALPHA ) && ( m_ShadowState.m_DestBlend == D3DBLEND_INVSRCALPHA ) ) + { + m_ShadowState.m_AlphaFunc = D3DCMP_GREATEREQUAL; + m_ShadowState.m_AlphaRef = 1; + } + } + } + if ( m_ShadowState.m_UsingFixedFunction ) + { + flags = m_DrawFlags; + + // We need to take this bad boy into account + if (HasConstantColor()) + flags |= SHADER_HAS_CONSTANT_COLOR; + + // We need to take lighting into account.. + if ( m_ShadowState.m_Lighting ) + flags |= SHADER_DRAW_NORMAL; + + if (m_ShadowState.m_Lighting) + flags |= SHADER_DRAW_COLOR; + + // Look for inconsistency in the shadow state (can't have texgen & + // SHADER_DRAW_TEXCOORD or SHADER_DRAW_SECONDARY_TEXCOORD0 on the same stage) + if (flags & (SHADER_DRAW_TEXCOORD0 | SHADER_DRAW_SECONDARY_TEXCOORD0)) + { + Assert( (m_ShadowState.m_TextureStage[0].m_TexCoordIndex & 0xFFFF0000) == 0 ); + } + if (flags & (SHADER_DRAW_TEXCOORD1 | SHADER_DRAW_SECONDARY_TEXCOORD1)) + { + Assert( (m_ShadowState.m_TextureStage[1].m_TexCoordIndex & 0xFFFF0000) == 0 ); + } + if (flags & (SHADER_DRAW_TEXCOORD2 | SHADER_DRAW_SECONDARY_TEXCOORD2)) + { + Assert( (m_ShadowState.m_TextureStage[2].m_TexCoordIndex & 0xFFFF0000) == 0 ); + } + if (flags & (SHADER_DRAW_TEXCOORD3 | SHADER_DRAW_SECONDARY_TEXCOORD3)) + { + Assert( (m_ShadowState.m_TextureStage[3].m_TexCoordIndex & 0xFFFF0000) == 0 ); + } + + // Vertex usage has already been set for pixel + vertex shaders + m_ShadowShaderState.m_VertexUsage = FlagsToVertexFormat( flags ); + + // Configure the texture stages + ConfigureFVFVertexShader(flags); + +#if 0 +//#ifdef _DEBUG + // NOTE: This must be true for stateblocks to work + for ( i = 0; i < m_pHardwareConfig->GetTextureStageCount(); ++i ) + { + if ( m_ShadowState.m_TextureStage[i].m_ColorOp == D3DTOP_DISABLE ) + { + Assert( m_ShadowState.m_TextureStage[i].m_ColorArg1 == D3DTA_TEXTURE ); + Assert( m_ShadowState.m_TextureStage[i].m_ColorArg2 == ((i == 0) ? D3DTA_DIFFUSE : D3DTA_CURRENT) ); + } + + if ( m_ShadowState.m_TextureStage[i].m_AlphaOp == D3DTOP_DISABLE ) + { + Assert( m_ShadowState.m_TextureStage[i].m_AlphaArg1 == D3DTA_TEXTURE ); + Assert( m_ShadowState.m_TextureStage[i].m_AlphaArg2 == ((i == 0) ? D3DTA_DIFFUSE : D3DTA_CURRENT) ); + } + } +#endif + } + else + { + // Pixel shaders, disable everything so as to prevent unnecessary state changes.... + for ( int i = 0; i < m_pHardwareConfig->GetTextureStageCount(); ++i ) + { + m_ShadowState.m_TextureStage[i].m_ColorArg1 = D3DTA_TEXTURE; + m_ShadowState.m_TextureStage[i].m_ColorArg2 = (i == 0) ? D3DTA_DIFFUSE : D3DTA_CURRENT; + m_ShadowState.m_TextureStage[i].m_AlphaArg1 = D3DTA_TEXTURE; + m_ShadowState.m_TextureStage[i].m_AlphaArg2 = (i == 0) ? D3DTA_DIFFUSE : D3DTA_CURRENT; + m_ShadowState.m_TextureStage[i].m_ColorOp = D3DTOP_DISABLE; + m_ShadowState.m_TextureStage[i].m_AlphaOp = D3DTOP_DISABLE; + m_ShadowState.m_TextureStage[i].m_TexCoordIndex = i; + } + m_ShadowState.m_Lighting = false; + m_ShadowState.m_SpecularEnable = false; + m_ShadowState.m_VertexBlendEnable = false; + m_ShadowShaderState.m_ModulateConstantColor = false; + } + + // Compute texture coordinates + ConfigureTextureCoordinates(flags); + + // Alpha to coverage + if ( m_ShadowState.m_EnableAlphaToCoverage ) + { + // Only allow this to be enabled if blending is disabled and testing is enabled + if ( ( m_ShadowState.m_AlphaBlendEnable == true ) || ( m_ShadowState.m_AlphaTestEnable == false ) ) + { + m_ShadowState.m_EnableAlphaToCoverage = false; + } + } +} + +void CShaderShadowDX8::FogMode( ShaderFogMode_t fogMode ) +{ + Assert( fogMode >= 0 && fogMode < SHADER_FOGMODE_NUMFOGMODES ); + m_ShadowState.m_FogMode = fogMode; +} + +void CShaderShadowDX8::DisableFogGammaCorrection( bool bDisable ) +{ + m_ShadowState.m_bDisableFogGammaCorrection = bDisable; +} + +void CShaderShadowDX8::SetDiffuseMaterialSource( ShaderMaterialSource_t materialSource ) +{ + COMPILE_TIME_ASSERT( ( int )D3DMCS_MATERIAL == ( int )SHADER_MATERIALSOURCE_MATERIAL ); + COMPILE_TIME_ASSERT( ( int )D3DMCS_COLOR1 == ( int )SHADER_MATERIALSOURCE_COLOR1 ); + COMPILE_TIME_ASSERT( ( int )D3DMCS_COLOR2 == ( int )SHADER_MATERIALSOURCE_COLOR2 ); + Assert( materialSource == SHADER_MATERIALSOURCE_MATERIAL || + materialSource == SHADER_MATERIALSOURCE_COLOR1 || + materialSource == SHADER_MATERIALSOURCE_COLOR2 ); + m_ShadowState.m_DiffuseMaterialSource = ( D3DMATERIALCOLORSOURCE )materialSource; +} diff --git a/materialsystem/shaderapidx9/shadershadowdx8.h b/materialsystem/shaderapidx9/shadershadowdx8.h new file mode 100644 index 0000000..09b16ae --- /dev/null +++ b/materialsystem/shaderapidx9/shadershadowdx8.h @@ -0,0 +1,176 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//===========================================================================// + +#ifndef SHADERSHADOWDX8_H +#define SHADERSHADOWDX8_H + +#ifdef _WIN32 +#pragma once +#endif + +#include "togl/rendermechanism.h" +#include "locald3dtypes.h" +#include "shaderapi/ishadershadow.h" + +class IShaderAPIDX8; + + +//----------------------------------------------------------------------------- +// Important enumerations +//----------------------------------------------------------------------------- +enum +{ + MAX_SAMPLERS = 16, + MAX_TEXTURE_STAGES = 16, +}; + + +//----------------------------------------------------------------------------- +// A structure maintaining the shadowed board state +//----------------------------------------------------------------------------- +struct TextureStageShadowState_t +{ + // State shadowing affects these + D3DTEXTUREOP m_ColorOp; + int m_ColorArg1; + int m_ColorArg2; + D3DTEXTUREOP m_AlphaOp; + int m_AlphaArg1; + int m_AlphaArg2; + int m_TexCoordIndex; +}; + +struct SamplerShadowState_t +{ + bool m_TextureEnable : 1; + bool m_SRGBReadEnable : 1; + bool m_Fetch4Enable : 1; + bool m_ShadowFilterEnable : 1; +}; + +struct ShadowState_t +{ + // Depth buffering state + D3DCMPFUNC m_ZFunc; + D3DZBUFFERTYPE m_ZEnable; + + // Write enable + DWORD m_ColorWriteEnable; + + // Fill mode + D3DFILLMODE m_FillMode; + + // Alpha state + D3DBLEND m_SrcBlend; + D3DBLEND m_DestBlend; + D3DBLENDOP m_BlendOp; + + // Separate alpha blend state + D3DBLEND m_SrcBlendAlpha; + D3DBLEND m_DestBlendAlpha; + D3DBLENDOP m_BlendOpAlpha; + + D3DCMPFUNC m_AlphaFunc; + int m_AlphaRef; + + // Texture stage state + TextureStageShadowState_t m_TextureStage[MAX_TEXTURE_STAGES]; + + // Sampler state + SamplerShadowState_t m_SamplerState[MAX_SAMPLERS]; + + ShaderFogMode_t m_FogMode; + + D3DMATERIALCOLORSOURCE m_DiffuseMaterialSource; + + unsigned char m_ZWriteEnable:1; + unsigned char m_ZBias:2; + // Cull State? + unsigned char m_CullEnable:1; + // Lighting in hardware? + unsigned char m_Lighting:1; + unsigned char m_SpecularEnable:1; + unsigned char m_AlphaBlendEnable:1; + unsigned char m_AlphaTestEnable:1; + + // Fixed function? + unsigned char m_UsingFixedFunction:1; + // Vertex blending? + unsigned char m_VertexBlendEnable:1; + // Auto-convert from linear to gamma upon writing to the frame buffer? + unsigned char m_SRGBWriteEnable:1; + // Seperate Alpha Blend? + unsigned char m_SeparateAlphaBlendEnable:1; + // Stencil? + unsigned char m_StencilEnable:1; + + unsigned char m_bDisableFogGammaCorrection:1; + + unsigned char m_EnableAlphaToCoverage:1; + + unsigned char m_Reserved : 1; + unsigned short m_nReserved2; +}; + + +//----------------------------------------------------------------------------- +// These are part of the "shadow" since they describe the shading algorithm +// but aren't actually captured in the state transition table +// because it would produce too many transitions +//----------------------------------------------------------------------------- +struct ShadowShaderState_t +{ + // The vertex + pixel shader group to use... + VertexShader_t m_VertexShader; + PixelShader_t m_PixelShader; + + // The static vertex + pixel shader indices + int m_nStaticVshIndex; + int m_nStaticPshIndex; + + // Vertex data used by this snapshot + // Note that the vertex format actually used will be the + // aggregate of the vertex formats used by all snapshots in a material + VertexFormat_t m_VertexUsage; + + // Morph data used by this snapshot + // Note that the morph format actually used will be the + // aggregate of the morph formats used by all snapshots in a material + MorphFormat_t m_MorphUsage; + + // Modulate constant color into the vertex color + bool m_ModulateConstantColor; + + bool m_nReserved[3]; +}; + + +//----------------------------------------------------------------------------- +// The shader setup API +//----------------------------------------------------------------------------- +abstract_class IShaderShadowDX8 : public IShaderShadow +{ +public: + // Initializes it + virtual void Init() = 0; + + // Gets at the shadow state + virtual ShadowState_t const& GetShadowState() = 0; + virtual ShadowShaderState_t const& GetShadowShaderState() = 0; + + // This must be called right before taking a snapshot + virtual void ComputeAggregateShadowState( ) = 0; + + // Class factory methods + static IShaderShadowDX8* Create( IShaderAPIDX8* pShaderAPIDX8 ); + static void Destroy( IShaderShadowDX8* pShaderShadow ); +}; + +extern IShaderShadowDX8 *g_pShaderShadowDx8; + +#endif // SHADERSHADOWDX8_H diff --git a/materialsystem/shaderapidx9/stubd3ddevice.h b/materialsystem/shaderapidx9/stubd3ddevice.h new file mode 100644 index 0000000..a97f015 --- /dev/null +++ b/materialsystem/shaderapidx9/stubd3ddevice.h @@ -0,0 +1,809 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef STUBD3DDEVICE_H +#define STUBD3DDEVICE_H +#ifdef _WIN32 +#pragma once +#endif + +#ifdef STUBD3D + +#include "locald3dtypes.h" +#include "filesystem.h" + +#ifdef USE_FOPEN +#include +#define FPRINTF fprintf +#else +#define FPRINTF s_pFileSystem->FPrintf +#endif + +#ifdef USE_FOPEN + +static FILE *s_FileHandle; + +#else + +static IFileSystem *s_pFileSystem; +static FileHandle_t s_FileHandle; + +#endif + + + +class CStubD3DTexture : public IDirect3DTexture8 +{ +private: + IDirect3DTexture8 *m_pTexture; + IDirect3DDevice8 *m_pDevice; + +public: + CStubD3DTexture( IDirect3DTexture8 *pTexture, IDirect3DDevice8 *pDevice ) + { + m_pTexture = pTexture; + m_pDevice = pDevice; + } + + /*** IUnknown methods ***/ + HRESULT __stdcall QueryInterface(REFIID riid, void** ppvObj) + { + FPRINTF( s_FileHandle, "IDirect3DTexture8::QueryInterface\n" ); + return m_pTexture->QueryInterface( riid, ppvObj ); + } + + ULONG __stdcall AddRef() + { + FPRINTF( s_FileHandle, "IDirect3DTexture8::AddRef\n" ); + return m_pTexture->AddRef(); + } + + ULONG __stdcall Release() + { + FPRINTF( s_FileHandle, "IDirect3DTexture8::Release\n" ); + return m_pTexture->Release(); + } + + /*** IDirect3DBaseTexture8 methods ***/ + HRESULT __stdcall GetDevice( IDirect3DDevice8** ppDevice ) + { + FPRINTF( s_FileHandle, "IDirect3DTexture8::GetDevice\n" ); +#if 0 + *ppDevice = m_pDevice; + return D3D_OK; +#else + return m_pTexture->GetDevice( ppDevice ); +#endif + } + + HRESULT __stdcall SetPrivateData( REFGUID refguid,CONST void* pData,DWORD SizeOfData,DWORD Flags) + { + FPRINTF( s_FileHandle, "IDirect3DTexture8::SetPrivateData\n" ); + return m_pTexture->SetPrivateData( refguid, pData, SizeOfData, Flags ); + } + + HRESULT __stdcall GetPrivateData( REFGUID refguid,void* pData,DWORD* pSizeOfData ) + { + FPRINTF( s_FileHandle, "IDirect3DTexture8::GetPrivateData\n" ); + return m_pTexture->GetPrivateData( refguid, pData, pSizeOfData ); + } + + HRESULT __stdcall FreePrivateData( REFGUID refguid ) + { + FPRINTF( s_FileHandle, "IDirect3DTexture8::GetPrivateData\n" ); + return m_pTexture->FreePrivateData( refguid ); + } + + DWORD __stdcall SetPriority( DWORD PriorityNew ) + { + FPRINTF( s_FileHandle, "IDirect3DTexture8::SetPriority\n" ); + return m_pTexture->SetPriority( PriorityNew ); + } + + DWORD __stdcall GetPriority() + { + FPRINTF( s_FileHandle, "IDirect3DTexture8::GetPriority\n" ); + return m_pTexture->GetPriority(); + } + + void __stdcall PreLoad() + { + FPRINTF( s_FileHandle, "IDirect3DTexture8::PreLoad\n" ); + m_pTexture->PreLoad(); + } + + D3DRESOURCETYPE __stdcall GetType() + { + FPRINTF( s_FileHandle, "IDirect3DTexture8::GetType\n" ); + return m_pTexture->GetType(); + } + + DWORD __stdcall SetLOD( DWORD LODNew ) + { + FPRINTF( s_FileHandle, "IDirect3DTexture8::SetLOD\n" ); + return m_pTexture->SetLOD( LODNew ); + } + + DWORD __stdcall GetLOD() + { + FPRINTF( s_FileHandle, "IDirect3DTexture8::GetLOD\n" ); + return m_pTexture->GetLOD(); + } + + DWORD __stdcall GetLevelCount() + { + FPRINTF( s_FileHandle, "IDirect3DTexture8::GetLevelCount\n" ); + return m_pTexture->GetLevelCount(); + } + + HRESULT __stdcall GetLevelDesc(UINT Level,D3DSURFACE_DESC *pDesc) + { + FPRINTF( s_FileHandle, "IDirect3DTexture8::GetLevelCount\n" ); + return m_pTexture->GetLevelDesc( Level, pDesc ); + } + + HRESULT __stdcall GetSurfaceLevel(UINT Level,IDirect3DSurface8** ppSurfaceLevel) + { + FPRINTF( s_FileHandle, "IDirect3DTexture8::GetSurfaceLevel\n" ); + return m_pTexture->GetSurfaceLevel( Level, ppSurfaceLevel ); + } + + HRESULT __stdcall LockRect(UINT Level,D3DLOCKED_RECT* pLockedRect,CONST RECT* pRect,DWORD Flags) + { + FPRINTF( s_FileHandle, "IDirect3DTexture8::LockRect\n" ); + return m_pTexture->LockRect( Level, pLockedRect, pRect, Flags ); + } + + HRESULT __stdcall UnlockRect(UINT Level) + { + FPRINTF( s_FileHandle, "IDirect3DTexture8::UnlockRect\n" ); + return m_pTexture->UnlockRect( Level ); + } + + HRESULT __stdcall AddDirtyRect( CONST RECT* pDirtyRect ) + { + FPRINTF( s_FileHandle, "IDirect3DTexture8::AddDirtyRect\n" ); + return m_pTexture->AddDirtyRect( pDirtyRect ); + } +}; + +class CStubD3DDevice : public IDirect3DDevice8 +{ +public: + CStubD3DDevice( IDirect3DDevice8 *pD3DDevice, IFileSystem *pFileSystem ) + { + Assert( pD3DDevice ); + m_pD3DDevice = pD3DDevice; +#ifdef USE_FOPEN + s_FileHandle = fopen( "stubd3d.txt", "w" ); +#else + Assert( pFileSystem ); + s_pFileSystem = pFileSystem; + s_FileHandle = pFileSystem->Open( "stubd3d.txt", "w" ); +#endif + } + + ~CStubD3DDevice() + { +#ifdef USE_FOPEN + fclose( s_FileHandle ); +#else + s_pFileSystem->Close( s_FileHandle ); +#endif + } + +private: + IDirect3DDevice8 *m_pD3DDevice; + +public: + /*** IUnknown methods ***/ + HRESULT __stdcall QueryInterface(REFIID riid, void** ppvObj) + { + FPRINTF( s_FileHandle, "QueryInterface\n" ); + return m_pD3DDevice->QueryInterface( riid, ppvObj ); + } + + ULONG __stdcall AddRef() + { + FPRINTF( s_FileHandle, "AddRef\n" ); + return m_pD3DDevice->AddRef(); + } + + ULONG __stdcall Release() + { + FPRINTF( s_FileHandle, "Release\n" ); + return m_pD3DDevice->Release(); + delete this; + } + + /*** IDirect3DDevice8 methods ***/ + HRESULT __stdcall TestCooperativeLevel() + { + FPRINTF( s_FileHandle, "TestCooperativeLevel\n" ); + return m_pD3DDevice->TestCooperativeLevel(); + } + + UINT __stdcall GetAvailableTextureMem() + { + FPRINTF( s_FileHandle, "GetAvailableTextureMem\n" ); + return m_pD3DDevice->GetAvailableTextureMem(); + } + + HRESULT __stdcall ResourceManagerDiscardBytes(DWORD Bytes) + { + FPRINTF( s_FileHandle, "ResourceManagerDiscardBytes\n" ); + return m_pD3DDevice->ResourceManagerDiscardBytes( Bytes ); + } + + HRESULT __stdcall GetDirect3D(IDirect3D8** ppD3D8) + { + FPRINTF( s_FileHandle, "GetDirect3D\n" ); + return m_pD3DDevice->GetDirect3D( ppD3D8 ); + } + + HRESULT __stdcall GetDeviceCaps(D3DCAPS8* pCaps) + { + FPRINTF( s_FileHandle, "GetDeviceCaps\n" ); + return m_pD3DDevice->GetDeviceCaps( pCaps ); + } + + HRESULT __stdcall GetDisplayMode(D3DDISPLAYMODE* pMode) + { + FPRINTF( s_FileHandle, "GetDisplayMode\n" ); + return m_pD3DDevice->GetDisplayMode( pMode ); + } + + HRESULT __stdcall GetCreationParameters(D3DDEVICE_CREATION_PARAMETERS *pParameters) + { + FPRINTF( s_FileHandle, "GetCreationParameters\n" ); + return m_pD3DDevice->GetCreationParameters( pParameters ); + } + + HRESULT __stdcall SetCursorProperties(UINT XHotSpot,UINT YHotSpot,IDirect3DSurface8* pCursorBitmap) + { + FPRINTF( s_FileHandle, "SetCursorProperties\n" ); + return m_pD3DDevice->SetCursorProperties( XHotSpot, YHotSpot, pCursorBitmap ); + } + + void __stdcall SetCursorPosition(UINT XScreenSpace,UINT YScreenSpace,DWORD Flags) + { + FPRINTF( s_FileHandle, "SetCursorPosition\n" ); + m_pD3DDevice->SetCursorPosition( XScreenSpace, YScreenSpace, Flags ); + } + + BOOL __stdcall ShowCursor(BOOL bShow) + { + FPRINTF( s_FileHandle, "ShowCursor\n" ); + return m_pD3DDevice->ShowCursor( bShow ); + } + + HRESULT __stdcall CreateAdditionalSwapChain(D3DPRESENT_PARAMETERS* pPresentationParameters,IDirect3DSwapChain8** pSwapChain) + { + FPRINTF( s_FileHandle, "CreateAdditionalSwapChain\n" ); + return m_pD3DDevice->CreateAdditionalSwapChain( pPresentationParameters, pSwapChain ); + } + + HRESULT __stdcall Reset(D3DPRESENT_PARAMETERS* pPresentationParameters) + { + FPRINTF( s_FileHandle, "Reset\n" ); + return m_pD3DDevice->Reset( pPresentationParameters ); + } + + HRESULT __stdcall Present(CONST RECT* pSourceRect,CONST RECT* pDestRect,HWND hDestWindowOverride,CONST RGNDATA* pDirtyRegion) + { + FPRINTF( s_FileHandle, "Present\n" ); + return m_pD3DDevice->Present( pSourceRect, pDestRect, hDestWindowOverride, pDirtyRegion ); + } + + HRESULT __stdcall GetBackBuffer(UINT BackBuffer,D3DBACKBUFFER_TYPE Type,IDirect3DSurface8** ppBackBuffer) + { + FPRINTF( s_FileHandle, "GetBackBuffer\n" ); + return m_pD3DDevice->GetBackBuffer( BackBuffer, Type, ppBackBuffer ); + } + + HRESULT __stdcall GetRasterStatus(D3DRASTER_STATUS* pRasterStatus) + { + FPRINTF( s_FileHandle, "GetRasterStatus\n" ); + return m_pD3DDevice->GetRasterStatus( pRasterStatus ); + } + + void __stdcall SetGammaRamp(DWORD Flags,CONST D3DGAMMARAMP* pRamp) + { + FPRINTF( s_FileHandle, "SetGammaRamp\n" ); + m_pD3DDevice->SetGammaRamp( Flags, pRamp ); + } + + void __stdcall GetGammaRamp(D3DGAMMARAMP* pRamp) + { + FPRINTF( s_FileHandle, "GetGammaRamp\n" ); + m_pD3DDevice->GetGammaRamp( pRamp ); + } + + HRESULT __stdcall CreateTexture(UINT Width,UINT Height,UINT Levels,DWORD Usage,D3DFORMAT Format,D3DPOOL Pool,IDirect3DTexture8** ppTexture) + { + FPRINTF( s_FileHandle, "CreateTexture\n" ); +#if 0 + HRESULT ret = m_pD3DDevice->CreateTexture( Width, Height, Levels, Usage, Format, Pool, ppTexture ); + if( ret == D3D_OK ) + { + *ppTexture = new CStubD3DTexture( *ppTexture, this ); + return ret; + } + else + { + return ret; + } +#else + return m_pD3DDevice->CreateTexture( Width, Height, Levels, Usage, Format, Pool, ppTexture ); +#endif + } + + HRESULT __stdcall CreateVolumeTexture(UINT Width,UINT Height,UINT Depth,UINT Levels,DWORD Usage,D3DFORMAT Format,D3DPOOL Pool,IDirect3DVolumeTexture8** ppVolumeTexture) + { + FPRINTF( s_FileHandle, "CreateVolumeTexture\n" ); + return m_pD3DDevice->CreateVolumeTexture( Width, Height, Depth, Levels, Usage, Format, Pool, ppVolumeTexture ); + } + + HRESULT __stdcall CreateCubeTexture(UINT EdgeLength,UINT Levels,DWORD Usage,D3DFORMAT Format,D3DPOOL Pool,IDirect3DCubeTexture8** ppCubeTexture) + { + FPRINTF( s_FileHandle, "CreateCubeTexture\n" ); + return m_pD3DDevice->CreateCubeTexture( EdgeLength, Levels, Usage, Format, Pool, ppCubeTexture ); + } + + HRESULT __stdcall CreateVertexBuffer(UINT Length,DWORD Usage,DWORD FVF,D3DPOOL Pool,IDirect3DVertexBuffer8** ppVertexBuffer) + { + FPRINTF( s_FileHandle, "CreateVertexBuffer\n" ); + return m_pD3DDevice->CreateVertexBuffer( Length, Usage, FVF, Pool, ppVertexBuffer ); + } + + HRESULT __stdcall CreateIndexBuffer(UINT Length,DWORD Usage,D3DFORMAT Format,D3DPOOL Pool,IDirect3DIndexBuffer8** ppIndexBuffer) + { + FPRINTF( s_FileHandle, "CreateIndexBuffer\n" ); + return m_pD3DDevice->CreateIndexBuffer( Length, Usage, Format, Pool, ppIndexBuffer ); + } + + HRESULT __stdcall CreateRenderTarget(UINT Width,UINT Height,D3DFORMAT Format,D3DMULTISAMPLE_TYPE MultiSample,BOOL Lockable,IDirect3DSurface8** ppSurface) + { + FPRINTF( s_FileHandle, "CreateRenderTarget\n" ); + return m_pD3DDevice->CreateRenderTarget( Width, Height, Format, MultiSample, Lockable, ppSurface ); + } + + HRESULT __stdcall CreateDepthStencilSurface(UINT Width,UINT Height,D3DFORMAT Format,D3DMULTISAMPLE_TYPE MultiSample,IDirect3DSurface8** ppSurface) + { + FPRINTF( s_FileHandle, "CreateDepthStencilSurface\n" ); + return m_pD3DDevice->CreateDepthStencilSurface( Width, Height, Format, MultiSample, ppSurface ); + } + + HRESULT __stdcall CreateImageSurface(UINT Width,UINT Height,D3DFORMAT Format,IDirect3DSurface8** ppSurface) + { + FPRINTF( s_FileHandle, "CreateImageSurface\n" ); + return m_pD3DDevice->CreateImageSurface( Width, Height, Format, ppSurface ); + } + + HRESULT __stdcall CopyRects(IDirect3DSurface8* pSourceSurface,CONST RECT* pSourceRectsArray,UINT cRects,IDirect3DSurface8* pDestinationSurface,CONST POINT* pDestPointsArray) + { + FPRINTF( s_FileHandle, "CopyRects\n" ); + return m_pD3DDevice->CopyRects( pSourceSurface, pSourceRectsArray, cRects, pDestinationSurface, pDestPointsArray ); + } + + HRESULT __stdcall UpdateTexture(IDirect3DBaseTexture8* pSourceTexture,IDirect3DBaseTexture8* pDestinationTexture) + { + FPRINTF( s_FileHandle, "UpdateTexture\n" ); + return m_pD3DDevice->UpdateTexture( pSourceTexture, pDestinationTexture ); + } + + HRESULT __stdcall GetFrontBuffer(IDirect3DSurface8* pDestSurface) + { + FPRINTF( s_FileHandle, "GetFrontBuffer\n" ); + return m_pD3DDevice->GetFrontBuffer( pDestSurface ); + } + + HRESULT __stdcall SetRenderTarget(IDirect3DSurface8* pRenderTarget,IDirect3DSurface8* pNewZStencil) + { + FPRINTF( s_FileHandle, "SetRenderTarget\n" ); + return m_pD3DDevice->SetRenderTarget( pRenderTarget, pNewZStencil ); + } + + HRESULT __stdcall GetRenderTarget(IDirect3DSurface8** ppRenderTarget) + { + FPRINTF( s_FileHandle, "GetRenderTarget\n" ); + return m_pD3DDevice->GetRenderTarget( ppRenderTarget ); + } + + HRESULT __stdcall GetDepthStencilSurface(IDirect3DSurface8** ppZStencilSurface) + { + FPRINTF( s_FileHandle, "GetDepthStencilSurface\n" ); + return m_pD3DDevice->GetDepthStencilSurface( ppZStencilSurface ); + } + + HRESULT __stdcall BeginScene( void ) + { + FPRINTF( s_FileHandle, "BeginScene\n" ); + return m_pD3DDevice->BeginScene(); + } + + HRESULT __stdcall EndScene() + { + FPRINTF( s_FileHandle, "EndScene\n" ); + return m_pD3DDevice->EndScene(); + } + + HRESULT __stdcall Clear(DWORD Count,CONST D3DRECT* pRects,DWORD Flags,D3DCOLOR Color,float Z,DWORD Stencil) + { + FPRINTF( s_FileHandle, "Clear\n" ); + return m_pD3DDevice->Clear( Count, pRects, Flags, Color, Z, Stencil ); + } + + HRESULT __stdcall SetTransform(D3DTRANSFORMSTATETYPE State,CONST D3DMATRIX* pMatrix) + { + FPRINTF( s_FileHandle, "SetTransform\n" ); + return m_pD3DDevice->SetTransform( State, pMatrix ); + } + + HRESULT __stdcall GetTransform(D3DTRANSFORMSTATETYPE State,D3DMATRIX* pMatrix) + { + FPRINTF( s_FileHandle, "GetTransform\n" ); + return m_pD3DDevice->GetTransform( State, pMatrix ); + } + + HRESULT __stdcall MultiplyTransform(D3DTRANSFORMSTATETYPE transformState,CONST D3DMATRIX* pMatrix) + { + FPRINTF( s_FileHandle, "MultiplyTransform\n" ); + return m_pD3DDevice->MultiplyTransform( transformState, pMatrix ); + } + + HRESULT __stdcall SetViewport(CONST D3DVIEWPORT8* pViewport) + { + FPRINTF( s_FileHandle, "SetViewport\n" ); + return m_pD3DDevice->SetViewport( pViewport ); + } + + HRESULT __stdcall GetViewport(D3DVIEWPORT8* pViewport) + { + FPRINTF( s_FileHandle, "GetViewport\n" ); + return m_pD3DDevice->GetViewport( pViewport ); + } + + HRESULT __stdcall SetMaterial(CONST D3DMATERIAL8* pMaterial) + { + FPRINTF( s_FileHandle, "SetMaterial\n" ); + return m_pD3DDevice->SetMaterial( pMaterial ); + } + + HRESULT __stdcall GetMaterial(D3DMATERIAL8* pMaterial) + { + FPRINTF( s_FileHandle, "GetMaterial\n" ); + return m_pD3DDevice->GetMaterial( pMaterial ); + } + + HRESULT __stdcall SetLight(DWORD Index,CONST D3DLIGHT8* pLight) + { + FPRINTF( s_FileHandle, "SetLight\n" ); + return m_pD3DDevice->SetLight( Index, pLight ); + } + + HRESULT __stdcall GetLight(DWORD Index,D3DLIGHT8* pLight) + { + FPRINTF( s_FileHandle, "GetLight\n" ); + return m_pD3DDevice->GetLight( Index, pLight ); + } + + HRESULT __stdcall LightEnable(DWORD Index,BOOL Enable) + { + FPRINTF( s_FileHandle, "LightEnable\n" ); + return m_pD3DDevice->LightEnable( Index, Enable ); + } + + HRESULT __stdcall GetLightEnable(DWORD Index,BOOL* pEnable) + { + FPRINTF( s_FileHandle, "GetLightEnable\n" ); + return m_pD3DDevice->GetLightEnable( Index, pEnable ); + } + + HRESULT __stdcall SetClipPlane(DWORD Index,CONST float* pPlane) + { + FPRINTF( s_FileHandle, "SetClipPlane\n" ); + return m_pD3DDevice->SetClipPlane( Index, pPlane ); + } + + HRESULT __stdcall GetClipPlane(DWORD Index,float* pPlane) + { + FPRINTF( s_FileHandle, "GetClipPlane\n" ); + return m_pD3DDevice->GetClipPlane( Index, pPlane ); + } + + HRESULT __stdcall SetRenderState(D3DRENDERSTATETYPE State,DWORD Value) + { + FPRINTF( s_FileHandle, "SetRenderState\n" ); + return m_pD3DDevice->SetRenderState( State, Value ); + } + + HRESULT __stdcall GetRenderState(D3DRENDERSTATETYPE State,DWORD* pValue) + { + FPRINTF( s_FileHandle, "GetRenderState\n" ); + return m_pD3DDevice->GetRenderState( State, pValue ); + } + + HRESULT __stdcall BeginStateBlock(void) + { + FPRINTF( s_FileHandle, "BeginStateBlock\n" ); + return m_pD3DDevice->BeginStateBlock(); + } + + HRESULT __stdcall EndStateBlock(DWORD* pToken) + { + FPRINTF( s_FileHandle, "EndStateBlock\n" ); + return m_pD3DDevice->EndStateBlock( pToken ); + } + + HRESULT __stdcall ApplyStateBlock(DWORD Token) + { + FPRINTF( s_FileHandle, "ApplyStateBlock\n" ); + return m_pD3DDevice->ApplyStateBlock( Token ); + } + + HRESULT __stdcall CaptureStateBlock(DWORD Token) + { + FPRINTF( s_FileHandle, "CaptureStateBlock\n" ); + return m_pD3DDevice->CaptureStateBlock( Token ); + } + + HRESULT __stdcall DeleteStateBlock(DWORD Token) + { + FPRINTF( s_FileHandle, "DeleteStateBlock\n" ); + return m_pD3DDevice->DeleteStateBlock( Token ); + } + + HRESULT __stdcall CreateStateBlock(D3DSTATEBLOCKTYPE Type,DWORD* pToken) + { + FPRINTF( s_FileHandle, "CreateStateBlock\n" ); + return m_pD3DDevice->CreateStateBlock( Type, pToken ); + } + + HRESULT __stdcall SetClipStatus(CONST D3DCLIPSTATUS8* pClipStatus) + { + FPRINTF( s_FileHandle, "SetClipStatus\n" ); + return m_pD3DDevice->SetClipStatus( pClipStatus ); + } + + HRESULT __stdcall GetClipStatus(D3DCLIPSTATUS8* pClipStatus) + { + FPRINTF( s_FileHandle, "GetClipStatus\n" ); + return m_pD3DDevice->GetClipStatus( pClipStatus ); + } + + HRESULT __stdcall GetTexture(DWORD Stage,IDirect3DBaseTexture8** ppTexture) + { + FPRINTF( s_FileHandle, "GetTexture\n" ); + return m_pD3DDevice->GetTexture( Stage, ppTexture ); + } + + HRESULT __stdcall SetTexture(DWORD Stage,IDirect3DBaseTexture8* pTexture) + { + FPRINTF( s_FileHandle, "SetTexture\n" ); + return m_pD3DDevice->SetTexture( Stage, pTexture ); + } + + HRESULT __stdcall GetTextureStageState(DWORD Stage,D3DTEXTURESTAGESTATETYPE Type,DWORD* pValue) + { + FPRINTF( s_FileHandle, "GetTextureStageState\n" ); + return m_pD3DDevice->GetTextureStageState( Stage, Type, pValue ); + } + + HRESULT __stdcall SetTextureStageState(DWORD Stage,D3DTEXTURESTAGESTATETYPE Type,DWORD Value) + { + FPRINTF( s_FileHandle, "SetTextureStageState\n" ); + return m_pD3DDevice->SetTextureStageState( Stage, Type, Value ); + } + + HRESULT __stdcall ValidateDevice(DWORD* pNumPasses) + { + FPRINTF( s_FileHandle, "ValidateDevice\n" ); +#if 0 + return m_pD3DDevice->ValidateDevice( pNumPasses ); +#else + return D3D_OK; +#endif + } + + HRESULT __stdcall GetInfo(DWORD DevInfoID,void* pDevInfoStruct,DWORD DevInfoStructSize) + { + FPRINTF( s_FileHandle, "GetInfo\n" ); + return m_pD3DDevice->GetInfo( DevInfoID, pDevInfoStruct, DevInfoStructSize ); + } + + HRESULT __stdcall SetPaletteEntries(UINT PaletteNumber,CONST PALETTEENTRY* pEntries) + { + FPRINTF( s_FileHandle, "SetPaletteEntries\n" ); + return m_pD3DDevice->SetPaletteEntries( PaletteNumber, pEntries ); + } + + HRESULT __stdcall GetPaletteEntries(UINT PaletteNumber,PALETTEENTRY* pEntries) + { + FPRINTF( s_FileHandle, "GetPaletteEntries\n" ); + return m_pD3DDevice->GetPaletteEntries( PaletteNumber, pEntries ); + } + + HRESULT __stdcall SetCurrentTexturePalette(UINT PaletteNumber) + { + FPRINTF( s_FileHandle, "SetCurrentTexturePalette\n" ); + return m_pD3DDevice->SetCurrentTexturePalette( PaletteNumber ); + } + + HRESULT __stdcall GetCurrentTexturePalette(UINT *PaletteNumber) + { + FPRINTF( s_FileHandle, "GetCurrentTexturePalette\n" ); + return m_pD3DDevice->GetCurrentTexturePalette( PaletteNumber ); + } + + HRESULT __stdcall DrawPrimitive(D3DPRIMITIVETYPE PrimitiveType,UINT StartVertex,UINT PrimitiveCount) + { + FPRINTF( s_FileHandle, "DrawPrimitive\n" ); + return m_pD3DDevice->DrawPrimitive( PrimitiveType, StartVertex, PrimitiveCount ); + } + + HRESULT __stdcall DrawIndexedPrimitive(D3DPRIMITIVETYPE primitiveType,UINT minIndex,UINT NumVertices,UINT startIndex,UINT primCount) + { + FPRINTF( s_FileHandle, "DrawIndexedPrimitive\n" ); + return m_pD3DDevice->DrawIndexedPrimitive( primitiveType,minIndex,NumVertices,startIndex,primCount ); + } + + HRESULT __stdcall DrawPrimitiveUP(D3DPRIMITIVETYPE PrimitiveType,UINT PrimitiveCount,CONST void* pVertexStreamZeroData,UINT VertexStreamZeroStride) + { + FPRINTF( s_FileHandle, "DrawPrimitiveUP\n" ); + return m_pD3DDevice->DrawPrimitiveUP( PrimitiveType, PrimitiveCount, pVertexStreamZeroData, VertexStreamZeroStride ); + } + + HRESULT __stdcall DrawIndexedPrimitiveUP(D3DPRIMITIVETYPE PrimitiveType,UINT MinVertexIndex,UINT NumVertexIndices,UINT PrimitiveCount,CONST void* pIndexData,D3DFORMAT IndexDataFormat,CONST void* pVertexStreamZeroData,UINT VertexStreamZeroStride) + { + FPRINTF( s_FileHandle, "DrawIndexedPrimitiveUP\n" ); + return m_pD3DDevice->DrawIndexedPrimitiveUP( PrimitiveType, MinVertexIndex, NumVertexIndices, PrimitiveCount, pIndexData, IndexDataFormat,pVertexStreamZeroData, VertexStreamZeroStride ); + } + + HRESULT __stdcall ProcessVertices(UINT SrcStartIndex,UINT DestIndex,UINT VertexCount,IDirect3DVertexBuffer8* pDestBuffer,DWORD Flags) + { + FPRINTF( s_FileHandle, "ProcessVertices\n" ); + return m_pD3DDevice->ProcessVertices( SrcStartIndex, DestIndex, VertexCount, pDestBuffer, Flags ); + } + + HRESULT __stdcall CreateVertexShader(CONST DWORD* pDeclaration,CONST DWORD* pFunction,DWORD* pHandle,DWORD Usage) + { + FPRINTF( s_FileHandle, "CreateVertexShader\n" ); + return m_pD3DDevice->CreateVertexShader( pDeclaration, pFunction, pHandle, Usage ); + } + + HRESULT __stdcall SetVertexShader(DWORD Handle) + { + FPRINTF( s_FileHandle, "SetVertexShader\n" ); + return m_pD3DDevice->SetVertexShader( Handle ); + } + + HRESULT __stdcall GetVertexShader(DWORD* pHandle) + { + FPRINTF( s_FileHandle, "GetVertexShader\n" ); + return m_pD3DDevice->GetVertexShader( pHandle ); + } + + HRESULT __stdcall DeleteVertexShader(DWORD Handle) + { + FPRINTF( s_FileHandle, "DeleteVertexShader\n" ); + return m_pD3DDevice->DeleteVertexShader( Handle ); + } + + HRESULT __stdcall SetVertexShaderConstant(DWORD Register,CONST void* pConstantData,DWORD ConstantCount) + { + FPRINTF( s_FileHandle, "SetVertexShaderConstant\n" ); + return m_pD3DDevice->SetVertexShaderConstant( Register, pConstantData, ConstantCount ); + } + + HRESULT __stdcall GetVertexShaderConstant(DWORD Register,void* pConstantData,DWORD ConstantCount) + { + FPRINTF( s_FileHandle, "GetVertexShaderConstant\n" ); + return m_pD3DDevice->GetVertexShaderConstant( Register, pConstantData, ConstantCount ); + } + + HRESULT __stdcall GetVertexShaderDeclaration(DWORD Handle,void* pData,DWORD* pSizeOfData) + { + FPRINTF( s_FileHandle, "GetVertexShaderDeclaration\n" ); + return m_pD3DDevice->GetVertexShaderDeclaration( Handle, pData, pSizeOfData ); + } + + HRESULT __stdcall GetVertexShaderFunction(DWORD Handle,void* pData,DWORD* pSizeOfData) + { + FPRINTF( s_FileHandle, "GetVertexShaderFunction\n" ); + return m_pD3DDevice->GetVertexShaderFunction( Handle, pData, pSizeOfData ); + } + + HRESULT __stdcall SetStreamSource(UINT StreamNumber,IDirect3DVertexBuffer8* pStreamData,UINT Stride) + { + FPRINTF( s_FileHandle, "SetStreamSource\n" ); + return m_pD3DDevice->SetStreamSource( StreamNumber, pStreamData, Stride ); + } + + HRESULT __stdcall GetStreamSource(UINT StreamNumber,IDirect3DVertexBuffer8** ppStreamData,UINT* pStride) + { + FPRINTF( s_FileHandle, "GetStreamSource\n" ); + return m_pD3DDevice->GetStreamSource( StreamNumber, ppStreamData, pStride ); + } + + HRESULT __stdcall SetIndices(IDirect3DIndexBuffer8* pIndexData,UINT BaseVertexIndex) + { + FPRINTF( s_FileHandle, "SetIndices\n" ); + return m_pD3DDevice->SetIndices( pIndexData, BaseVertexIndex ); + } + + HRESULT __stdcall GetIndices(IDirect3DIndexBuffer8** ppIndexData,UINT* pBaseVertexIndex) + { + FPRINTF( s_FileHandle, "GetIndices\n" ); + return m_pD3DDevice->GetIndices( ppIndexData, pBaseVertexIndex ); + } + + HRESULT __stdcall CreatePixelShader(CONST DWORD* pFunction,DWORD* pHandle) + { + FPRINTF( s_FileHandle, "CreatePixelShader\n" ); + return m_pD3DDevice->CreatePixelShader( pFunction, pHandle ); + } + + HRESULT __stdcall SetPixelShader(DWORD Handle) + { + FPRINTF( s_FileHandle, "SetPixelShader\n" ); + return m_pD3DDevice->SetPixelShader( Handle ); + } + + HRESULT __stdcall GetPixelShader(DWORD* pHandle) + { + FPRINTF( s_FileHandle, "GetPixelShader\n" ); + return m_pD3DDevice->GetPixelShader( pHandle ); + } + + HRESULT __stdcall DeletePixelShader(DWORD Handle) + { + FPRINTF( s_FileHandle, "DeletePixelShader\n" ); + return m_pD3DDevice->DeletePixelShader( Handle ); + } + + HRESULT __stdcall SetPixelShaderConstant(DWORD Register,CONST void* pConstantData,DWORD ConstantCount) + { + FPRINTF( s_FileHandle, "SetPixelShaderConstant\n" ); + return m_pD3DDevice->SetPixelShaderConstant( Register, pConstantData, ConstantCount ); + } + + HRESULT __stdcall GetPixelShaderConstant(DWORD Register,void* pConstantData,DWORD ConstantCount) + { + FPRINTF( s_FileHandle, "GetPixelShaderConstant\n" ); + return m_pD3DDevice->GetPixelShaderConstant( Register, pConstantData, ConstantCount ); + } + + HRESULT __stdcall GetPixelShaderFunction(DWORD Handle,void* pData,DWORD* pSizeOfData) + { + FPRINTF( s_FileHandle, "GetPixelShaderFunction\n" ); + return m_pD3DDevice->GetPixelShaderFunction( Handle, pData, pSizeOfData ); + } + + HRESULT __stdcall DrawRectPatch(UINT Handle,CONST float* pNumSegs,CONST D3DRECTPATCH_INFO* pRectPatchInfo) + { + FPRINTF( s_FileHandle, "DrawRectPatch\n" ); + return m_pD3DDevice->DrawRectPatch( Handle, pNumSegs, pRectPatchInfo ); + } + + HRESULT __stdcall DrawTriPatch(UINT Handle,CONST float* pNumSegs,CONST D3DTRIPATCH_INFO* pTriPatchInfo) + { + FPRINTF( s_FileHandle, "DrawTriPatch\n" ); + return m_pD3DDevice->DrawTriPatch( Handle, pNumSegs, pTriPatchInfo ); + } + + HRESULT __stdcall DeletePatch(UINT Handle) + { + FPRINTF( s_FileHandle, "DeletePatch\n" ); + return m_pD3DDevice->DeletePatch( Handle ); + } +}; + +#endif // STUBD3D + +#endif // STUBD3DDEVICE_H + diff --git a/materialsystem/shaderapidx9/texturedx8.cpp b/materialsystem/shaderapidx9/texturedx8.cpp new file mode 100644 index 0000000..b5154a5 --- /dev/null +++ b/materialsystem/shaderapidx9/texturedx8.cpp @@ -0,0 +1,1545 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//===========================================================================// + +#define DISABLE_PROTECTED_THINGS +#include "locald3dtypes.h" +#include "texturedx8.h" +#include "shaderapidx8_global.h" +#include "colorformatdx8.h" +#include "shaderapi/ishaderutil.h" +#include "materialsystem/imaterialsystem.h" +#include "utlvector.h" +#include "recording.h" +#include "shaderapi/ishaderapi.h" +#include "filesystem.h" +#include "locald3dtypes.h" +#include "textureheap.h" +#include "tier1/utlbuffer.h" +#include "tier1/callqueue.h" +#include "tier0/vprof.h" +#include "vtf/vtf.h" +#include "tier0/icommandline.h" + +#include "tier0/memdbgon.h" + +#ifdef _WIN32 +#pragma warning (disable:4189 4701) +#endif + +static int s_TextureCount = 0; +static bool s_bTestingVideoMemorySize = false; + +//----------------------------------------------------------------------------- +// Stats... +//----------------------------------------------------------------------------- + +int TextureCount() +{ + return s_TextureCount; +} + +static bool IsVolumeTexture( IDirect3DBaseTexture* pBaseTexture ) +{ + if ( !pBaseTexture ) + { + return false; + } + + return ( pBaseTexture->GetType() == D3DRTYPE_VOLUMETEXTURE ); +} + +static HRESULT GetLevelDesc( IDirect3DBaseTexture* pBaseTexture, UINT level, D3DSURFACE_DESC* pDesc ) +{ + MEM_ALLOC_D3D_CREDIT(); + + if ( !pBaseTexture ) + { + return ( HRESULT )-1; + } + + HRESULT hr; + switch( pBaseTexture->GetType() ) + { + case D3DRTYPE_TEXTURE: + hr = ( ( IDirect3DTexture * )pBaseTexture )->GetLevelDesc( level, pDesc ); + break; + case D3DRTYPE_CUBETEXTURE: + hr = ( ( IDirect3DCubeTexture * )pBaseTexture )->GetLevelDesc( level, pDesc ); + break; + default: + return ( HRESULT )-1; + } + return hr; +} + +static HRESULT GetSurfaceFromTexture( IDirect3DBaseTexture* pBaseTexture, UINT level, + D3DCUBEMAP_FACES cubeFaceID, IDirect3DSurface** ppSurfLevel ) +{ + MEM_ALLOC_D3D_CREDIT(); + + if ( !pBaseTexture ) + { + return ( HRESULT )-1; + } + + HRESULT hr; + + switch( pBaseTexture->GetType() ) + { + case D3DRTYPE_TEXTURE: + hr = ( ( IDirect3DTexture * )pBaseTexture )->GetSurfaceLevel( level, ppSurfLevel ); + break; + case D3DRTYPE_CUBETEXTURE: + if (cubeFaceID !=0) + { + //Debugger(); + } + + hr = ( ( IDirect3DCubeTexture * )pBaseTexture )->GetCubeMapSurface( cubeFaceID, level, ppSurfLevel ); + break; + default: + Assert(0); + return ( HRESULT )-1; + } + return hr; +} + +//----------------------------------------------------------------------------- +// Gets the image format of a texture +//----------------------------------------------------------------------------- +static ImageFormat GetImageFormat( IDirect3DBaseTexture* pTexture ) +{ + MEM_ALLOC_D3D_CREDIT(); + + if ( pTexture ) + { + HRESULT hr; + if ( !IsVolumeTexture( pTexture ) ) + { + D3DSURFACE_DESC desc; + hr = GetLevelDesc( pTexture, 0, &desc ); + if ( !FAILED( hr ) ) + return ImageLoader::D3DFormatToImageFormat( desc.Format ); + } + else + { + D3DVOLUME_DESC desc; + IDirect3DVolumeTexture *pVolumeTexture = static_cast( pTexture ); + hr = pVolumeTexture->GetLevelDesc( 0, &desc ); + if ( !FAILED( hr ) ) + return ImageLoader::D3DFormatToImageFormat( desc.Format ); + } + } + + // Bogus baby! + return (ImageFormat)-1; +} + + +//----------------------------------------------------------------------------- +// Allocates the D3DTexture +//----------------------------------------------------------------------------- +IDirect3DBaseTexture* CreateD3DTexture( int width, int height, int nDepth, + ImageFormat dstFormat, int numLevels, int nCreationFlags, char *debugLabel ) // OK to skip the last param +{ + if ( nDepth <= 0 ) + { + nDepth = 1; + } + + bool isCubeMap = ( nCreationFlags & TEXTURE_CREATE_CUBEMAP ) != 0; + bool bIsRenderTarget = ( nCreationFlags & TEXTURE_CREATE_RENDERTARGET ) != 0; + bool bManaged = ( nCreationFlags & TEXTURE_CREATE_MANAGED ) != 0; + bool bSysmem = ( nCreationFlags & TEXTURE_CREATE_SYSMEM ) != 0; + bool bIsDepthBuffer = ( nCreationFlags & TEXTURE_CREATE_DEPTHBUFFER ) != 0; + bool isDynamic = ( nCreationFlags & TEXTURE_CREATE_DYNAMIC ) != 0; + bool bAutoMipMap = ( nCreationFlags & TEXTURE_CREATE_AUTOMIPMAP ) != 0; + bool bVertexTexture = ( nCreationFlags & TEXTURE_CREATE_VERTEXTEXTURE ) != 0; + bool bAllowNonFilterable = ( nCreationFlags & TEXTURE_CREATE_UNFILTERABLE_OK ) != 0; + bool bVolumeTexture = ( nDepth > 1 ); + bool bIsFallback = ( nCreationFlags & TEXTURE_CREATE_FALLBACK ) != 0; + bool bNoD3DBits = ( nCreationFlags & TEXTURE_CREATE_NOD3DMEMORY ) != 0; + bool bSRGB = (nCreationFlags & TEXTURE_CREATE_SRGB) != 0; // for Posix/GL only + + // NOTE: This function shouldn't be used for creating depth buffers! + Assert( !bIsDepthBuffer ); + + D3DFORMAT d3dFormat = D3DFMT_UNKNOWN; + + D3DPOOL pool = bManaged ? D3DPOOL_MANAGED : D3DPOOL_DEFAULT; + if ( bSysmem ) + pool = D3DPOOL_SYSTEMMEM; + + if ( IsX360() ) + { + // 360 does not support vertex textures + // 360 render target creation path is for the target as a texture source (NOT the EDRAM version) + // use normal texture format rules + Assert( !bVertexTexture ); + if ( !bVertexTexture ) + { + d3dFormat = ImageLoader::ImageFormatToD3DFormat( FindNearestSupportedFormat( dstFormat, false, false, false ) ); + } + } + else + { + d3dFormat = ImageLoader::ImageFormatToD3DFormat( FindNearestSupportedFormat( dstFormat, bVertexTexture, bIsRenderTarget, bAllowNonFilterable ) ); + } + + if ( d3dFormat == D3DFMT_UNKNOWN ) + { + Warning( "ShaderAPIDX8::CreateD3DTexture: Invalid color format!\n" ); + Assert( 0 ); + return 0; + } + + IDirect3DBaseTexture* pBaseTexture = NULL; + IDirect3DTexture* pD3DTexture = NULL; + IDirect3DCubeTexture* pD3DCubeTexture = NULL; + IDirect3DVolumeTexture* pD3DVolumeTexture = NULL; + HRESULT hr = S_OK; + DWORD usage = 0; + + if ( bIsRenderTarget ) + { + usage |= D3DUSAGE_RENDERTARGET; + } + if ( isDynamic ) + { + usage |= D3DUSAGE_DYNAMIC; + } + if ( bAutoMipMap ) + { + usage |= D3DUSAGE_AUTOGENMIPMAP; + } + +#ifdef DX_TO_GL_ABSTRACTION + { + if (bSRGB) + { + usage |= D3DUSAGE_TEXTURE_SRGB; // does not exist in real DX9... just for GL to know that this is an SRGB tex + } + } +#endif + + if ( isCubeMap ) + { +#if !defined( _X360 ) + hr = Dx9Device()->CreateCubeTexture( + width, + numLevels, + usage, + d3dFormat, + pool, + &pD3DCubeTexture, + NULL + #if defined( DX_TO_GL_ABSTRACTION ) + , debugLabel // tex create funcs take extra arg for debug name on GL + #endif + ); +#else + pD3DCubeTexture = g_TextureHeap.AllocCubeTexture( width, numLevels, usage, d3dFormat, bIsFallback, bNoD3DBits ); +#endif + pBaseTexture = pD3DCubeTexture; + } + else if ( bVolumeTexture ) + { +#if !defined( _X360 ) + hr = Dx9Device()->CreateVolumeTexture( + width, + height, + nDepth, + numLevels, + usage, + d3dFormat, + pool, + &pD3DVolumeTexture, + NULL + #if defined( DX_TO_GL_ABSTRACTION ) + , debugLabel // tex create funcs take extra arg for debug name on GL + #endif + ); +#else + Assert( !bIsFallback && !bNoD3DBits ); + pD3DVolumeTexture = g_TextureHeap.AllocVolumeTexture( width, height, nDepth, numLevels, usage, d3dFormat ); +#endif + pBaseTexture = pD3DVolumeTexture; + } + else + { +#if !defined( _X360 ) + // Override usage and managed params if using special hardware shadow depth map formats... + if ( ( d3dFormat == NVFMT_RAWZ ) || ( d3dFormat == NVFMT_INTZ ) || + ( d3dFormat == D3DFMT_D16 ) || ( d3dFormat == D3DFMT_D24S8 ) || + ( d3dFormat == ATIFMT_D16 ) || ( d3dFormat == ATIFMT_D24S8 ) ) + { + // Not putting D3DUSAGE_RENDERTARGET here causes D3D debug spew later, but putting the flag causes this create to fail... + usage = D3DUSAGE_DEPTHSTENCIL; + bManaged = false; + } + + // Override managed param if using special null texture format + if ( d3dFormat == NVFMT_NULL ) + { + bManaged = false; + } + + hr = Dx9Device()->CreateTexture( + width, + height, + numLevels, + usage, + d3dFormat, + pool, + &pD3DTexture, + NULL + #if defined( DX_TO_GL_ABSTRACTION ) + , debugLabel // tex create funcs take extra arg for debug name on GL + #endif + ); + +#else + pD3DTexture = g_TextureHeap.AllocTexture( width, height, numLevels, usage, d3dFormat, bIsFallback, bNoD3DBits ); +#endif + pBaseTexture = pD3DTexture; + } + + if ( FAILED( hr ) ) + { +#ifdef ENABLE_NULLREF_DEVICE_SUPPORT + if( CommandLine()->FindParm( "-nulldevice" ) ) + { + Warning( "ShaderAPIDX8::CreateD3DTexture: Null device used. Texture not created.\n" ); + return 0; + } +#endif + + switch ( hr ) + { + case D3DERR_INVALIDCALL: + Warning( "ShaderAPIDX8::CreateD3DTexture: D3DERR_INVALIDCALL\n" ); + break; + case D3DERR_OUTOFVIDEOMEMORY: + // This conditional is here so that we don't complain when testing + // how much video memory we have. . this is kinda gross. + if ( !s_bTestingVideoMemorySize ) + { + Warning( "ShaderAPIDX8::CreateD3DTexture: D3DERR_OUTOFVIDEOMEMORY\n" ); + } + break; + case E_OUTOFMEMORY: + Warning( "ShaderAPIDX8::CreateD3DTexture: E_OUTOFMEMORY\n" ); + break; + default: + break; + } + return 0; + } + +#ifdef MEASURE_DRIVER_ALLOCATIONS + int nMipCount = numLevels; + if ( !nMipCount ) + { + while ( width > 1 || height > 1 ) + { + width >>= 1; + height >>= 1; + ++nMipCount; + } + } + + int nMemUsed = nMipCount * 1.1f * 1024; + if ( isCubeMap ) + { + nMemUsed *= 6; + } + + VPROF_INCREMENT_GROUP_COUNTER( "texture count", COUNTER_GROUP_NO_RESET, 1 ); + VPROF_INCREMENT_GROUP_COUNTER( "texture driver mem", COUNTER_GROUP_NO_RESET, nMemUsed ); + VPROF_INCREMENT_GROUP_COUNTER( "total driver mem", COUNTER_GROUP_NO_RESET, nMemUsed ); +#endif + + ++s_TextureCount; + + return pBaseTexture; +} + + +//----------------------------------------------------------------------------- +// Texture destruction +//----------------------------------------------------------------------------- +void ReleaseD3DTexture( IDirect3DBaseTexture* pD3DTex ) +{ + int ref = pD3DTex->Release(); + Assert( ref == 0 ); +} + +void DestroyD3DTexture( IDirect3DBaseTexture* pD3DTex ) +{ + if ( pD3DTex ) + { +#ifdef MEASURE_DRIVER_ALLOCATIONS + D3DRESOURCETYPE type = pD3DTex->GetType(); + int nMipCount = pD3DTex->GetLevelCount(); + if ( type == D3DRTYPE_CUBETEXTURE ) + { + nMipCount *= 6; + } + int nMemUsed = nMipCount * 1.1f * 1024; + VPROF_INCREMENT_GROUP_COUNTER( "texture count", COUNTER_GROUP_NO_RESET, -1 ); + VPROF_INCREMENT_GROUP_COUNTER( "texture driver mem", COUNTER_GROUP_NO_RESET, -nMemUsed ); + VPROF_INCREMENT_GROUP_COUNTER( "total driver mem", COUNTER_GROUP_NO_RESET, -nMemUsed ); +#endif + +#if !defined( _X360 ) + CMatRenderContextPtr pRenderContext( materials ); + ICallQueue *pCallQueue; + if ( ( pCallQueue = pRenderContext->GetCallQueue() ) != NULL ) + { + pCallQueue->QueueCall( ReleaseD3DTexture, pD3DTex ); + } + else + { + ReleaseD3DTexture( pD3DTex ); + } +#else + g_TextureHeap.FreeTexture( pD3DTex ); +#endif + --s_TextureCount; + } +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *pTex - +// Output : int +//----------------------------------------------------------------------------- +int GetD3DTextureRefCount( IDirect3DBaseTexture *pTex ) +{ + if ( !pTex ) + return 0; + + pTex->AddRef(); + int ref = pTex->Release(); + + return ref; +} + +//----------------------------------------------------------------------------- +// See version 13 for a function that converts a texture to a mipmap (ConvertToMipmap) +//----------------------------------------------------------------------------- + + +//----------------------------------------------------------------------------- +// Lock, unlock a texture... +//----------------------------------------------------------------------------- + +static RECT s_LockedSrcRect; +static D3DLOCKED_RECT s_LockedRect; +#ifdef DBGFLAG_ASSERT +static bool s_bInLock = false; +#endif + +bool LockTexture( ShaderAPITextureHandle_t bindId, int copy, IDirect3DBaseTexture* pTexture, int level, + D3DCUBEMAP_FACES cubeFaceID, int xOffset, int yOffset, int width, int height, bool bDiscard, + CPixelWriter& writer ) +{ + Assert( !s_bInLock ); + + IDirect3DSurface* pSurf; + HRESULT hr = GetSurfaceFromTexture( pTexture, level, cubeFaceID, &pSurf ); + if ( FAILED( hr ) ) + return false; + + s_LockedSrcRect.left = xOffset; + s_LockedSrcRect.right = xOffset + width; + s_LockedSrcRect.top = yOffset; + s_LockedSrcRect.bottom = yOffset + height; + + unsigned int flags = D3DLOCK_NOSYSLOCK; + flags |= bDiscard ? D3DLOCK_DISCARD : 0; + RECORD_COMMAND( DX8_LOCK_TEXTURE, 6 ); + RECORD_INT( bindId ); + RECORD_INT( copy ); + RECORD_INT( level ); + RECORD_INT( cubeFaceID ); + RECORD_STRUCT( &s_LockedSrcRect, sizeof(s_LockedSrcRect) ); + RECORD_INT( flags ); + + tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "D3DLockTexture" ); + + hr = pSurf->LockRect( &s_LockedRect, &s_LockedSrcRect, flags ); + pSurf->Release(); + + if ( FAILED( hr ) ) + return false; + + writer.SetPixelMemory( GetImageFormat(pTexture), s_LockedRect.pBits, s_LockedRect.Pitch ); + +#ifdef DBGFLAG_ASSERT + s_bInLock = true; +#endif + return true; +} + +void UnlockTexture( ShaderAPITextureHandle_t bindId, int copy, IDirect3DBaseTexture* pTexture, int level, + D3DCUBEMAP_FACES cubeFaceID ) +{ + tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s", __FUNCTION__ ); + + Assert( s_bInLock ); + + IDirect3DSurface* pSurf; + HRESULT hr = GetSurfaceFromTexture( pTexture, level, cubeFaceID, &pSurf ); + if (FAILED(hr)) + return; + +#ifdef RECORD_TEXTURES + int width = s_LockedSrcRect.right - s_LockedSrcRect.left; + int height = s_LockedSrcRect.bottom - s_LockedSrcRect.top; + int imageFormatSize = ImageLoader::SizeInBytes( GetImageFormat( pTexture ) ); + Assert( imageFormatSize != 0 ); + int validDataBytesPerRow = imageFormatSize * width; + int storeSize = validDataBytesPerRow * height; + static CUtlVector< unsigned char > tmpMem; + if( tmpMem.Size() < storeSize ) + { + tmpMem.AddMultipleToTail( storeSize - tmpMem.Size() ); + } + unsigned char *pDst = tmpMem.Base(); + unsigned char *pSrc = ( unsigned char * )s_LockedRect.pBits; + RECORD_COMMAND( DX8_SET_TEXTURE_DATA, 3 ); + RECORD_INT( validDataBytesPerRow ); + RECORD_INT( height ); + int i; + for( i = 0; i < height; i++ ) + { + memcpy( pDst, pSrc, validDataBytesPerRow ); + pDst += validDataBytesPerRow; + pSrc += s_LockedRect.Pitch; + } + RECORD_STRUCT( tmpMem.Base(), storeSize ); +#endif // RECORD_TEXTURES + + RECORD_COMMAND( DX8_UNLOCK_TEXTURE, 4 ); + RECORD_INT( bindId ); + RECORD_INT( copy ); + RECORD_INT( level ); + RECORD_INT( cubeFaceID ); + + hr = pSurf->UnlockRect(); + pSurf->Release(); +#ifdef DBGFLAG_ASSERT + s_bInLock = false; +#endif +} + +//----------------------------------------------------------------------------- +// Compute texture size based on compression +//----------------------------------------------------------------------------- + +static inline int DetermineGreaterPowerOfTwo( int val ) +{ + int num = 1; + while (val > num) + { + num <<= 1; + } + + return num; +} + +inline int DeterminePowerOfTwo( int val ) +{ + int pow = 0; + while ((val & 0x1) == 0x0) + { + val >>= 1; + ++pow; + } + + return pow; +} + + +//----------------------------------------------------------------------------- +// Blit in bits +//----------------------------------------------------------------------------- +// NOTE: IF YOU CHANGE THIS, CHANGE THE VERSION IN PLAYBACK.CPP!!!! +// OPTIMIZE??: could lock the texture directly instead of the surface in dx9. +#if !defined( _X360 ) +static void BlitSurfaceBits( TextureLoadInfo_t &info, int xOffset, int yOffset, int srcStride ) +{ + tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s", __FUNCTION__ ); + + // Get the level of the texture we want to write into + IDirect3DSurface* pTextureLevel; + + if (info.m_CubeFaceID !=0) + { + //Debugger(); + } + + + HRESULT hr = GetSurfaceFromTexture( info.m_pTexture, info.m_nLevel, info.m_CubeFaceID, &pTextureLevel ); + if ( FAILED( hr ) ) + return; + + RECT srcRect; + RECT *pSrcRect = NULL; + D3DLOCKED_RECT lockedRect; + + srcRect.left = xOffset; + srcRect.right = xOffset + info.m_nWidth; + srcRect.top = yOffset; + srcRect.bottom = yOffset + info.m_nHeight; + +#if defined( SHADERAPIDX9 ) && !defined( _X360 ) && !defined( DX_TO_GL_ABSTRACTION ) + if ( !info.m_bTextureIsLockable ) + { + // Copy from system memory to video memory using D3D9Device->UpdateSurface + bool bSuccess = false; + + D3DSURFACE_DESC desc; + Verify( pTextureLevel->GetDesc( &desc ) == S_OK ); + ImageFormat dstFormat = ImageLoader::D3DFormatToImageFormat( desc.Format ); + D3DFORMAT dstFormatD3D = ImageLoader::ImageFormatToD3DFormat( dstFormat ); + + IDirect3DSurface* pSrcSurface = NULL; + bool bCopyBitsToSrcSurface = true; + +#if defined(IS_WINDOWS_PC) && defined(SHADERAPIDX9) + // D3D9Ex fast path: create a texture wrapping our own system memory buffer + // if the source and destination formats are exactly the same and the stride + // is tightly packed. no locking/blitting required. + // NOTE: the fast path does not work on sub-4x4 DXT compressed textures. + extern bool g_ShaderDeviceUsingD3D9Ex; + if ( g_ShaderDeviceUsingD3D9Ex && + ( info.m_SrcFormat == dstFormat || ( info.m_SrcFormat == IMAGE_FORMAT_DXT1_ONEBITALPHA && dstFormat == IMAGE_FORMAT_DXT1 ) ) && + ( !ImageLoader::IsCompressed( dstFormat ) || (info.m_nWidth >= 4 || info.m_nHeight >= 4) ) ) + { + if ( srcStride == 0 || srcStride == info.m_nWidth * ImageLoader::SizeInBytes( info.m_SrcFormat ) ) + { + IDirect3DTexture9* pTempTex = NULL; + if ( Dx9Device()->CreateTexture( info.m_nWidth, info.m_nHeight, 1, 0, dstFormatD3D, D3DPOOL_SYSTEMMEM, &pTempTex, (HANDLE*) &info.m_pSrcData ) == S_OK ) + { + IDirect3DSurface* pTempSurf = NULL; + if ( pTempTex->GetSurfaceLevel( 0, &pTempSurf ) == S_OK ) + { + pSrcSurface = pTempSurf; + bCopyBitsToSrcSurface = false; + } + pTempTex->Release(); + } + } + } +#endif + + // If possible to create a texture of this size, create a temporary texture in + // system memory and then use the UpdateSurface method to copy between textures. + if ( !pSrcSurface && ( g_pHardwareConfig->Caps().m_SupportsNonPow2Textures || + ( IsPowerOfTwo( info.m_nWidth ) && IsPowerOfTwo( info.m_nHeight ) ) ) ) + { + int tempW = info.m_nWidth, tempH = info.m_nHeight, mip = 0; + if ( info.m_nLevel > 0 && ( ( tempW | tempH ) & 3 ) && ImageLoader::IsCompressed( dstFormat ) ) + { + // Loading lower mip levels of DXT compressed textures is sort of tricky + // because we can't create textures that aren't multiples of 4, and we can't + // pass subrectangles of DXT textures into UpdateSurface. Create a temporary + // texture which is 1 or 2 mip levels larger and then lock the appropriate + // mip level to grab its correctly-dimensioned surface. -henryg 11/18/2011 + mip = ( info.m_nLevel > 1 && ( ( tempW | tempH ) & 1 ) ) ? 2 : 1; + tempW <<= mip; + tempH <<= mip; + } + + IDirect3DTexture9* pTempTex = NULL; + IDirect3DSurface* pTempSurf = NULL; + if ( Dx9Device()->CreateTexture( tempW, tempH, mip+1, 0, dstFormatD3D, D3DPOOL_SYSTEMMEM, &pTempTex, NULL ) == S_OK ) + { + if ( pTempTex->GetSurfaceLevel( mip, &pTempSurf ) == S_OK ) + { + pSrcSurface = pTempSurf; + bCopyBitsToSrcSurface = true; + } + pTempTex->Release(); + } + } + + // Create an offscreen surface if the texture path wasn't an option. + if ( !pSrcSurface ) + { + IDirect3DSurface* pTempSurf = NULL; + if ( Dx9Device()->CreateOffscreenPlainSurface( info.m_nWidth, info.m_nHeight, dstFormatD3D, D3DPOOL_SYSTEMMEM, &pTempSurf, NULL ) == S_OK ) + { + pSrcSurface = pTempSurf; + bCopyBitsToSrcSurface = true; + } + } + + // Lock and fill the surface + if ( bCopyBitsToSrcSurface && pSrcSurface ) + { + if ( pSrcSurface->LockRect( &lockedRect, NULL, D3DLOCK_NOSYSLOCK ) == S_OK ) + { + unsigned char *pImage = (unsigned char *)lockedRect.pBits; + ShaderUtil()->ConvertImageFormat( info.m_pSrcData, info.m_SrcFormat, + pImage, dstFormat, info.m_nWidth, info.m_nHeight, srcStride, lockedRect.Pitch ); + pSrcSurface->UnlockRect(); + } + else + { + // Lock failed. + pSrcSurface->Release(); + pSrcSurface = NULL; + } + } + + // Perform the UpdateSurface call that blits between system and video memory + if ( pSrcSurface ) + { + POINT pt = { xOffset, yOffset }; + bSuccess = ( Dx9Device()->UpdateSurface( pSrcSurface, NULL, pTextureLevel, &pt ) == S_OK ); + pSrcSurface->Release(); + } + + if ( !bSuccess ) + { + Warning( "CShaderAPIDX8::BlitTextureBits: couldn't lock texture rect or use UpdateSurface\n" ); + } + + pTextureLevel->Release(); + return; + } +#endif + + Assert( info.m_bTextureIsLockable ); + +#ifndef RECORD_TEXTURES + RECORD_COMMAND( DX8_LOCK_TEXTURE, 6 ); + RECORD_INT( info.m_TextureHandle ); + RECORD_INT( info.m_nCopy ); + RECORD_INT( info.m_nLevel ); + RECORD_INT( info.m_CubeFaceID ); + RECORD_STRUCT( &srcRect, sizeof(srcRect) ); + RECORD_INT( D3DLOCK_NOSYSLOCK ); +#endif + + { + tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s - D3DLockRect", __FUNCTION__ ); + + // lock the region (could be the full surface or less) + if ( FAILED( pTextureLevel->LockRect( &lockedRect, &srcRect, D3DLOCK_NOSYSLOCK ) ) ) + { + Warning( "CShaderAPIDX8::BlitTextureBits: couldn't lock texture rect\n" ); + pTextureLevel->Release(); + return; + } + } + + { + tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s - ConvertImageFormat", __FUNCTION__ ); + + // garymcthack : need to make a recording command for this. + ImageFormat dstFormat = GetImageFormat( info.m_pTexture ); + unsigned char *pImage = (unsigned char *)lockedRect.pBits; + ShaderUtil()->ConvertImageFormat( info.m_pSrcData, info.m_SrcFormat, + pImage, dstFormat, info.m_nWidth, info.m_nHeight, srcStride, lockedRect.Pitch ); + } + +#ifndef RECORD_TEXTURES + RECORD_COMMAND( DX8_UNLOCK_TEXTURE, 4 ); + RECORD_INT( info.m_TextureHandle ); + RECORD_INT( info.m_nCopy ); + RECORD_INT( info.m_nLevel ); + RECORD_INT( info.m_CubeFaceID ); +#endif + + { + tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s - UnlockRect", __FUNCTION__ ); + + if ( FAILED( pTextureLevel->UnlockRect() ) ) + { + Warning( "CShaderAPIDX8::BlitTextureBits: couldn't unlock texture rect\n" ); + pTextureLevel->Release(); + return; + } + } + + tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s - pTextureLevel->Release", __FUNCTION__ ); + pTextureLevel->Release(); +} +#endif + +//----------------------------------------------------------------------------- +// Puts 2D texture data into 360 gpu memory. +//----------------------------------------------------------------------------- +#if defined( _X360 ) +static void BlitSurfaceBits( TextureLoadInfo_t &info, int xOffset, int yOffset, int srcStride ) +{ + // xbox textures are NOT backed in gpu memory contiguously + // stride details are critical - see [Xbox 360 Texture Storage] + // a d3dformat identifier on the xbox is tiled, the same d3dformat on the pc is expected linear to the app + // we purposely hide the tiling here, otherwise much confusion for the pc + // the *entire* target must be un-tiled *only* before any *subrect* blitting linear work + // the *entire* target must then be re-tiled after the *subrect* blit + // procedural textures require this to subrect blit their new portions correctly + // the tiling dance can be avoided if the source and target match in tiled state during a full rect blit + + if ( info.m_bSrcIsTiled ) + { + // not supporting subrect blitting from a tiled source + Assert( 0 ); + return; + } + + CUtlBuffer formatConvertMemory; + unsigned char *pSrcData = info.m_pSrcData; + + ImageFormat dstFormat = GetImageFormat( info.m_pTexture ); + if ( dstFormat != info.m_SrcFormat ) + { + if ( !info.m_bCanConvertFormat ) + { + // texture is expected to be in target format + // not supporting conversion of a tiled source + Assert( 0 ); + return; + } + + int srcSize = ImageLoader::GetMemRequired( info.m_nWidth, info.m_nHeight, 1, info.m_SrcFormat, false ); + int dstSize = ImageLoader::GetMemRequired( info.m_nWidth, info.m_nHeight, 1, dstFormat, false ); + formatConvertMemory.EnsureCapacity( dstSize ); + + // due to format conversion, source is in non-native order + ImageLoader::PreConvertSwapImageData( (unsigned char*)info.m_pSrcData, srcSize, info.m_SrcFormat, info.m_nWidth, srcStride ); + + // slow conversion operation + if ( !ShaderUtil()->ConvertImageFormat( + info.m_pSrcData, + info.m_SrcFormat, + (unsigned char*)formatConvertMemory.Base(), + dstFormat, + info.m_nWidth, + info.m_nHeight, + srcStride, + 0 ) ) + { + // conversion failed + Assert( 0 ); + return; + } + + // due to format conversion, source must have been in non-native order + ImageLoader::PostConvertSwapImageData( (unsigned char*)formatConvertMemory.Base(), dstSize, dstFormat ); + + pSrcData = (unsigned char*)formatConvertMemory.Base(); + } + + // get the top mip level info (needed for proper sub mip access) + XGTEXTURE_DESC baseDesc; + XGGetTextureDesc( info.m_pTexture, 0, &baseDesc ); + bool bDstIsTiled = XGIsTiledFormat( baseDesc.Format ) == TRUE; + + // get the target mip level info + XGTEXTURE_DESC mipDesc; + XGGetTextureDesc( info.m_pTexture, info.m_nLevel, &mipDesc ); + bool bFullSurfBlit = ( mipDesc.Width == (unsigned)info.m_nWidth && mipDesc.Height == (unsigned)info.m_nHeight ); + + // get the mip level of the texture we want to write into + IDirect3DSurface* pTextureLevel; + HRESULT hr = GetSurfaceFromTexture( info.m_pTexture, info.m_nLevel, info.m_CubeFaceID, &pTextureLevel ); + if ( FAILED( hr ) ) + { + Warning( "CShaderAPIDX8::BlitTextureBits: GetSurfaceFromTexture() failure\n" ); + return; + } + + CUtlBuffer scratchMemory; + D3DLOCKED_RECT lockedRect; + + hr = pTextureLevel->LockRect( &lockedRect, NULL, D3DLOCK_NOSYSLOCK ); + if ( FAILED( hr ) ) + { + Warning( "CShaderAPIDX8::BlitTextureBits: couldn't lock texture rect\n" ); + goto cleanUp; + } + unsigned char *pTargetImage = (unsigned char *)lockedRect.pBits; + + POINT p; + p.x = xOffset; + p.y = yOffset; + + RECT r; + r.left = 0; + r.top = 0; + r.right = info.m_nWidth; + r.bottom = info.m_nHeight; + + int blockSize = mipDesc.Width/mipDesc.WidthInBlocks; + if ( !srcStride ) + { + srcStride = (mipDesc.Width/blockSize)*mipDesc.BytesPerBlock; + } + + // subrect blitting path + if ( !bDstIsTiled ) + { + // Copy the subrect without conversion + hr = XGCopySurface( + pTargetImage, + mipDesc.RowPitch, + mipDesc.Width, + mipDesc.Height, + mipDesc.Format, + &p, + pSrcData, + srcStride, + mipDesc.Format, + &r, + 0, + 0 ); + if ( FAILED( hr ) ) + { + Warning( "CShaderAPIDX8::BlitTextureBits: failed subrect copy\n" ); + goto cleanUp; + } + } + else + { + int tileFlags = 0; + if ( !( mipDesc.Flags & XGTDESC_PACKED ) ) + tileFlags |= XGTILE_NONPACKED; + if ( mipDesc.Flags & XGTDESC_BORDERED ) + tileFlags |= XGTILE_BORDER; + + // tile the temp store back into the target surface + XGTileTextureLevel( + baseDesc.Width, + baseDesc.Height, + info.m_nLevel, + XGGetGpuFormat( baseDesc.Format ), + tileFlags, + pTargetImage, + &p, + pSrcData, + srcStride, + &r ); + } + + hr = pTextureLevel->UnlockRect(); + if ( FAILED( hr ) ) + { + Warning( "CShaderAPIDX8::BlitTextureBits: couldn't unlock texture rect\n" ); + goto cleanUp; + } + +cleanUp: + pTextureLevel->Release(); +} +#endif + +//----------------------------------------------------------------------------- +// Blit in bits +//----------------------------------------------------------------------------- +#if !defined( _X360 ) +static void BlitVolumeBits( TextureLoadInfo_t &info, int xOffset, int yOffset, int srcStride ) +{ + D3DBOX srcBox; + D3DLOCKED_BOX lockedBox; + srcBox.Left = xOffset; + srcBox.Right = xOffset + info.m_nWidth; + srcBox.Top = yOffset; + srcBox.Bottom = yOffset + info.m_nHeight; + srcBox.Front = info.m_nZOffset; + srcBox.Back = info.m_nZOffset + 1; + +#ifndef RECORD_TEXTURES + RECORD_COMMAND( DX8_LOCK_TEXTURE, 6 ); + RECORD_INT( info.m_TextureHandle ); + RECORD_INT( info.m_nCopy ); + RECORD_INT( info.m_nLevel ); + RECORD_INT( info.m_CubeFaceID ); + RECORD_STRUCT( &srcRect, sizeof(srcRect) ); + RECORD_INT( D3DLOCK_NOSYSLOCK ); +#endif + + IDirect3DVolumeTexture *pVolumeTexture = static_cast( info.m_pTexture ); + if ( FAILED( pVolumeTexture->LockBox( info.m_nLevel, &lockedBox, &srcBox, D3DLOCK_NOSYSLOCK ) ) ) + { + Warning( "BlitVolumeBits: couldn't lock volume texture rect\n" ); + return; + } + + // garymcthack : need to make a recording command for this. + ImageFormat dstFormat = GetImageFormat( info.m_pTexture ); + unsigned char *pImage = (unsigned char *)lockedBox.pBits; + ShaderUtil()->ConvertImageFormat( info.m_pSrcData, info.m_SrcFormat, + pImage, dstFormat, info.m_nWidth, info.m_nHeight, srcStride, lockedBox.RowPitch ); + +#ifndef RECORD_TEXTURES + RECORD_COMMAND( DX8_UNLOCK_TEXTURE, 4 ); + RECORD_INT( info.m_TextureHandle ); + RECORD_INT( info.m_nCopy ); + RECORD_INT( info.m_nLevel ); + RECORD_INT( info.m_CubeFaceID ); +#endif + + if ( FAILED( pVolumeTexture->UnlockBox( info.m_nLevel ) ) ) + { + Warning( "BlitVolumeBits: couldn't unlock volume texture rect\n" ); + return; + } +} +#endif + +//----------------------------------------------------------------------------- +// Puts 3D texture data into 360 gpu memory. +// Does not support any subvolume or slice blitting. +//----------------------------------------------------------------------------- +#if defined( _X360 ) +static void BlitVolumeBits( TextureLoadInfo_t &info, int xOffset, int yOffset, int srcStride ) +{ + if ( xOffset || yOffset || info.m_nZOffset || srcStride ) + { + // not supporting any subvolume blitting + // the entire volume per mip must be blitted + Assert( 0 ); + return; + } + + ImageFormat dstFormat = GetImageFormat( info.m_pTexture ); + if ( dstFormat != info.m_SrcFormat ) + { + // texture is expected to be in target format + // not supporting conversion + Assert( 0 ); + return; + } + + // get the top mip level info (needed for proper sub mip access) + XGTEXTURE_DESC baseDesc; + XGGetTextureDesc( info.m_pTexture, 0, &baseDesc ); + bool bDstIsTiled = XGIsTiledFormat( baseDesc.Format ) == TRUE; + if ( info.m_bSrcIsTiled && !bDstIsTiled ) + { + // not supporting a tiled source into an untiled target + Assert( 0 ); + return; + } + + // get the mip level info + XGTEXTURE_DESC mipDesc; + XGGetTextureDesc( info.m_pTexture, info.m_nLevel, &mipDesc ); + bool bFullSurfBlit = ( mipDesc.Width == (unsigned int)info.m_nWidth && mipDesc.Height == (unsigned int)info.m_nHeight ); + + if ( !bFullSurfBlit ) + { + // not supporting subrect blitting + Assert( 0 ); + return; + } + + D3DLOCKED_BOX lockedBox; + + // get the mip level of the volume we want to write into + IDirect3DVolumeTexture *pVolumeTexture = static_cast( info.m_pTexture ); + HRESULT hr = pVolumeTexture->LockBox( info.m_nLevel, &lockedBox, NULL, D3DLOCK_NOSYSLOCK ); + if ( FAILED( hr ) ) + { + Warning( "CShaderAPIDX8::BlitVolumeBits: Couldn't lock volume box\n" ); + return; + } + + unsigned char *pSrcData = info.m_pSrcData; + unsigned char *pTargetImage = (unsigned char *)lockedBox.pBits; + + int tileFlags = 0; + if ( !( mipDesc.Flags & XGTDESC_PACKED ) ) + tileFlags |= XGTILE_NONPACKED; + if ( mipDesc.Flags & XGTDESC_BORDERED ) + tileFlags |= XGTILE_BORDER; + + if ( !info.m_bSrcIsTiled && bDstIsTiled ) + { + // tile the source directly into the target surface + XGTileVolumeTextureLevel( + baseDesc.Width, + baseDesc.Height, + baseDesc.Depth, + info.m_nLevel, + XGGetGpuFormat( baseDesc.Format ), + tileFlags, + pTargetImage, + NULL, + pSrcData, + mipDesc.RowPitch, + mipDesc.SlicePitch, + NULL ); + } + else if ( !info.m_bSrcIsTiled && !bDstIsTiled ) + { + // not implemented yet + Assert( 0 ); + } + else + { + // not implemented yet + Assert( 0 ); + } + + hr = pVolumeTexture->UnlockBox( info.m_nLevel ); + if ( FAILED( hr ) ) + { + Warning( "CShaderAPIDX8::BlitVolumeBits: couldn't unlock volume box\n" ); + return; + } +} +#endif + +// FIXME: How do I blit from D3DPOOL_SYSTEMMEM to D3DPOOL_MANAGED? I used to use CopyRects for this. UpdateSurface doesn't work because it can't blit to anything besides D3DPOOL_DEFAULT. +// We use this only in the case where we need to create a < 4x4 miplevel for a compressed texture. We end up creating a 4x4 system memory texture, and blitting it into the proper miplevel. +// 6) LockRects should be used for copying between SYSTEMMEM and +// MANAGED. For such a small copy, you'd avoid a significant +// amount of overhead from the old CopyRects code. Ideally, you +// should just lock the bottom of MANAGED and generate your +// sub-4x4 data there. + +// NOTE: IF YOU CHANGE THIS, CHANGE THE VERSION IN PLAYBACK.CPP!!!! +static void BlitTextureBits( TextureLoadInfo_t &info, int xOffset, int yOffset, int srcStride ) +{ +#ifdef RECORD_TEXTURES + RECORD_COMMAND( DX8_BLIT_TEXTURE_BITS, 14 ); + RECORD_INT( info.m_TextureHandle ); + RECORD_INT( info.m_nCopy ); + RECORD_INT( info.m_nLevel ); + RECORD_INT( info.m_CubeFaceID ); + RECORD_INT( xOffset ); + RECORD_INT( yOffset ); + RECORD_INT( info.m_nZOffset ); + RECORD_INT( info.m_nWidth ); + RECORD_INT( info.m_nHeight ); + RECORD_INT( info.m_SrcFormat ); + RECORD_INT( srcStride ); + RECORD_INT( GetImageFormat( info.m_pTexture ) ); + // strides are in bytes. + int srcDataSize; + if ( srcStride == 0 ) + { + srcDataSize = ImageLoader::GetMemRequired( info.m_nWidth, info.m_nHeight, 1, info.m_SrcFormat, false ); + } + else + { + srcDataSize = srcStride * info.m_nHeight; + } + RECORD_INT( srcDataSize ); + RECORD_STRUCT( info.m_pSrcData, srcDataSize ); +#endif // RECORD_TEXTURES + + if ( !IsVolumeTexture( info.m_pTexture ) ) + { + Assert( info.m_nZOffset == 0 ); + BlitSurfaceBits( info, xOffset, yOffset, srcStride ); + } + else + { + BlitVolumeBits( info, xOffset, yOffset, srcStride ); + } +} + +//----------------------------------------------------------------------------- +// Texture image upload +//----------------------------------------------------------------------------- +void LoadTexture( TextureLoadInfo_t &info ) +{ + MEM_ALLOC_D3D_CREDIT(); + + Assert( info.m_pSrcData ); + Assert( info.m_pTexture ); + +#ifdef _DEBUG + ImageFormat format = GetImageFormat( info.m_pTexture ); + Assert( (format != -1) && (format == FindNearestSupportedFormat( format, false, false, false )) ); +#endif + + // Copy in the bits... + BlitTextureBits( info, 0, 0, 0 ); +} + +void LoadVolumeTextureFromVTF( TextureLoadInfo_t &info, IVTFTexture* pVTF, int iVTFFrame ) +{ + if ( !info.m_pTexture || info.m_pTexture->GetType() != D3DRTYPE_VOLUMETEXTURE ) + { + Assert( 0 ); + return; + } + + IDirect3DVolumeTexture9 *pVolTex = static_cast( info.m_pTexture ); + + D3DVOLUME_DESC desc; + if ( pVolTex->GetLevelDesc( 0, &desc ) != S_OK ) + { + Warning( "LoadVolumeTextureFromVTF: couldn't get texture level description\n" ); + return; + } + + int iMipCount = pVolTex->GetLevelCount(); + if ( pVTF->Depth() != (int)desc.Depth || pVTF->Width() != (int)desc.Width || pVTF->Height() != (int)desc.Height || pVTF->MipCount() < iMipCount ) + { + Warning( "LoadVolumeTextureFromVTF: VTF dimensions do not match texture\n" ); + return; + } + + TextureLoadInfo_t sliceInfo = info; + +#if !defined( _X360 ) && !defined( DX_TO_GL_ABSTRACTION ) + IDirect3DVolumeTexture9 *pStagingTexture = NULL; + if ( !info.m_bTextureIsLockable ) + { + IDirect3DVolumeTexture9 *pTemp; + if ( Dx9Device()->CreateVolumeTexture( desc.Width, desc.Height, desc.Depth, iMipCount, 0, desc.Format, D3DPOOL_SYSTEMMEM, &pTemp, NULL ) != S_OK ) + { + Warning( "LoadVolumeTextureFromVTF: failed to create temporary staging texture\n" ); + return; + } + sliceInfo.m_pTexture = static_cast( pTemp ); + sliceInfo.m_bTextureIsLockable = true; + pStagingTexture = pTemp; + } +#endif + + for ( int iMip = 0; iMip < iMipCount; ++iMip ) + { + int w, h, d; + pVTF->ComputeMipLevelDimensions( iMip, &w, &h, &d ); + sliceInfo.m_nLevel = iMip; + sliceInfo.m_nWidth = w; + sliceInfo.m_nHeight = h; + for ( int iSlice = 0; iSlice < d; ++iSlice ) + { + sliceInfo.m_nZOffset = iSlice; + sliceInfo.m_pSrcData = pVTF->ImageData( iVTFFrame, 0, iMip, 0, 0, iSlice ); + BlitTextureBits( sliceInfo, 0, 0, 0 ); + } + } + +#if !defined( _X360 ) && !defined( DX_TO_GL_ABSTRACTION ) + if ( pStagingTexture ) + { + if ( Dx9Device()->UpdateTexture( pStagingTexture, pVolTex ) != S_OK ) + { + Warning( "LoadVolumeTextureFromVTF: volume UpdateTexture failed\n" ); + } + pStagingTexture->Release(); + } +#endif +} + +void LoadCubeTextureFromVTF( TextureLoadInfo_t &info, IVTFTexture* pVTF, int iVTFFrame ) +{ + if ( !info.m_pTexture || info.m_pTexture->GetType() != D3DRTYPE_CUBETEXTURE ) + { + Assert( 0 ); + return; + } + + IDirect3DCubeTexture9 *pCubeTex = static_cast( info.m_pTexture ); + + D3DSURFACE_DESC desc; + if ( pCubeTex->GetLevelDesc( 0, &desc ) != S_OK ) + { + Warning( "LoadCubeTextureFromVTF: couldn't get texture level description\n" ); + return; + } + + int iMipCount = pCubeTex->GetLevelCount(); + if ( pVTF->Depth() != 1 || pVTF->Width() != (int)desc.Width || pVTF->Height() != (int)desc.Height || pVTF->FaceCount() < 6 || pVTF->MipCount() < iMipCount ) + { + Warning( "LoadCubeTextureFromVTF: VTF dimensions do not match texture\n" ); + return; + } + + TextureLoadInfo_t faceInfo = info; + +#if !defined( _X360 ) && !defined( DX_TO_GL_ABSTRACTION ) + IDirect3DCubeTexture9 *pStagingTexture = NULL; + if ( !info.m_bTextureIsLockable ) + { + IDirect3DCubeTexture9 *pTemp; + if ( Dx9Device()->CreateCubeTexture( desc.Width, iMipCount, 0, desc.Format, D3DPOOL_SYSTEMMEM, &pTemp, NULL ) != S_OK ) + { + Warning( "LoadCubeTextureFromVTF: failed to create temporary staging texture\n" ); + return; + } + faceInfo.m_pTexture = static_cast( pTemp ); + faceInfo.m_bTextureIsLockable = true; + pStagingTexture = pTemp; + } +#endif + + for ( int iMip = 0; iMip < iMipCount; ++iMip ) + { + int w, h, d; + pVTF->ComputeMipLevelDimensions( iMip, &w, &h, &d ); + faceInfo.m_nLevel = iMip; + faceInfo.m_nWidth = w; + faceInfo.m_nHeight = h; + for ( int iFace = 0; iFace < 6; ++iFace ) + { + faceInfo.m_CubeFaceID = (D3DCUBEMAP_FACES) iFace; + faceInfo.m_pSrcData = pVTF->ImageData( iVTFFrame, iFace, iMip ); + BlitTextureBits( faceInfo, 0, 0, 0 ); + } + } + +#if !defined( _X360 ) && !defined( DX_TO_GL_ABSTRACTION ) + if ( pStagingTexture ) + { + if ( Dx9Device()->UpdateTexture( pStagingTexture, pCubeTex ) != S_OK ) + { + Warning( "LoadCubeTextureFromVTF: cube UpdateTexture failed\n" ); + } + pStagingTexture->Release(); + } +#endif +} + +void LoadTextureFromVTF( TextureLoadInfo_t &info, IVTFTexture* pVTF, int iVTFFrame ) +{ + if ( !info.m_pTexture || info.m_pTexture->GetType() != D3DRTYPE_TEXTURE ) + { + Assert( 0 ); + return; + } + + IDirect3DTexture9 *pTex = static_cast( info.m_pTexture ); + + D3DSURFACE_DESC desc; + { + tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s - GetLevelDesc", __FUNCTION__ ); + if ( pTex->GetLevelDesc( 0, &desc ) != S_OK ) + { + Warning( "LoadTextureFromVTF: couldn't get texture level description\n" ); + return; + } + } + + int iMipCount = pTex->GetLevelCount(); + if ( pVTF->Depth() != 1 || pVTF->Width() != (int)desc.Width || pVTF->Height() != (int)desc.Height || pVTF->MipCount() < iMipCount || pVTF->FaceCount() <= (int)info.m_CubeFaceID ) + { + Warning( "LoadTextureFromVTF: VTF dimensions do not match texture\n" ); + return; + } + + // Info may have a cube face ID if we are falling back to 2D sphere map support + TextureLoadInfo_t mipInfo = info; + int iVTFFaceNum = info.m_CubeFaceID; + mipInfo.m_CubeFaceID = (D3DCUBEMAP_FACES)0; + +#if !defined( _X360 ) && !defined( DX_TO_GL_ABSTRACTION ) + // If blitting more than one mip level of an unlockable texture, create a temporary + // texture for all mip levels only call UpdateTexture once. For textures with + // only a single mip level, fall back on the support in BlitSurfaceBits. -henryg + IDirect3DTexture9 *pStagingTexture = NULL; + if ( !info.m_bTextureIsLockable && iMipCount > 1 ) + { + tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s - CreateSysmemTexture", __FUNCTION__ ); + + IDirect3DTexture9 *pTemp; + if ( Dx9Device()->CreateTexture( desc.Width, desc.Height, iMipCount, 0, desc.Format, D3DPOOL_SYSTEMMEM, &pTemp, NULL ) != S_OK ) + { + Warning( "LoadTextureFromVTF: failed to create temporary staging texture\n" ); + return; + } + + mipInfo.m_pTexture = static_cast( pTemp ); + mipInfo.m_bTextureIsLockable = true; + pStagingTexture = pTemp; + } +#endif + + // Get the clamped resolutions from the VTF, then apply any clamping we've done from the higher level code. + // (For example, we chop off the bottom of the mipmap pyramid at 32x32--that is reflected in iMipCount, so + // honor that here). + // int finest = 0, coarsest = 0; + int finest = 0, coarsest = iMipCount; + // pVTF->GetMipmapRange( &finest, &coarsest ); + // pVTF->ComputeMipLevelDimensions( iMip, &nWidth, &nHeight, &nDepth ); + finest = Min( finest, iMipCount - 1 ); + coarsest = Min( coarsest, iMipCount - 1 ); + Assert( finest <= coarsest && coarsest < iMipCount ); + + { + tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s - BlitTextureBits", __FUNCTION__ ); + for ( int iMip = finest; iMip <= coarsest; ++iMip ) + { + int w, h, d; + pVTF->ComputeMipLevelDimensions( iMip, &w, &h, &d ); + mipInfo.m_nLevel = iMip; + mipInfo.m_nWidth = w; + mipInfo.m_nHeight = h; + mipInfo.m_pSrcData = pVTF->ImageData( iVTFFrame, iVTFFaceNum, iMip ); + + tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s - BlitTextureBits - %d", __FUNCTION__, iMip ); + + BlitTextureBits( mipInfo, 0, 0, 0 ); + } + } + +#if !defined( _X360 ) && !defined( DX_TO_GL_ABSTRACTION ) + if ( pStagingTexture ) + { + if ( ( coarsest - finest + 1 ) == iMipCount ) + { + tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s - UpdateTexture", __FUNCTION__ ); + if ( Dx9Device()->UpdateTexture( pStagingTexture, pTex ) != S_OK ) + { + Warning( "LoadTextureFromVTF: UpdateTexture failed\n" ); + } + } + else + { + tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s - UpdateSurface", __FUNCTION__ ); + + for ( int mip = finest; mip <= coarsest; ++mip ) + { + tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s - UpdateSurface - %d", __FUNCTION__, mip ); + + IDirect3DSurface9 *pSrcSurf = NULL, + *pDstSurf = NULL; + + if ( pStagingTexture->GetSurfaceLevel( mip, &pSrcSurf ) != S_OK ) + Warning( "LoadTextureFromVTF: couldn't get surface level %d for system surface\n", mip ); + + if ( pTex->GetSurfaceLevel( mip, &pDstSurf ) != S_OK ) + Warning( "LoadTextureFromVTF: couldn't get surface level %d for dest surface\n", mip ); + + { + tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s - UpdateSurface - Call ", __FUNCTION__, mip ); + if ( !pSrcSurf || !pDstSurf || Dx9Device()->UpdateSurface( pSrcSurf, NULL, pDstSurf, NULL ) != S_OK ) + Warning( "LoadTextureFromVTF: surface update failed.\n" ); + } + + if ( pSrcSurf ) + pSrcSurf->Release(); + + if ( pDstSurf ) + pDstSurf->Release(); + } + } + + tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s - Cleanup", __FUNCTION__ ); + pStagingTexture->Release(); + } +#endif +} + + +//----------------------------------------------------------------------------- +// Upload to a sub-piece of a texture +//----------------------------------------------------------------------------- +void LoadSubTexture( TextureLoadInfo_t &info, int xOffset, int yOffset, int srcStride ) +{ + Assert( info.m_pSrcData ); + Assert( info.m_pTexture ); + +#if defined( _X360 ) + // xboxissue - not supporting subrect swizzling + Assert( !info.m_bSrcIsTiled ); +#endif + +#ifdef _DEBUG + ImageFormat format = GetImageFormat( info.m_pTexture ); + Assert( (format == FindNearestSupportedFormat(format, false, false, false )) && (format != -1) ); +#endif + + // Copy in the bits... + BlitTextureBits( info, xOffset, yOffset, srcStride ); +} + + +//----------------------------------------------------------------------------- +// Returns the size of texture memory, in MB +//----------------------------------------------------------------------------- +// Helps with startup time.. we don't use the texture memory size for anything anyways +#define DONT_CHECK_MEM + +int ComputeTextureMemorySize( const GUID &nDeviceGUID, D3DDEVTYPE deviceType ) +{ +#if defined( _X360 ) + return 0; +#elif defined( DONT_CHECK_MEM ) + return (deviceType == D3DDEVTYPE_REF) ? (64 * 1024 * 1024) : 102236160; +#else + + FileHandle_t file = g_pFullFileSystem->Open( "vidcfg.bin", "rb", "EXECUTABLE_PATH" ); + if ( file ) + { + GUID deviceId; + int texSize; + g_pFullFileSystem->Read( &deviceId, sizeof(deviceId), file ); + g_pFullFileSystem->Read( &texSize, sizeof(texSize), file ); + g_pFullFileSystem->Close( file ); + if ( nDeviceGUID == deviceId ) + { + return texSize; + } + } + // How much texture memory? + if (deviceType == D3DDEVTYPE_REF) + return 64 * 1024 * 1024; + + // Sadly, the only way to compute texture memory size + // is to allocate a crapload of textures until we can't any more + ImageFormat fmt = FindNearestSupportedFormat( IMAGE_FORMAT_BGR565, false, false, false ); + int textureSize = ShaderUtil()->GetMemRequired( 256, 256, 1, fmt, false ); + + int totalSize = 0; + CUtlVector< IDirect3DBaseTexture* > textures; + + s_bTestingVideoMemorySize = true; + while (true) + { + RECORD_COMMAND( DX8_CREATE_TEXTURE, 7 ); + RECORD_INT( textures.Count() ); + RECORD_INT( 256 ); + RECORD_INT( 256 ); + RECORD_INT( ImageLoader::ImageFormatToD3DFormat(fmt) ); + RECORD_INT( 1 ); + RECORD_INT( false ); + RECORD_INT( 1 ); + + IDirect3DBaseTexture* pTex = CreateD3DTexture( 256, 256, 1, fmt, 1, 0 ); + if (!pTex) + break; + totalSize += textureSize; + + textures.AddToTail( pTex ); + } + s_bTestingVideoMemorySize = false; + + // Free all the temp textures + for (int i = textures.Size(); --i >= 0; ) + { + RECORD_COMMAND( DX8_DESTROY_TEXTURE, 1 ); + RECORD_INT( i ); + + DestroyD3DTexture( textures[i] ); + } + + file = g_pFullFileSystem->Open( "vidcfg.bin", "wb", "EXECUTABLE_PATH" ); + if ( file ) + { + g_pFullFileSystem->Write( &nDeviceGUID, sizeof(GUID), file ); + g_pFullFileSystem->Write( &totalSize, sizeof(totalSize), file ); + g_pFullFileSystem->Close( file ); + } + + return totalSize; +#endif +} diff --git a/materialsystem/shaderapidx9/texturedx8.h b/materialsystem/shaderapidx9/texturedx8.h new file mode 100644 index 0000000..2901798 --- /dev/null +++ b/materialsystem/shaderapidx9/texturedx8.h @@ -0,0 +1,110 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// + +#ifndef TEXTUREDX8_H +#define TEXTUREDX8_H + +#ifdef _WIN32 +#pragma once +#endif + +#include "togl/rendermechanism.h" +#include "bitmap/imageformat.h" +#include "locald3dtypes.h" +#include "shaderapi/ishaderapi.h" + + +//----------------------------------------------------------------------------- +// Forward declarations +//----------------------------------------------------------------------------- +class CPixelWriter; + + +//----------------------------------------------------------------------------- +// Returns the size of texture memory +//----------------------------------------------------------------------------- +int ComputeTextureMemorySize( const GUID &nDeviceId, D3DDEVTYPE deviceType ); + + +//----------------------------------------------------------------------------- +// Texture creation +//----------------------------------------------------------------------------- +IDirect3DBaseTexture *CreateD3DTexture( int width, int height, int depth, + ImageFormat dstFormat, int numLevels, int creationFlags, char *debugLabel=NULL ); // OK to not-supply the last param + + +//----------------------------------------------------------------------------- +// Texture destruction +//----------------------------------------------------------------------------- +void DestroyD3DTexture( IDirect3DBaseTexture *pTex ); + +int GetD3DTextureRefCount( IDirect3DBaseTexture *pTex ); + + +//----------------------------------------------------------------------------- +// Texture heap methods +//----------------------------------------------------------------------------- +#if defined( _X360 ) +void SetD3DTextureImmobile( IDirect3DBaseTexture *pTex, bool bImmobile ); +void CompactTextureHeap(); +#endif + +//----------------------------------------------------------------------------- +// Stats... +//----------------------------------------------------------------------------- +int TextureCount(); + + +//----------------------------------------------------------------------------- +// Info for texture loading +//----------------------------------------------------------------------------- +struct TextureLoadInfo_t +{ + ShaderAPITextureHandle_t m_TextureHandle; + int m_nCopy; + IDirect3DBaseTexture *m_pTexture; + int m_nLevel; + D3DCUBEMAP_FACES m_CubeFaceID; + int m_nWidth; + int m_nHeight; + int16 m_nZOffset; // What z-slice of the volume texture are we loading? +#if defined( _X360 ) + bool m_bSrcIsTiled; // format may not be, but data could be + bool m_bCanConvertFormat; // allow format conversion +#else + bool m_bTextureIsLockable; +#endif + ImageFormat m_SrcFormat; + unsigned char *m_pSrcData; +}; + + +//----------------------------------------------------------------------------- +// Texture image upload +//----------------------------------------------------------------------------- +void LoadTexture( TextureLoadInfo_t &info ); +void LoadTextureFromVTF( TextureLoadInfo_t &info, IVTFTexture* pVTF, int iVTFFrame ); +void LoadCubeTextureFromVTF( TextureLoadInfo_t &info, IVTFTexture* pVTF, int iVTFFrame ); +void LoadVolumeTextureFromVTF( TextureLoadInfo_t &info, IVTFTexture* pVTF, int iVTFFrame ); + +//----------------------------------------------------------------------------- +// Upload to a sub-piece of a texture +//----------------------------------------------------------------------------- +void LoadSubTexture( TextureLoadInfo_t &info, int xOffset, int yOffset, int srcStride ); + +//----------------------------------------------------------------------------- +// Lock, unlock a texture... +//----------------------------------------------------------------------------- +bool LockTexture( ShaderAPITextureHandle_t textureHandle, int copy, IDirect3DBaseTexture* pTexture, int level, + D3DCUBEMAP_FACES cubeFaceID, int xOffset, int yOffset, int width, int height, bool bDiscard, + CPixelWriter& writer ); + +void UnlockTexture( ShaderAPITextureHandle_t textureHandle, int copy, IDirect3DBaseTexture* pTexture, int level, + D3DCUBEMAP_FACES cubeFaceID ); + +#endif // TEXTUREDX8_H \ No newline at end of file diff --git a/materialsystem/shaderapidx9/textureheap.cpp b/materialsystem/shaderapidx9/textureheap.cpp new file mode 100644 index 0000000..b2c9a66 --- /dev/null +++ b/materialsystem/shaderapidx9/textureheap.cpp @@ -0,0 +1,1258 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//============================================================================= + +#include "tier1/mempool.h" +#include "tier1/convar.h" +#include "tier1/utlmap.h" +#include "shaderapidx8.h" +#include "texturedx8.h" +#include "textureheap.h" +#include "shaderapidx8_global.h" + +#include "tier0/memdbgon.h" + +#define USE_STANDARD_ALLOCATOR +#ifdef USE_STANDARD_ALLOCATOR +#define UseStandardAllocator() (true) +#elif !defined(_RETAIL) +bool g_bUseStandardAllocator = false; +bool UseStandardAllocator() +{ + static bool bReadCommandLine; + if ( !bReadCommandLine ) + { + bReadCommandLine = true; + const char *pStr = Plat_GetCommandLine(); + if ( pStr ) + { + char tempStr[512]; + Q_strncpy( tempStr, pStr, sizeof( tempStr ) - 1 ); + tempStr[ sizeof( tempStr ) - 1 ] = 0; + _strlwr( tempStr ); + + if ( strstr( tempStr, "-notextureheap" ) ) + g_bUseStandardAllocator = true; + } + } + return g_bUseStandardAllocator; +} +#else +#define UseStandardAllocator() (false) +#endif + +#if !defined( _RELEASE ) && !defined( _RETAIL ) +#define StrongAssert( expr ) if ( (expr) ) ; else { DebuggerBreak(); } +#else +#define StrongAssert( expr ) ((void)0) +#endif + +//----------------------------------------------------------------------------- +// Get Texture HW base +//----------------------------------------------------------------------------- +void *GetD3DTextureBasePtr( IDirect3DBaseTexture* pTex ) +{ + // assumes base and mips are contiguous + return (void *)( (unsigned int)pTex->Format.BaseAddress << 12 ); +} + +class CD3DTextureAllocator +{ +public: + static void *Alloc( int bytes ) + { + DWORD attributes = MAKE_XALLOC_ATTRIBUTES( + 0, + false, + TRUE, + FALSE, + eXALLOCAllocatorId_D3D, + XALLOC_PHYSICAL_ALIGNMENT_4K, + XALLOC_MEMPROTECT_WRITECOMBINE, + FALSE, + XALLOC_MEMTYPE_PHYSICAL ); + m_nTotalAllocations++; + m_nTotalSize += AlignValue( bytes, 4096 ); + return XMemAlloc( bytes, attributes ); + } + + static void Free( void *p ) + { + DWORD attributes = MAKE_XALLOC_ATTRIBUTES( + 0, + false, + TRUE, + FALSE, + eXALLOCAllocatorId_D3D, + XALLOC_PHYSICAL_ALIGNMENT_4K, + XALLOC_MEMPROTECT_WRITECOMBINE, + FALSE, + XALLOC_MEMTYPE_PHYSICAL ); + m_nTotalAllocations--; + m_nTotalSize -= XMemSize( p, attributes ); + XMemFree( p, attributes ); + } + + static int GetAllocations() + { + return m_nTotalAllocations; + } + + static int GetSize() + { + return m_nTotalSize; + } + + static int m_nTotalSize; + static int m_nTotalAllocations; +}; + +int CD3DTextureAllocator::m_nTotalSize; +int CD3DTextureAllocator::m_nTotalAllocations; + +enum TextureAllocator_t +{ + TA_DEFAULT, + TA_MIXED, + TA_UNKNOWN, +}; + +struct THBaseInfo +{ + TextureAllocator_t m_fAllocator; + int m_TextureSize; // stored for delayed allocations +}; + +struct THInfo_t : public THBaseInfo +{ + // Mixed heap info + int nLogicalBytes; + int nBytes; + bool bFree:1; + bool bNonTexture:1; + + THInfo_t *pPrev, *pNext; +}; + +struct THFreeBlock_t +{ + THInfo_t heapInfo; + THFreeBlock_t *pPrevFree, *pNextFree; +}; + +class CXboxTexture : public IDirect3DTexture, public THInfo_t +{ +public: + CXboxTexture() + : bImmobile(false) + { + } + + bool bImmobile; + bool CanRelocate() { return ( !bImmobile && !IsBusy() ); } +}; + +class CXboxCubeTexture : public IDirect3DCubeTexture, public THBaseInfo +{ +}; + +class CXboxVolumeTexture : public IDirect3DVolumeTexture, public THBaseInfo +{ +}; + + +void SetD3DTextureImmobile( IDirect3DBaseTexture *pTexture, bool bImmobile ) +{ + if ( pTexture->GetType() == D3DRTYPE_TEXTURE ) + { + (( CXboxTexture *)pTexture)->bImmobile = bImmobile; + } +} + +CXboxTexture *GetTexture( THInfo_t *pInfo ) +{ + if ( !pInfo->bFree && !pInfo->bNonTexture ) + { + return (CXboxTexture *)((byte *)pInfo - offsetof( CXboxTexture, m_fAllocator )); + } + return NULL; +} + +inline THFreeBlock_t *GetFreeBlock( THInfo_t *pInfo ) +{ + if ( pInfo->bFree ) + { + return (THFreeBlock_t *)((byte *)pInfo - offsetof( THFreeBlock_t, heapInfo )); + } + return NULL; +} + +class CMixedTextureHeap +{ + enum + { + SIZE_ALIGNMENT = XBOX_HDD_SECTORSIZE, + MIN_BLOCK_SIZE = 1024, + }; +public: + + CMixedTextureHeap() : + m_nLogicalBytes( 0 ), + m_nActualBytes( 0 ), + m_nAllocs( 0 ), + m_nOldBytes( 0 ), + m_nNonTextureAllocs( 0 ), + m_nBytesTotal( 0 ), + m_pBase( NULL ), + m_pFirstFree( NULL ) + { + } + + void Init() + { + extern ConVar mat_texturecachesize; + MEM_ALLOC_CREDIT_("CMixedTextureHeap"); + + m_nBytesTotal = ( mat_texturecachesize.GetInt() * 1024 * 1024 ); +#if 0 + m_nBytesTotal = AlignValue( m_nBytesTotal, SIZE_ALIGNMENT ); + m_pBase = CD3DTextureAllocator::Alloc( m_nBytesTotal ); +#else + m_nBytesTotal = AlignValue( m_nBytesTotal, 16*1024*1024 ); + m_pBase = XPhysicalAlloc( m_nBytesTotal, MAXULONG_PTR, 4096, PAGE_READWRITE | PAGE_WRITECOMBINE | MEM_16MB_PAGES ); +#endif + m_pFirstFree = (THFreeBlock_t *)m_pBase; + + + m_pFirstFree->heapInfo.bFree = true; + m_pFirstFree->heapInfo.bNonTexture = false; + m_pFirstFree->heapInfo.nBytes = m_nBytesTotal; + m_pFirstFree->heapInfo.pNext = NULL; + m_pFirstFree->heapInfo.pPrev = NULL; + m_pFirstFree->pNextFree = NULL; + m_pFirstFree->pPrevFree = NULL; + + m_pLastFree = m_pFirstFree; + } + + void *Alloc( int bytes, THInfo_t *pInfo, bool bNonTexture = false ) + { + pInfo->nBytes = AlignValue( bytes, SIZE_ALIGNMENT ); + + if ( !m_pBase ) + { + Init(); + } + + if ( bNonTexture && m_nNonTextureAllocs == 0 ) + { + Compact(); + } + + void *p = FindBlock( pInfo ); + + if ( !p ) + { + p = ExpandToFindBlock( pInfo ); + } + + if ( p ) + { + pInfo->nLogicalBytes = bytes; + pInfo->bNonTexture = bNonTexture; + m_nLogicalBytes += bytes; + if ( !IsRetail() ) + { + m_nOldBytes += AlignValue( bytes, 4096 ); + } + m_nActualBytes += pInfo->nBytes; + m_nAllocs++; + + if ( bNonTexture ) + { + m_nNonTextureAllocs++; + } + } + return p; + } + + void Free( void *p, THInfo_t *pInfo ) + { + if ( !p ) + { + return; + } + + if ( !IsRetail() ) + { + m_nOldBytes -= AlignValue( pInfo->nLogicalBytes, 4096 ); + } + + if ( pInfo->bNonTexture ) + { + m_nNonTextureAllocs--; + } + + m_nLogicalBytes -= pInfo->nLogicalBytes; + m_nAllocs--; + m_nActualBytes -= pInfo->nBytes; + + THFreeBlock_t *pFree = (THFreeBlock_t *)p; + pFree->heapInfo = *pInfo; + pFree->heapInfo.bFree = true; + + AddToBlocksList( &pFree->heapInfo, pFree->heapInfo.pPrev, pFree->heapInfo.pNext ); + + pFree = MergeLeft( pFree ); + pFree = MergeRight( pFree ); + + AddToFreeList( pFree ); + + if ( pInfo->bNonTexture && m_nNonTextureAllocs == 0 ) + { + Compact(); + } + } + + int Size( void *p, THInfo_t *pInfo ) + { + return AlignValue( pInfo->nBytes, SIZE_ALIGNMENT ); + } + + bool IsOwner( void *p ) + { + return ( m_pBase && p >= m_pBase && p < (byte *)m_pBase + m_nBytesTotal ); + } + + //----------------------------------------------------- + + void *FindBlock( THInfo_t *pInfo ) + { + THFreeBlock_t *pCurrent = m_pFirstFree; + + int nBytesDesired = pInfo->nBytes; + + // Find the first block big enough to hold, then split it if appropriate + while ( pCurrent && pCurrent->heapInfo.nBytes < nBytesDesired ) + { + pCurrent = pCurrent->pNextFree; + } + + if ( pCurrent ) + { + return ClaimBlock( pCurrent, pInfo ); + } + + return NULL; + } + + void AddToFreeList( THFreeBlock_t *pFreeBlock ) + { + if ( !IsRetail() ) + { + pFreeBlock->heapInfo.nLogicalBytes = 0; + } + + if ( m_pFirstFree ) + { + THFreeBlock_t *pPrev = NULL; + THFreeBlock_t *pNext = m_pFirstFree; + + int nBytes = pFreeBlock->heapInfo.nBytes; + + while ( pNext && pNext->heapInfo.nBytes < nBytes ) + { + pPrev = pNext; + pNext = pNext->pNextFree; + } + + pFreeBlock->pPrevFree = pPrev; + pFreeBlock->pNextFree = pNext; + + if ( pPrev ) + { + pPrev->pNextFree = pFreeBlock; + } + else + { + m_pFirstFree = pFreeBlock; + } + + if ( pNext ) + { + pNext->pPrevFree = pFreeBlock; + } + else + { + m_pLastFree = pFreeBlock; + } + } + else + { + pFreeBlock->pPrevFree = pFreeBlock->pNextFree = NULL; + m_pLastFree = m_pFirstFree = pFreeBlock; + } + } + + void RemoveFromFreeList( THFreeBlock_t *pFreeBlock ) + { + if ( m_pFirstFree == pFreeBlock ) + { + m_pFirstFree = m_pFirstFree->pNextFree; + } + else if ( pFreeBlock->pPrevFree ) + { + pFreeBlock->pPrevFree->pNextFree = pFreeBlock->pNextFree; + } + + if ( m_pLastFree == pFreeBlock ) + { + m_pLastFree = pFreeBlock->pPrevFree; + } + else if ( pFreeBlock->pNextFree ) + { + pFreeBlock->pNextFree->pPrevFree = pFreeBlock->pPrevFree; + } + + pFreeBlock->pPrevFree = pFreeBlock->pNextFree = NULL; + } + + THFreeBlock_t *GetLastFree() + { + return m_pLastFree; + } + + void AddToBlocksList( THInfo_t *pBlock, THInfo_t *pPrev, THInfo_t *pNext ) + { + if ( pPrev ) + { + pPrev->pNext = pBlock; + } + + if ( pNext) + { + pNext->pPrev = pBlock; + } + + pBlock->pPrev = pPrev; + pBlock->pNext = pNext; + } + + void RemoveFromBlocksList( THInfo_t *pBlock ) + { + if ( pBlock->pPrev ) + { + pBlock->pPrev->pNext = pBlock->pNext; + } + + if ( pBlock->pNext ) + { + pBlock->pNext->pPrev = pBlock->pPrev; + } + } + + //----------------------------------------------------- + + void *ClaimBlock( THFreeBlock_t *pFreeBlock, THInfo_t *pInfo ) + { + RemoveFromFreeList( pFreeBlock ); + + int nBytesDesired = pInfo->nBytes; + int nBytesRemainder = pFreeBlock->heapInfo.nBytes - nBytesDesired; + *pInfo = pFreeBlock->heapInfo; + pInfo->bFree = false; + pInfo->bNonTexture = false; + if ( nBytesRemainder >= MIN_BLOCK_SIZE ) + { + pInfo->nBytes = nBytesDesired; + + THFreeBlock_t *pRemainder = (THFreeBlock_t *)(((byte *)(pFreeBlock)) + nBytesDesired); + pRemainder->heapInfo.bFree = true; + pRemainder->heapInfo.nBytes = nBytesRemainder; + + AddToBlocksList( &pRemainder->heapInfo, pInfo, pInfo->pNext ); + AddToFreeList( pRemainder ); + } + AddToBlocksList( pInfo, pInfo->pPrev, pInfo->pNext ); + return pFreeBlock; + } + + THFreeBlock_t *MergeLeft( THFreeBlock_t *pFree ) + { + THInfo_t *pPrev = pFree->heapInfo.pPrev; + if ( pPrev && pPrev->bFree ) + { + pPrev->nBytes += pFree->heapInfo.nBytes; + RemoveFromBlocksList( &pFree->heapInfo ); + pFree = GetFreeBlock( pPrev ); + RemoveFromFreeList( pFree ); + } + return pFree; + } + + THFreeBlock_t *MergeRight( THFreeBlock_t *pFree ) + { + THInfo_t *pNext = pFree->heapInfo.pNext; + if ( pNext && pNext->bFree ) + { + pFree->heapInfo.nBytes += pNext->nBytes; + RemoveFromBlocksList( pNext ); + RemoveFromFreeList( GetFreeBlock( pNext ) ); + } + return pFree; + } + + //----------------------------------------------------- + + bool GetExpansionList( THFreeBlock_t *pFreeBlock, THInfo_t **ppStart, THInfo_t **ppEnd, int depth = 1 ) + { + THInfo_t *pStart; + THInfo_t *pEnd; + int i; + + pStart = &pFreeBlock->heapInfo; + pEnd = &pFreeBlock->heapInfo; + + if ( m_nNonTextureAllocs > 0 ) + { + return false; + } + + // Walk backwards to start of expansion + i = depth; + while ( i > 0 && pStart->pPrev) + { + THInfo_t *pScan = pStart->pPrev; + + while ( i > 0 && pScan && !pScan->bFree && GetTexture( pScan )->CanRelocate() ) + { + pScan = pScan->pPrev; + i--; + } + + if ( !pScan || !pScan->bFree ) + { + break; + } + + pStart = pScan; + } + + // Walk forwards to start of expansion + i = depth; + while ( i > 0 && pEnd->pNext) + { + THInfo_t *pScan = pStart->pNext; + + while ( i > 0 && pScan && !pScan->bFree && GetTexture( pScan )->CanRelocate() ) + { + pScan = pScan->pNext; + i--; + } + + if ( !pScan || !pScan->bFree ) + { + break; + } + + pEnd = pScan; + } + + *ppStart = pStart; + *ppEnd = pEnd; + + return ( pStart != pEnd ); + } + + THFreeBlock_t *CompactExpansionList( THInfo_t *pStart, THInfo_t *pEnd ) + { +// X360TBD: +Assert( 0 ); +return NULL; +#if 0 +#ifdef TH_PARANOID + Validate(); +#endif + StrongAssert( pStart->bFree ); + StrongAssert( pEnd->bFree ); + byte *pNextBlock = (byte *)pStart; + + THInfo_t *pTextureBlock = pStart; + THInfo_t *pLastBlock = pStart->pPrev; + + while ( pTextureBlock != pEnd ) + { + CXboxTexture *pTexture = GetTexture( pTextureBlock ); + // If it's a texture, move it and thread it on. Otherwise, discard it + if ( pTexture ) + { + void *pTextureBits = GetD3DTextureBasePtr( pTexture ); + int nBytes = pTextureBlock->nBytes; + + if ( pNextBlock + nBytes <= pTextureBits) + { + memcpy( pNextBlock, pTextureBits, nBytes ); + } + else + { + memmove( pNextBlock, pTextureBits, nBytes ); + } + + pTexture->Data = 0; + pTexture->Register( pNextBlock ); + + pNextBlock += nBytes; + if ( pLastBlock) + { + pLastBlock->pNext = pTextureBlock; + } + pTextureBlock->pPrev = pLastBlock; + pLastBlock = pTextureBlock; + } + else + { + StrongAssert( pTextureBlock->bFree ); + RemoveFromFreeList( GetFreeBlock( pTextureBlock ) ); + } + pTextureBlock = pTextureBlock->pNext; + } + + RemoveFromFreeList( GetFreeBlock( pEnd ) ); + + // Make a new block and fix up the block lists + THFreeBlock_t *pFreeBlock = (THFreeBlock_t *)pNextBlock; + pFreeBlock->heapInfo.pPrev = pLastBlock; + pLastBlock->pNext = &pFreeBlock->heapInfo; + pFreeBlock->heapInfo.pNext = pEnd->pNext; + if ( pEnd->pNext ) + { + pEnd->pNext->pPrev = &pFreeBlock->heapInfo; + } + pFreeBlock->heapInfo.bFree = true; + pFreeBlock->heapInfo.nBytes = ( (byte *)pEnd - pNextBlock ) + pEnd->nBytes; + + AddToFreeList( pFreeBlock ); + +#ifdef TH_PARANOID + Validate(); +#endif + return pFreeBlock; +#endif + } + + THFreeBlock_t *ExpandBlock( THFreeBlock_t *pFreeBlock, int depth = 1 ) + { + THInfo_t *pStart; + THInfo_t *pEnd; + + if ( GetExpansionList( pFreeBlock, &pStart, &pEnd, depth ) ) + { + return CompactExpansionList( pStart, pEnd ); + } + + return pFreeBlock; + } + + THFreeBlock_t *ExpandBlockToFit( THFreeBlock_t *pFreeBlock, unsigned bytes ) + { + if ( pFreeBlock ) + { + THInfo_t *pStart; + THInfo_t *pEnd; + + if ( GetExpansionList( pFreeBlock, &pStart, &pEnd, 2 ) ) + { + unsigned sum = 0; + THInfo_t *pCurrent = pStart; + while( pCurrent != pEnd->pNext ) + { + if ( pCurrent->bFree ) + { + sum += pCurrent->nBytes; + } + pCurrent = pCurrent->pNext; + } + + if ( sum >= bytes ) + { + pFreeBlock = CompactExpansionList( pStart, pEnd ); + } + } + } + + return pFreeBlock; + } + + void *ExpandToFindBlock( THInfo_t *pInfo ) + { + THFreeBlock_t *pFreeBlock = ExpandBlockToFit( GetLastFree(), pInfo->nBytes ); + if ( pFreeBlock && pFreeBlock->heapInfo.nBytes >= pInfo->nBytes ) + { + return ClaimBlock( pFreeBlock, pInfo ); + } + return NULL; + } + + void Compact() + { + if ( m_nNonTextureAllocs > 0 ) + { + return; + } + + for (;;) + { + THFreeBlock_t *pCurrent = m_pFirstFree; + THFreeBlock_t *pNew; + while ( pCurrent ) + { + int nBytesOld = pCurrent->heapInfo.nBytes; + pNew = ExpandBlock( pCurrent, 999999 ); + + if ( pNew != pCurrent || pNew->heapInfo.nBytes != nBytesOld ) + { +#ifdef TH_PARANOID + Validate(); +#endif + break; + } + +#ifdef TH_PARANOID + pNew = ExpandBlock( pCurrent, 999999 ); + StrongAssert( pNew == pCurrent && pNew->heapInfo.nBytes == nBytesOld ); +#endif + + pCurrent = pCurrent->pNextFree; + } + + if ( !pCurrent ) + { + break; + } + } + } + + void Validate() + { + if ( !m_pFirstFree ) + { + return; + } + + if ( m_nNonTextureAllocs > 0 ) + { + return; + } + + THInfo_t *pLast = NULL; + THInfo_t *pInfo = &m_pFirstFree->heapInfo; + + while ( pInfo->pPrev ) + { + pInfo = pInfo->pPrev; + } + + void *pNextExpectedAddress = m_pBase; + + while ( pInfo ) + { + byte *pCurrentAddress = (byte *)(( pInfo->bFree ) ? GetFreeBlock( pInfo ) : GetD3DTextureBasePtr( GetTexture( pInfo ) ) ); + StrongAssert( pCurrentAddress == pNextExpectedAddress ); + StrongAssert( pInfo->pPrev == pLast ); + pNextExpectedAddress = pCurrentAddress + pInfo->nBytes; + pLast = pInfo; + pInfo = pInfo->pNext; + } + + THFreeBlock_t *pFree = m_pFirstFree; + THFreeBlock_t *pLastFree = NULL; + int nBytesHeap = XPhysicalSize( m_pBase ); + + while ( pFree ) + { + StrongAssert( pFree->pPrevFree == pLastFree ); + StrongAssert( (void *)pFree >= m_pBase && (void *)pFree < (byte *)m_pBase + nBytesHeap ); + StrongAssert( !pFree->pPrevFree || ( (void *)pFree->pPrevFree >= m_pBase && (void *)pFree->pPrevFree < (byte *)m_pBase + nBytesHeap ) ); + StrongAssert( !pFree->pNextFree || ( (void *)pFree->pNextFree >= m_pBase && (void *)pFree->pNextFree < (byte *)m_pBase + nBytesHeap ) ); + StrongAssert( !pFree->pPrevFree || pFree->pPrevFree->heapInfo.nBytes <= pFree->heapInfo.nBytes ); + pLastFree = pFree; + pFree = pFree->pNextFree; + } + } + + //----------------------------------------------------- + + THFreeBlock_t *m_pFirstFree; + THFreeBlock_t *m_pLastFree; + void *m_pBase; + + int m_nLogicalBytes; + int m_nActualBytes; + int m_nAllocs; + int m_nOldBytes; + int m_nNonTextureAllocs; + int m_nBytesTotal; +}; + +//----------------------------------------------------------------------------- + +inline TextureAllocator_t GetTextureAllocator( IDirect3DBaseTexture9 *pTexture ) +{ + return ( pTexture->GetType() == D3DRTYPE_CUBETEXTURE ) ? (( CXboxCubeTexture *)pTexture)->m_fAllocator : (( CXboxTexture *)pTexture)->m_fAllocator; +} + +//----------------------------------------------------------------------------- + +CMixedTextureHeap g_MixedTextureHeap; + +CON_COMMAND( mat_texture_heap_stats, "" ) +{ + if ( UseStandardAllocator() ) + { + Msg( "Texture heap stats: (Standard Allocator)\n" ); + Msg( "Allocations:%d Size:%d\n", CD3DTextureAllocator::GetAllocations(), CD3DTextureAllocator::GetSize() ); + } + else + { + Msg( "Texture heap stats:\n" ); + Msg( " Mixed textures: %dk/%dk allocated in %d textures\n", g_MixedTextureHeap.m_nLogicalBytes/1024, g_MixedTextureHeap.m_nActualBytes/1024, g_MixedTextureHeap.m_nAllocs ); + float oldFootprint = g_MixedTextureHeap.m_nOldBytes; + float newFootprint = g_MixedTextureHeap.m_nActualBytes; + Msg( "\n Old: %.3fmb, New: %.3fmb\n", oldFootprint / (1024.0*1024.0), newFootprint / (1024.0*1024.0) ); + } +} + +CON_COMMAND( mat_texture_heap_compact, "" ) +{ + Msg( "Validating texture heap...\n" ); + g_MixedTextureHeap.Validate(); + Msg( "Compacting texture heap...\n" ); + unsigned oldLargest = ( g_MixedTextureHeap.GetLastFree() ) ? g_MixedTextureHeap.GetLastFree()->heapInfo.nBytes : 0; + g_MixedTextureHeap.Compact(); + unsigned newLargest = ( g_MixedTextureHeap.GetLastFree() ) ? g_MixedTextureHeap.GetLastFree()->heapInfo.nBytes : 0; + + Msg( "\n Old largest block: %.3fk, New largest block: %.3fk\n\n", oldLargest / 1024.0, newLargest / 1024.0 ); + + Msg( "Validating texture heap...\n" ); + g_MixedTextureHeap.Validate(); + Msg( "Done.\n" ); +} + +//----------------------------------------------------------------------------- +// Nasty back doors +//----------------------------------------------------------------------------- + +void CompactTextureHeap() +{ + unsigned oldLargest = ( g_MixedTextureHeap.GetLastFree() ) ? g_MixedTextureHeap.GetLastFree()->heapInfo.nBytes : 0; + g_MixedTextureHeap.Compact(); + unsigned newLargest = ( g_MixedTextureHeap.GetLastFree() ) ? g_MixedTextureHeap.GetLastFree()->heapInfo.nBytes : 0; + + DevMsg( "Compacted texture heap. Old largest block: %.3fk, New largest block: %.3fk\n", oldLargest / 1024.0, newLargest / 1024.0 ); +} + +CTextureHeap g_TextureHeap; + +//----------------------------------------------------------------------------- +// Build and alloc a texture resource +//----------------------------------------------------------------------------- +IDirect3DTexture *CTextureHeap::AllocTexture( int width, int height, int levels, DWORD usage, D3DFORMAT d3dFormat, bool bFallback, bool bNoD3DMemory ) +{ + CXboxTexture* pD3DTexture = new CXboxTexture; + + // create a texture with contiguous mips and packed tails + DWORD dwTextureSize = XGSetTextureHeaderEx( + width, + height, + levels, + usage, + d3dFormat, + 0, + 0, + 0, + XGHEADER_CONTIGUOUS_MIP_OFFSET, + 0, + pD3DTexture, + NULL, + NULL ); + + // based on "Xbox 360 Texture Storage" + // can truncate the terminal tile using packed tails + // the terminal tile must be at 32x32 or 16x16 packed + if ( width == height && levels != 0 ) + { + int terminalWidth = width >> (levels - 1); + if ( d3dFormat == D3DFMT_DXT1 ) + { + if ( terminalWidth <= 32 ) + { + dwTextureSize -= 4*1024; + } + } + else if ( d3dFormat == D3DFMT_DXT5 ) + { + if ( terminalWidth == 32 ) + { + dwTextureSize -= 8*1024; + } + else if ( terminalWidth <= 16 ) + { + dwTextureSize -= 12*1024; + } + } + } + + pD3DTexture->m_TextureSize = dwTextureSize; + + if ( !bFallback && bNoD3DMemory ) + { + pD3DTexture->m_fAllocator = TA_UNKNOWN; + return pD3DTexture; + } + + void *pBuffer; + if ( UseStandardAllocator() ) + { + MEM_ALLOC_CREDIT_( __FILE__ ": Standard D3D" ); + pBuffer = CD3DTextureAllocator::Alloc( dwTextureSize ); + pD3DTexture->m_fAllocator = TA_DEFAULT; + } + else + { + MEM_ALLOC_CREDIT_( __FILE__ ": Mixed texture" ); + pBuffer = g_MixedTextureHeap.Alloc( dwTextureSize, pD3DTexture ); + if ( pBuffer ) + { + pD3DTexture->m_fAllocator = TA_MIXED; + } + else + { + g_MixedTextureHeap.Compact(); + pBuffer = g_MixedTextureHeap.Alloc( dwTextureSize, pD3DTexture ); + if ( pBuffer ) + { + pD3DTexture->m_fAllocator = TA_MIXED; + } + else + { + pBuffer = CD3DTextureAllocator::Alloc( dwTextureSize ); + pD3DTexture->m_fAllocator = TA_DEFAULT; + } + } + } + + if ( !pBuffer ) + { + delete pD3DTexture; + return NULL; + } + + XGOffsetResourceAddress( pD3DTexture, pBuffer ); + + return pD3DTexture; +} + +//----------------------------------------------------------------------------- +// Build and alloc a cube texture resource +//----------------------------------------------------------------------------- +IDirect3DCubeTexture *CTextureHeap::AllocCubeTexture( int width, int levels, DWORD usage, D3DFORMAT d3dFormat, bool bFallback, bool bNoD3DMemory ) +{ + CXboxCubeTexture* pD3DCubeTexture = new CXboxCubeTexture; + + // create a cube texture with contiguous mips and packed tails + DWORD dwTextureSize = XGSetCubeTextureHeaderEx( + width, + levels, + usage, + d3dFormat, + 0, + 0, + 0, + XGHEADER_CONTIGUOUS_MIP_OFFSET, + pD3DCubeTexture, + NULL, + NULL ); + pD3DCubeTexture->m_TextureSize = dwTextureSize; + + if ( !bFallback && bNoD3DMemory ) + { + pD3DCubeTexture->m_fAllocator = TA_UNKNOWN; + return pD3DCubeTexture; + } + + void *pBits; + if ( UseStandardAllocator() ) + { + MEM_ALLOC_CREDIT_( __FILE__ ": Cubemap standard D3D" ); + pBits = CD3DTextureAllocator::Alloc( dwTextureSize ); + pD3DCubeTexture->m_fAllocator = TA_DEFAULT; + } + else + { + // @todo: switch to texture heap + MEM_ALLOC_CREDIT_( __FILE__ ": Odd sized cubemap textures" ); + // Really only happens with environment map + pBits = CD3DTextureAllocator::Alloc( dwTextureSize ); + pD3DCubeTexture->m_fAllocator = TA_DEFAULT; + } + + if ( !pBits ) + { + delete pD3DCubeTexture; + return NULL; + } + + XGOffsetResourceAddress( pD3DCubeTexture, pBits ); + + return pD3DCubeTexture; +} + +//----------------------------------------------------------------------------- +// Allocate an Volume Texture +//----------------------------------------------------------------------------- +IDirect3DVolumeTexture *CTextureHeap::AllocVolumeTexture( int width, int height, int depth, int levels, DWORD usage, D3DFORMAT d3dFormat ) +{ + CXboxVolumeTexture *pD3DVolumeTexture = new CXboxVolumeTexture; + + // create a cube texture with contiguous mips and packed tails + DWORD dwTextureSize = XGSetVolumeTextureHeaderEx( + width, + height, + depth, + levels, + usage, + d3dFormat, + 0, + 0, + 0, + XGHEADER_CONTIGUOUS_MIP_OFFSET, + pD3DVolumeTexture, + NULL, + NULL ); + + void *pBits; + + MEM_ALLOC_CREDIT_( __FILE__ ": Volume standard D3D" ); + + pBits = CD3DTextureAllocator::Alloc( dwTextureSize ); + pD3DVolumeTexture->m_fAllocator = TA_DEFAULT; + pD3DVolumeTexture->m_TextureSize = dwTextureSize; + + if ( !pBits ) + { + delete pD3DVolumeTexture; + return NULL; + } + + XGOffsetResourceAddress( pD3DVolumeTexture, pBits ); + + return pD3DVolumeTexture; +} + +//----------------------------------------------------------------------------- +// Get current backbuffer multisample type (used in AllocRenderTargetSurface() ) +//----------------------------------------------------------------------------- +D3DMULTISAMPLE_TYPE CTextureHeap::GetBackBufferMultiSampleType() +{ + int backWidth, backHeight; + ShaderAPI()->GetBackBufferDimensions( backWidth, backHeight ); + + // 2xMSAA at 640x480 and 848x480 are the only supported multisample mode on 360 (2xMSAA for 720p would + // use predicated tiling, which would require a rewrite of *all* our render target code) + // FIXME: shuffle the EDRAM surfaces to allow 4xMSAA for standard def + // (they would overlap & trash each other with the current allocation scheme) + D3DMULTISAMPLE_TYPE backBufferMultiSampleType = g_pShaderDevice->IsAAEnabled() ? D3DMULTISAMPLE_2_SAMPLES : D3DMULTISAMPLE_NONE; + Assert( ( g_pShaderDevice->IsAAEnabled() == false ) || (backHeight == 480) ); + + return backBufferMultiSampleType; +} + +//----------------------------------------------------------------------------- +// Allocate an EDRAM surface +//----------------------------------------------------------------------------- +IDirect3DSurface *CTextureHeap::AllocRenderTargetSurface( int width, int height, D3DFORMAT d3dFormat, bool bMultiSample, int base ) +{ + // render target surfaces don't need to exist simultaneously + // force their allocations to overlap at the end of back buffer and zbuffer + // this should leave 3MB (of 10) free assuming 1280x720 (and 5MB with 640x480@2xMSAA) + D3DMULTISAMPLE_TYPE backBufferMultiSampleType = GetBackBufferMultiSampleType(); + D3DMULTISAMPLE_TYPE multiSampleType = bMultiSample ? backBufferMultiSampleType : D3DMULTISAMPLE_NONE; + if ( base < 0 ) + { + int backWidth, backHeight; + ShaderAPI()->GetBackBufferDimensions( backWidth, backHeight ); + D3DFORMAT backBufferFormat = ImageLoader::ImageFormatToD3DFormat( g_pShaderDevice->GetBackBufferFormat() ); + base = 2*XGSurfaceSize( backWidth, backHeight, backBufferFormat, backBufferMultiSampleType ); + } + + D3DSURFACE_PARAMETERS surfParameters; + surfParameters.Base = base; + surfParameters.ColorExpBias = 0; + + if ( ( d3dFormat == D3DFMT_D24FS8 ) || ( d3dFormat == D3DFMT_D24S8 ) || ( d3dFormat == D3DFMT_D16 ) ) + { + surfParameters.HierarchicalZBase = 0; + if ( ( surfParameters.HierarchicalZBase + XGHierarchicalZSize( width, height, multiSampleType ) ) > GPU_HIERARCHICAL_Z_TILES ) + { + // overflow, can't hold the tiles so disable + surfParameters.HierarchicalZBase = 0xFFFFFFFF; + } + } + else + { + // not using + surfParameters.HierarchicalZBase = 0xFFFFFFFF; + } + + HRESULT hr; + IDirect3DSurface9 *pSurface = NULL; + hr = Dx9Device()->CreateRenderTarget( width, height, d3dFormat, multiSampleType, 0, FALSE, &pSurface, &surfParameters ); + Assert( !FAILED( hr ) ); + + return pSurface; +} + +//----------------------------------------------------------------------------- +// Perform the real d3d allocation, returns true if succesful, false otherwise. +// Only valid for a texture created with no d3d bits, otherwise no-op. +//----------------------------------------------------------------------------- +bool CTextureHeap::AllocD3DMemory( IDirect3DBaseTexture *pD3DTexture ) +{ + if ( !pD3DTexture ) + { + return false; + } + + if ( pD3DTexture->GetType() == D3DRTYPE_SURFACE ) + { + // there are no d3d bits for a surface + return false; + } + + void *pBits = GetD3DTextureBasePtr( pD3DTexture ); + if ( pBits ) + { + // already have d3d bits + return true; + } + + if ( pD3DTexture->GetType() == D3DRTYPE_TEXTURE ) + { + MEM_ALLOC_CREDIT_( __FILE__ ": Standard D3D" ); + pBits = CD3DTextureAllocator::Alloc( ((CXboxTexture *)pD3DTexture)->m_TextureSize ); + ((CXboxTexture *)pD3DTexture)->m_fAllocator = TA_DEFAULT; + XGOffsetResourceAddress( (CXboxTexture *)pD3DTexture, pBits ); + return true; + } + else if ( pD3DTexture->GetType() == D3DRTYPE_CUBETEXTURE ) + { + MEM_ALLOC_CREDIT_( __FILE__ ": Cubemap standard D3D" ); + pBits = CD3DTextureAllocator::Alloc( ((CXboxCubeTexture *)pD3DTexture)->m_TextureSize ); + ((CXboxCubeTexture *)pD3DTexture)->m_fAllocator = TA_DEFAULT; + XGOffsetResourceAddress( (CXboxCubeTexture *)pD3DTexture, pBits ); + return true; + } + + return false; +} + +//----------------------------------------------------------------------------- +// Release the allocated store +//----------------------------------------------------------------------------- +void CTextureHeap::FreeTexture( IDirect3DBaseTexture *pD3DTexture ) +{ + if ( !pD3DTexture ) + { + return; + } + + if ( pD3DTexture->GetType() == D3DRTYPE_SURFACE ) + { + // texture heap doesn't own render target surfaces + // allow callers to call through for less higher level detection + int ref = ((IDirect3DSurface*)pD3DTexture)->Release(); + Assert( ref == 0 ); + ref = ref; // Quiet "unused variable" warning in release + return; + } + else + { + byte *pBits = (byte *)GetD3DTextureBasePtr( pD3DTexture ); + if ( pBits ) + { + switch ( GetTextureAllocator( pD3DTexture ) ) + { + case TA_DEFAULT: + CD3DTextureAllocator::Free( pBits ); + break; + + case TA_MIXED: + g_MixedTextureHeap.Free( pBits, ((CXboxTexture *)pD3DTexture) ); + break; + } + } + } + + if ( pD3DTexture->GetType() == D3DRTYPE_TEXTURE ) + { + delete (CXboxTexture *)pD3DTexture; + } + else if ( pD3DTexture->GetType() == D3DRTYPE_VOLUMETEXTURE ) + { + delete (CXboxVolumeTexture *)pD3DTexture; + } + else if ( pD3DTexture->GetType() == D3DRTYPE_CUBETEXTURE ) + { + delete (CXboxCubeTexture *)pD3DTexture; + } +} + +//----------------------------------------------------------------------------- +// Returns the allocated footprint +//----------------------------------------------------------------------------- +int CTextureHeap::GetSize( IDirect3DBaseTexture *pD3DTexture ) +{ + if( pD3DTexture == NULL ) + return 0; + + if ( pD3DTexture->GetType() == D3DRTYPE_SURFACE ) + { + D3DSURFACE_DESC surfaceDesc; + HRESULT hr = ((IDirect3DSurface*)pD3DTexture)->GetDesc( &surfaceDesc ); + Assert( !FAILED( hr ) ); + hr = hr; // Quiet "unused variable" warning in release + + int size = ImageLoader::GetMemRequired( + surfaceDesc.Width, + surfaceDesc.Height, + 0, + ImageLoader::D3DFormatToImageFormat( surfaceDesc.Format ), + false ); + + return size; + } + else if ( pD3DTexture->GetType() == D3DRTYPE_TEXTURE ) + { + return ((CXboxTexture *)pD3DTexture)->m_TextureSize; + } + else if ( pD3DTexture->GetType() == D3DRTYPE_CUBETEXTURE ) + { + return ((CXboxCubeTexture *)pD3DTexture)->m_TextureSize; + } + else if ( pD3DTexture->GetType() == D3DRTYPE_VOLUMETEXTURE ) + { + return ((CXboxVolumeTexture *)pD3DTexture)->m_TextureSize; + } + + return 0; +} + +//----------------------------------------------------------------------------- +// Crunch the pools +//----------------------------------------------------------------------------- +void CTextureHeap::Compact() +{ + g_MixedTextureHeap.Compact(); +} diff --git a/materialsystem/shaderapidx9/textureheap.h b/materialsystem/shaderapidx9/textureheap.h new file mode 100644 index 0000000..ceeb3a9 --- /dev/null +++ b/materialsystem/shaderapidx9/textureheap.h @@ -0,0 +1,42 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//============================================================================= + +#ifndef TEXTUREHEAP_H +#define TEXTUREHEAP_H + +#if defined( _X360 ) + +#include "locald3dtypes.h" + +class CTextureHeap +{ +public: + IDirect3DTexture *AllocTexture( int width, int height, int levels, DWORD usage, D3DFORMAT format, bool bFallback, bool bNoD3DMemory ); + IDirect3DCubeTexture *AllocCubeTexture( int width, int levels, DWORD usage, D3DFORMAT format, bool bFallback, bool bNoD3DMemory ); + IDirect3DVolumeTexture *AllocVolumeTexture( int width, int height, int depth, int levels, DWORD usage, D3DFORMAT format ); + IDirect3DSurface *AllocRenderTargetSurface( int width, int height, D3DFORMAT format, bool bMultiSample = false , int base = -1); + + // Perform the real d3d allocation, returns true if succesful, false otherwise. + // Only valid for a texture created with no d3d bits, otherwise no-op. + bool AllocD3DMemory( IDirect3DBaseTexture *pTexture ); + + // Release header and d3d bits. + void FreeTexture( IDirect3DBaseTexture *pTexture ); + + // Returns the amount of memory needed or allocated for the texture. + int GetSize( IDirect3DBaseTexture *pTexture ); + + // Crunch the heap. + void Compact(); + + // Get current backbuffer multisample type + D3DMULTISAMPLE_TYPE GetBackBufferMultiSampleType(); +}; + +extern CTextureHeap g_TextureHeap; + +#endif +#endif // TEXTUREHEAP_H diff --git a/materialsystem/shaderapidx9/vertexdecl.cpp b/materialsystem/shaderapidx9/vertexdecl.cpp new file mode 100644 index 0000000..3d70dbc --- /dev/null +++ b/materialsystem/shaderapidx9/vertexdecl.cpp @@ -0,0 +1,576 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//===========================================================================// + +#undef PROTECTED_THINGS_ENABLE +#include "vertexdecl.h" // this includes inside the dx headers +#define PROTECTED_THINGS_ENABLE +#include "materialsystem/imaterialsystemhardwareconfig.h" +#include "shaderapidx8_global.h" +#include "tier0/dbg.h" +#include "utlrbtree.h" +#include "recording.h" +#include "tier1/strtools.h" +#include "tier0/vprof.h" +#include "materialsystem/imesh.h" +#include "shaderdevicedx8.h" + +// NOTE: This has to be the last file included! +#include "tier0/memdbgon.h" + +//----------------------------------------------------------------------------- +// Computes the DX8 vertex specification +//----------------------------------------------------------------------------- +static const char *DeclTypeToString( BYTE type ) +{ + switch( type ) + { + case D3DDECLTYPE_FLOAT1: + return "D3DDECLTYPE_FLOAT1"; + case D3DDECLTYPE_FLOAT2: + return "D3DDECLTYPE_FLOAT2"; + case D3DDECLTYPE_FLOAT3: + return "D3DDECLTYPE_FLOAT3"; + case D3DDECLTYPE_FLOAT4: + return "D3DDECLTYPE_FLOAT4"; + case D3DDECLTYPE_D3DCOLOR: + return "D3DDECLTYPE_D3DCOLOR"; + case D3DDECLTYPE_UBYTE4: + return "D3DDECLTYPE_UBYTE4"; + case D3DDECLTYPE_SHORT2: + return "D3DDECLTYPE_SHORT2"; + case D3DDECLTYPE_SHORT4: + return "D3DDECLTYPE_SHORT4"; + case D3DDECLTYPE_UBYTE4N: + return "D3DDECLTYPE_UBYTE4N"; + case D3DDECLTYPE_SHORT2N: + return "D3DDECLTYPE_SHORT2N"; + case D3DDECLTYPE_SHORT4N: + return "D3DDECLTYPE_SHORT4N"; + case D3DDECLTYPE_USHORT2N: + return "D3DDECLTYPE_USHORT2N"; + case D3DDECLTYPE_USHORT4N: + return "D3DDECLTYPE_USHORT4N"; + case D3DDECLTYPE_UDEC3: + return "D3DDECLTYPE_UDEC3"; + case D3DDECLTYPE_DEC3N: + return "D3DDECLTYPE_DEC3N"; + case D3DDECLTYPE_FLOAT16_2: + return "D3DDECLTYPE_FLOAT16_2"; + case D3DDECLTYPE_FLOAT16_4: + return "D3DDECLTYPE_FLOAT16_4"; + default: + Assert( 0 ); + return "ERROR"; + } +} + +static const char *DeclMethodToString( BYTE method ) +{ + switch( method ) + { + case D3DDECLMETHOD_DEFAULT: + return "D3DDECLMETHOD_DEFAULT"; + case D3DDECLMETHOD_PARTIALU: + return "D3DDECLMETHOD_PARTIALU"; + case D3DDECLMETHOD_PARTIALV: + return "D3DDECLMETHOD_PARTIALV"; + case D3DDECLMETHOD_CROSSUV: + return "D3DDECLMETHOD_CROSSUV"; + case D3DDECLMETHOD_UV: + return "D3DDECLMETHOD_UV"; + case D3DDECLMETHOD_LOOKUP: + return "D3DDECLMETHOD_LOOKUP"; + case D3DDECLMETHOD_LOOKUPPRESAMPLED: + return "D3DDECLMETHOD_LOOKUPPRESAMPLED"; + default: + Assert( 0 ); + return "ERROR"; + } +} + +static const char *DeclUsageToString( BYTE usage ) +{ + switch( usage ) + { + case D3DDECLUSAGE_POSITION: + return "D3DDECLUSAGE_POSITION"; + case D3DDECLUSAGE_BLENDWEIGHT: + return "D3DDECLUSAGE_BLENDWEIGHT"; + case D3DDECLUSAGE_BLENDINDICES: + return "D3DDECLUSAGE_BLENDINDICES"; + case D3DDECLUSAGE_NORMAL: + return "D3DDECLUSAGE_NORMAL"; + case D3DDECLUSAGE_PSIZE: + return "D3DDECLUSAGE_PSIZE"; + case D3DDECLUSAGE_COLOR: + return "D3DDECLUSAGE_COLOR"; + case D3DDECLUSAGE_TEXCOORD: + return "D3DDECLUSAGE_TEXCOORD"; + case D3DDECLUSAGE_TANGENT: + return "D3DDECLUSAGE_TANGENT"; + case D3DDECLUSAGE_BINORMAL: + return "D3DDECLUSAGE_BINORMAL"; + case D3DDECLUSAGE_TESSFACTOR: + return "D3DDECLUSAGE_TESSFACTOR"; +// case D3DDECLUSAGE_POSITIONTL: +// return "D3DDECLUSAGE_POSITIONTL"; + default: + Assert( 0 ); + return "ERROR"; + } +} + +static D3DDECLTYPE VertexElementToDeclType( VertexElement_t element, VertexCompressionType_t compressionType ) +{ + Detect_VertexElement_t_Changes( element ); + + if ( compressionType == VERTEX_COMPRESSION_ON ) + { + // Compressed-vertex element sizes + switch ( element ) + { +#if ( COMPRESSED_NORMALS_TYPE == COMPRESSED_NORMALS_SEPARATETANGENTS_SHORT2 ) + case VERTEX_ELEMENT_NORMAL: return D3DDECLTYPE_SHORT2; + case VERTEX_ELEMENT_USERDATA4: return D3DDECLTYPE_SHORT2; +#else //( COMPRESSED_NORMALS_TYPE == COMPRESSED_NORMALS_COMBINEDTANGENTS_UBYTE4 ) + case VERTEX_ELEMENT_NORMAL: return D3DDECLTYPE_UBYTE4; + case VERTEX_ELEMENT_USERDATA4: return D3DDECLTYPE_UBYTE4; +#endif + case VERTEX_ELEMENT_BONEWEIGHTS1: return D3DDECLTYPE_SHORT2; + case VERTEX_ELEMENT_BONEWEIGHTS2: return D3DDECLTYPE_SHORT2; + default: + break; + } + } + + // Uncompressed-vertex element sizes + switch ( element ) + { + case VERTEX_ELEMENT_POSITION: return D3DDECLTYPE_FLOAT3; + case VERTEX_ELEMENT_NORMAL: return D3DDECLTYPE_FLOAT3; + case VERTEX_ELEMENT_COLOR: return D3DDECLTYPE_D3DCOLOR; + case VERTEX_ELEMENT_SPECULAR: return D3DDECLTYPE_D3DCOLOR; + case VERTEX_ELEMENT_TANGENT_S: return D3DDECLTYPE_FLOAT3; + case VERTEX_ELEMENT_TANGENT_T: return D3DDECLTYPE_FLOAT3; + case VERTEX_ELEMENT_WRINKLE: + // Wrinkle is packed into Position.W, it is not specified as a separate vertex element + Assert( 0 ); + return D3DDECLTYPE_UNUSED; +#if !defined( _X360 ) + case VERTEX_ELEMENT_BONEINDEX: return D3DDECLTYPE_D3DCOLOR; +#else + // UBYTE4 comes in as [0,255] in the shader, which is ideal for bone indices + // (unfortunately, UBYTE4 is not universally supported on PC DX8 GPUs) + case VERTEX_ELEMENT_BONEINDEX: return D3DDECLTYPE_UBYTE4; +#endif + case VERTEX_ELEMENT_BONEWEIGHTS1: return D3DDECLTYPE_FLOAT1; + case VERTEX_ELEMENT_BONEWEIGHTS2: return D3DDECLTYPE_FLOAT2; + case VERTEX_ELEMENT_BONEWEIGHTS3: return D3DDECLTYPE_FLOAT3; + case VERTEX_ELEMENT_BONEWEIGHTS4: return D3DDECLTYPE_FLOAT4; + case VERTEX_ELEMENT_USERDATA1: return D3DDECLTYPE_FLOAT1; + case VERTEX_ELEMENT_USERDATA2: return D3DDECLTYPE_FLOAT2; + case VERTEX_ELEMENT_USERDATA3: return D3DDECLTYPE_FLOAT3; + case VERTEX_ELEMENT_USERDATA4: return D3DDECLTYPE_FLOAT4; + case VERTEX_ELEMENT_TEXCOORD1D_0: return D3DDECLTYPE_FLOAT1; + case VERTEX_ELEMENT_TEXCOORD1D_1: return D3DDECLTYPE_FLOAT1; + case VERTEX_ELEMENT_TEXCOORD1D_2: return D3DDECLTYPE_FLOAT1; + case VERTEX_ELEMENT_TEXCOORD1D_3: return D3DDECLTYPE_FLOAT1; + case VERTEX_ELEMENT_TEXCOORD1D_4: return D3DDECLTYPE_FLOAT1; + case VERTEX_ELEMENT_TEXCOORD1D_5: return D3DDECLTYPE_FLOAT1; + case VERTEX_ELEMENT_TEXCOORD1D_6: return D3DDECLTYPE_FLOAT1; + case VERTEX_ELEMENT_TEXCOORD1D_7: return D3DDECLTYPE_FLOAT1; + case VERTEX_ELEMENT_TEXCOORD2D_0: return D3DDECLTYPE_FLOAT2; + case VERTEX_ELEMENT_TEXCOORD2D_1: return D3DDECLTYPE_FLOAT2; + case VERTEX_ELEMENT_TEXCOORD2D_2: return D3DDECLTYPE_FLOAT2; + case VERTEX_ELEMENT_TEXCOORD2D_3: return D3DDECLTYPE_FLOAT2; + case VERTEX_ELEMENT_TEXCOORD2D_4: return D3DDECLTYPE_FLOAT2; + case VERTEX_ELEMENT_TEXCOORD2D_5: return D3DDECLTYPE_FLOAT2; + case VERTEX_ELEMENT_TEXCOORD2D_6: return D3DDECLTYPE_FLOAT2; + case VERTEX_ELEMENT_TEXCOORD2D_7: return D3DDECLTYPE_FLOAT2; + case VERTEX_ELEMENT_TEXCOORD3D_0: return D3DDECLTYPE_FLOAT3; + case VERTEX_ELEMENT_TEXCOORD3D_1: return D3DDECLTYPE_FLOAT3; + case VERTEX_ELEMENT_TEXCOORD3D_2: return D3DDECLTYPE_FLOAT3; + case VERTEX_ELEMENT_TEXCOORD3D_3: return D3DDECLTYPE_FLOAT3; + case VERTEX_ELEMENT_TEXCOORD3D_4: return D3DDECLTYPE_FLOAT3; + case VERTEX_ELEMENT_TEXCOORD3D_5: return D3DDECLTYPE_FLOAT3; + case VERTEX_ELEMENT_TEXCOORD3D_6: return D3DDECLTYPE_FLOAT3; + case VERTEX_ELEMENT_TEXCOORD3D_7: return D3DDECLTYPE_FLOAT3; + case VERTEX_ELEMENT_TEXCOORD4D_0: return D3DDECLTYPE_FLOAT4; + case VERTEX_ELEMENT_TEXCOORD4D_1: return D3DDECLTYPE_FLOAT4; + case VERTEX_ELEMENT_TEXCOORD4D_2: return D3DDECLTYPE_FLOAT4; + case VERTEX_ELEMENT_TEXCOORD4D_3: return D3DDECLTYPE_FLOAT4; + case VERTEX_ELEMENT_TEXCOORD4D_4: return D3DDECLTYPE_FLOAT4; + case VERTEX_ELEMENT_TEXCOORD4D_5: return D3DDECLTYPE_FLOAT4; + case VERTEX_ELEMENT_TEXCOORD4D_6: return D3DDECLTYPE_FLOAT4; + case VERTEX_ELEMENT_TEXCOORD4D_7: return D3DDECLTYPE_FLOAT4; + default: + Assert(0); + return D3DDECLTYPE_UNUSED; + }; +} + +void PrintVertexDeclaration( const D3DVERTEXELEMENT9 *pDecl ) +{ + int i; + static D3DVERTEXELEMENT9 declEnd = D3DDECL_END(); + for ( i = 0; ; i++ ) + { + if ( memcmp( &pDecl[i], &declEnd, sizeof( declEnd ) ) == 0 ) + { + Warning( "D3DDECL_END\n" ); + break; + } + Msg( "%d: Stream: %d, Offset: %d, Type: %s, Method: %s, Usage: %s, UsageIndex: %d\n", + i, ( int )pDecl[i].Stream, ( int )pDecl[i].Offset, + DeclTypeToString( pDecl[i].Type ), + DeclMethodToString( pDecl[i].Method ), + DeclUsageToString( pDecl[i].Usage ), + ( int )pDecl[i].UsageIndex ); + } +} + +//----------------------------------------------------------------------------- +// Converts format to a vertex decl +//----------------------------------------------------------------------------- +void ComputeVertexSpec( VertexFormat_t fmt, D3DVERTEXELEMENT9 *pDecl, bool bStaticLit, bool bUsingFlex, bool bUsingMorph ) +{ + int i = 0; + int offset = 0; + + VertexCompressionType_t compressionType = CompressionType( fmt ); + + if ( IsX360() ) + { + // On 360, there's a performance penalty for reading more than 2 streams in the vertex shader + // (we don't do this yet, but we should be aware if we start doing it) +#ifdef _DEBUG + int numStreams = 1 + ( bStaticLit ? 1 : 0 ) + ( bUsingFlex ? 1 : 0 ) + ( bUsingMorph ? 1 : 0 ); + Assert( numStreams <= 2 ); +#endif + } + + if ( fmt & VERTEX_POSITION ) + { + pDecl[i].Stream = 0; + pDecl[i].Offset = offset; + pDecl[i].Method = D3DDECLMETHOD_DEFAULT; + pDecl[i].Usage = D3DDECLUSAGE_POSITION; + pDecl[i].UsageIndex = 0; + pDecl[i].Type = VertexElementToDeclType( VERTEX_ELEMENT_POSITION, compressionType ); + offset += GetVertexElementSize( VERTEX_ELEMENT_POSITION, compressionType ); + ++i; + } + + int numBones = NumBoneWeights(fmt); + if ( numBones > 0 ) + { + pDecl[i].Stream = 0; + pDecl[i].Offset = offset; + pDecl[i].Method = D3DDECLMETHOD_DEFAULT; + pDecl[i].Usage = D3DDECLUSAGE_BLENDWEIGHT; + pDecl[i].UsageIndex = 0; + + // Always exactly two weights + pDecl[i].Type = VertexElementToDeclType( VERTEX_ELEMENT_BONEWEIGHTS2, compressionType ); + offset += GetVertexElementSize( VERTEX_ELEMENT_BONEWEIGHTS2, compressionType ); + ++i; + } + + if ( fmt & VERTEX_BONE_INDEX ) + { + // this isn't FVF!!!!! + pDecl[i].Stream = 0; + pDecl[i].Offset = offset; + pDecl[i].Method = D3DDECLMETHOD_DEFAULT; + pDecl[i].Usage = D3DDECLUSAGE_BLENDINDICES; + pDecl[i].UsageIndex = 0; + pDecl[i].Type = VertexElementToDeclType( VERTEX_ELEMENT_BONEINDEX, compressionType ); + offset += GetVertexElementSize( VERTEX_ELEMENT_BONEINDEX, compressionType ); + ++i; + } + + int normalOffset = -1; + if ( fmt & VERTEX_NORMAL ) + { + pDecl[i].Stream = 0; + pDecl[i].Offset = offset; + normalOffset = offset; + pDecl[i].Method = D3DDECLMETHOD_DEFAULT; + pDecl[i].Usage = D3DDECLUSAGE_NORMAL; + pDecl[i].UsageIndex = 0; + pDecl[i].Type = VertexElementToDeclType( VERTEX_ELEMENT_NORMAL, compressionType ); + offset += GetVertexElementSize( VERTEX_ELEMENT_NORMAL, compressionType ); + ++i; + } + + if ( fmt & VERTEX_COLOR ) + { + pDecl[i].Stream = 0; + pDecl[i].Offset = offset; + pDecl[i].Method = D3DDECLMETHOD_DEFAULT; + pDecl[i].Usage = D3DDECLUSAGE_COLOR; + pDecl[i].UsageIndex = 0; + pDecl[i].Type = VertexElementToDeclType( VERTEX_ELEMENT_COLOR, compressionType ); + offset += GetVertexElementSize( VERTEX_ELEMENT_COLOR, compressionType ); + ++i; + } + + if ( fmt & VERTEX_SPECULAR ) + { + Assert( !bStaticLit ); + pDecl[i].Stream = 0; + pDecl[i].Offset = offset; + pDecl[i].Method = D3DDECLMETHOD_DEFAULT; + pDecl[i].Usage = D3DDECLUSAGE_COLOR; + pDecl[i].UsageIndex = 1; // SPECULAR goes in the second COLOR slot + pDecl[i].Type = VertexElementToDeclType( VERTEX_ELEMENT_SPECULAR, compressionType ); + offset += GetVertexElementSize( VERTEX_ELEMENT_SPECULAR, compressionType ); + ++i; + } + + VertexElement_t texCoordDimensions[4] = { VERTEX_ELEMENT_TEXCOORD1D_0, + VERTEX_ELEMENT_TEXCOORD2D_0, + VERTEX_ELEMENT_TEXCOORD3D_0, + VERTEX_ELEMENT_TEXCOORD4D_0 }; + for ( int j = 0; j < VERTEX_MAX_TEXTURE_COORDINATES; ++j ) + { + int nCoordSize = TexCoordSize( j, fmt ); + if ( nCoordSize <= 0 ) + continue; + Assert( nCoordSize <= 4 ); + + pDecl[i].Stream = 0; + pDecl[i].Offset = offset; + pDecl[i].Method = D3DDECLMETHOD_DEFAULT; + pDecl[i].Usage = D3DDECLUSAGE_TEXCOORD; + pDecl[i].UsageIndex = j; + VertexElement_t texCoordElement = (VertexElement_t)( texCoordDimensions[ nCoordSize - 1 ] + j ); + pDecl[i].Type = VertexElementToDeclType( texCoordElement, compressionType ); + offset += GetVertexElementSize( texCoordElement, compressionType ); + ++i; + } + + if ( fmt & VERTEX_TANGENT_S ) + { + pDecl[i].Stream = 0; + pDecl[i].Offset = offset; + pDecl[i].Method = D3DDECLMETHOD_DEFAULT; + pDecl[i].Usage = D3DDECLUSAGE_TANGENT; + pDecl[i].UsageIndex = 0; + // NOTE: this is currently *not* compressed + pDecl[i].Type = VertexElementToDeclType( VERTEX_ELEMENT_TANGENT_S, compressionType ); + offset += GetVertexElementSize( VERTEX_ELEMENT_TANGENT_S, compressionType ); + ++i; + } + + if ( fmt & VERTEX_TANGENT_T ) + { + pDecl[i].Stream = 0; + pDecl[i].Offset = offset; + pDecl[i].Method = D3DDECLMETHOD_DEFAULT; + pDecl[i].Usage = D3DDECLUSAGE_BINORMAL; + pDecl[i].UsageIndex = 0; + // NOTE: this is currently *not* compressed + pDecl[i].Type = VertexElementToDeclType( VERTEX_ELEMENT_TANGENT_T, compressionType ); + offset += GetVertexElementSize( VERTEX_ELEMENT_TANGENT_T, compressionType ); + ++i; + } + + int userDataSize = UserDataSize(fmt); + if ( userDataSize > 0 ) + { + Assert( userDataSize == 4 ); // This is actually only ever used for tangents + pDecl[i].Stream = 0; + if ( ( compressionType == VERTEX_COMPRESSION_ON ) && + ( COMPRESSED_NORMALS_TYPE == COMPRESSED_NORMALS_COMBINEDTANGENTS_UBYTE4 ) ) + { + // FIXME: Normals and tangents are packed together into a single UBYTE4 element, + // so just point this back at the same data while we're testing UBYTE4 out. + pDecl[i].Offset = normalOffset; + } + else + { + pDecl[i].Offset = offset; + } + pDecl[i].Method = D3DDECLMETHOD_DEFAULT; + pDecl[i].Usage = D3DDECLUSAGE_TANGENT; + pDecl[i].UsageIndex = 0; + VertexElement_t userDataElement = (VertexElement_t)( VERTEX_ELEMENT_USERDATA1 + ( userDataSize - 1 ) ); + pDecl[i].Type = VertexElementToDeclType( userDataElement, compressionType ); + offset += GetVertexElementSize( userDataElement, compressionType ); + ++i; + } + + if ( bStaticLit ) + { + // force stream 1 to have specular color in it, which is used for baked static lighting + pDecl[i].Stream = 1; + pDecl[i].Offset = 0; + pDecl[i].Method = D3DDECLMETHOD_DEFAULT; + pDecl[i].Usage = D3DDECLUSAGE_COLOR; + pDecl[i].UsageIndex = 1; // SPECULAR goes into the second COLOR slot + pDecl[i].Type = VertexElementToDeclType( VERTEX_ELEMENT_SPECULAR, compressionType ); + ++i; + } + + if ( HardwareConfig()->SupportsVertexAndPixelShaders() ) + { + // FIXME: There needs to be a better way of doing this + // In 2.0b, assume position is 4d, storing wrinkle in pos.w. + bool bUseWrinkle = HardwareConfig()->SupportsPixelShaders_2_b(); + + // Force stream 2 to have flex deltas in it + pDecl[i].Stream = 2; + pDecl[i].Offset = 0; + pDecl[i].Method = D3DDECLMETHOD_DEFAULT; + pDecl[i].Usage = D3DDECLUSAGE_POSITION; + pDecl[i].UsageIndex = 1; + // FIXME: unify this with VertexElementToDeclType(): + pDecl[i].Type = bUseWrinkle ? D3DDECLTYPE_FLOAT4 : D3DDECLTYPE_FLOAT3; + ++i; + + int normalOffset = GetVertexElementSize( VERTEX_ELEMENT_POSITION, compressionType ); + if ( bUseWrinkle ) + { + normalOffset += GetVertexElementSize( VERTEX_ELEMENT_WRINKLE, compressionType ); + } + + // Normal deltas + pDecl[i].Stream = 2; + pDecl[i].Offset = normalOffset; + pDecl[i].Method = D3DDECLMETHOD_DEFAULT; + pDecl[i].Usage = D3DDECLUSAGE_NORMAL; + pDecl[i].UsageIndex = 1; + // NOTE: this is currently *not* compressed + pDecl[i].Type = VertexElementToDeclType( VERTEX_ELEMENT_NORMAL, VERTEX_COMPRESSION_NONE ); + ++i; + } + + if ( bUsingMorph ) + { + // force stream 3 to have vertex index in it, which is used for doing vertex texture reads + pDecl[i].Stream = 3; + pDecl[i].Offset = 0; + pDecl[i].Method = D3DDECLMETHOD_DEFAULT; + pDecl[i].Usage = D3DDECLUSAGE_POSITION; + pDecl[i].UsageIndex = 2; + pDecl[i].Type = VertexElementToDeclType( VERTEX_ELEMENT_USERDATA1, compressionType ); + ++i; + } + + static D3DVERTEXELEMENT9 declEnd = D3DDECL_END(); + pDecl[i] = declEnd; + + //PrintVertexDeclaration( pDecl ); +} + +//----------------------------------------------------------------------------- +// Gets the declspec associated with a vertex format +//----------------------------------------------------------------------------- +struct VertexDeclLookup_t +{ + enum LookupFlags_t + { + STATIC_LIT = 0x1, + USING_MORPH = 0x2, + USING_FLEX = 0x4, + }; + + VertexFormat_t m_VertexFormat; + int m_nFlags; + IDirect3DVertexDeclaration9 *m_pDecl; + + bool operator==( const VertexDeclLookup_t &src ) const + { + return ( m_VertexFormat == src.m_VertexFormat ) && ( m_nFlags == src.m_nFlags ); + } +}; + + +//----------------------------------------------------------------------------- +// Dictionary of vertex decls +// FIXME: stick this in the class? +// FIXME: Does anything cause this to get flushed? +//----------------------------------------------------------------------------- +static bool VertexDeclLessFunc( const VertexDeclLookup_t &src1, const VertexDeclLookup_t &src2 ) +{ + if ( src1.m_nFlags == src2.m_nFlags ) + return src1.m_VertexFormat < src2.m_VertexFormat; + + return ( src1.m_nFlags < src2.m_nFlags ); +} + +static CUtlRBTree s_VertexDeclDict( 0, 256, VertexDeclLessFunc ); + +//----------------------------------------------------------------------------- +// Gets the declspec associated with a vertex format +//----------------------------------------------------------------------------- +IDirect3DVertexDeclaration9 *FindOrCreateVertexDecl( VertexFormat_t fmt, bool bStaticLit, bool bUsingFlex, bool bUsingMorph ) +{ + MEM_ALLOC_D3D_CREDIT(); + + VertexDeclLookup_t lookup; + lookup.m_VertexFormat = fmt; + lookup.m_nFlags = 0; + if ( bStaticLit ) + { + lookup.m_nFlags |= VertexDeclLookup_t::STATIC_LIT; + } + if ( bUsingMorph ) + { + lookup.m_nFlags |= VertexDeclLookup_t::USING_MORPH; + } + if ( bUsingFlex ) + { + lookup.m_nFlags |= VertexDeclLookup_t::USING_FLEX; + } + + int i = s_VertexDeclDict.Find( lookup ); + if ( i != s_VertexDeclDict.InvalidIndex() ) + { + // found + return s_VertexDeclDict[i].m_pDecl; + } + + D3DVERTEXELEMENT9 decl[32]; + ComputeVertexSpec( fmt, decl, bStaticLit, bUsingFlex, bUsingMorph ); + + HRESULT hr = + Dx9Device()->CreateVertexDeclaration( decl, &lookup.m_pDecl ); + + // NOTE: can't record until we have m_pDecl! + RECORD_COMMAND( DX8_CREATE_VERTEX_DECLARATION, 2 ); + RECORD_INT( ( int )lookup.m_pDecl ); + RECORD_STRUCT( decl, sizeof( decl ) ); + COMPILE_TIME_ASSERT( sizeof( decl ) == sizeof( D3DVERTEXELEMENT9 ) * 32 ); + + Assert( hr == D3D_OK ); + if ( hr != D3D_OK ) + { + Warning( " ERROR: failed to create vertex decl for vertex format 0x%08llX! You'll probably see messed-up mesh rendering - to diagnose, build shaderapidx9.dll in debug.\n", fmt ); + } + + s_VertexDeclDict.Insert( lookup ); + return lookup.m_pDecl; +} + + +//----------------------------------------------------------------------------- +// Clears out all declspecs +//----------------------------------------------------------------------------- +void ReleaseAllVertexDecl() +{ + int i = s_VertexDeclDict.FirstInorder(); + while ( i != s_VertexDeclDict.InvalidIndex() ) + { + if ( s_VertexDeclDict[i].m_pDecl ) + s_VertexDeclDict[i].m_pDecl->Release(); + i = s_VertexDeclDict.NextInorder( i ); + } +} + diff --git a/materialsystem/shaderapidx9/vertexdecl.h b/materialsystem/shaderapidx9/vertexdecl.h new file mode 100644 index 0000000..d275fdb --- /dev/null +++ b/materialsystem/shaderapidx9/vertexdecl.h @@ -0,0 +1,32 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// + +#ifndef VERTEXDECL_H +#define VERTEXDECL_H + +#ifdef _WIN32 +#pragma once +#endif + +#include "locald3dtypes.h" +#include "materialsystem/imaterial.h" + + +//----------------------------------------------------------------------------- +// Gets the declspec associated with a vertex format +//----------------------------------------------------------------------------- +IDirect3DVertexDeclaration9 *FindOrCreateVertexDecl( VertexFormat_t fmt, bool bStaticLit, bool bUsingFlex, bool bUsingMorph ); + +//----------------------------------------------------------------------------- +// Clears out all declspecs +//----------------------------------------------------------------------------- +void ReleaseAllVertexDecl( ); + + +#endif // VERTEXDECL_H + diff --git a/materialsystem/shaderapidx9/vertexshaderdx8.cpp b/materialsystem/shaderapidx9/vertexshaderdx8.cpp new file mode 100644 index 0000000..f0a7a0c --- /dev/null +++ b/materialsystem/shaderapidx9/vertexshaderdx8.cpp @@ -0,0 +1,3783 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Vertex/Pixel Shaders +// +//===========================================================================// +#define DISABLE_PROTECTED_THINGS +#if ( defined(_WIN32) && !defined( _X360 ) ) +#elif POSIX +#include +#include +#include +#include +#include +#include +#include +#define closesocket close +#define WSAGetLastError() errno +#undef SOCKET +typedef int SOCKET; +#define SOCKET_ERROR (-1) +#define SD_SEND 0x01 +#define INVALID_SOCKET (~0) +#endif + +#include "togl/rendermechanism.h" +#include "vertexshaderdx8.h" +#include "tier1/utlsymbol.h" +#include "tier1/utlvector.h" +#include "tier1/utldict.h" +#include "tier1/utllinkedlist.h" +#include "tier1/utlbuffer.h" +#include "tier1/UtlStringMap.h" +#include "locald3dtypes.h" +#include "shaderapidx8_global.h" +#include "recording.h" +#include "tier0/vprof.h" +#include "materialsystem/imaterialsystem.h" +#include "materialsystem/imaterialsystemhardwareconfig.h" +#include "KeyValues.h" +#include "shaderapidx8.h" +#include "materialsystem/IShader.h" +#include "IShaderSystem.h" +#include "tier0/fasttimer.h" +#include +#include +#include +#include "filesystem.h" +#include "convar.h" +#include "materialsystem/shader_vcs_version.h" +#include "tier1/lzmaDecoder.h" +#include "tier1/utlmap.h" + +#include "datacache/idatacache.h" +#include "tier1/diff.h" +#include "shaderdevicedx8.h" +#include "filesystem/IQueuedLoader.h" +#include "tier2/tier2.h" +#include "shaderapi/ishaderutil.h" +#include "tier0/icommandline.h" + +#include "Color.h" +#include "tier0/dbg.h" + +#ifdef REMOTE_DYNAMIC_SHADER_COMPILE + +# if defined (POSIX) + +# include +# include + +# else + +# include +# include + +# endif + +#endif + +// NOTE: This has to be the last file included! +#include "tier0/memdbgon.h" + + +// It currently includes windows.h and we don't want that. +#if 0 //def USE_ACTUAL_DX + +#include "../utils/bzip2/bzlib.h" + +#else + +int BZ2_bzBuffToBuffDecompress( + char* dest, + unsigned int* destLen, + char* source, + unsigned int sourceLen, + int _small, + int verbosity + ) +{ + return 0; +} + +#endif + +static ConVar mat_remoteshadercompile( "mat_remoteshadercompile", "127.0.0.1", FCVAR_CHEAT ); + +//#define PROFILE_SHADER_CREATE + +//#define NO_AMBIENT_CUBE +#define MAX_BONES 3 + +// debugging aid +#define MAX_SHADER_HISTORY 16 + +#if !defined( _X360 ) +#define SHADER_FNAME_EXTENSION ".vcs" +#else +#define SHADER_FNAME_EXTENSION ".360.vcs" +#endif + +#ifdef DYNAMIC_SHADER_COMPILE +volatile static char s_ShaderCompileString[]="dynamic_shader_compile_is_on"; +#endif + +#ifdef DYNAMIC_SHADER_COMPILE +static void MatFlushShaders( void ); +#endif + +// D3D to OpenGL translator +//static D3DToGL_ASM sg_D3DToOpenGLTranslator; // Remove the _ASM to switch to the new translator +//static D3DToGL sg_NewD3DToOpenGLTranslator; // Remove the _ASM to switch to the new translator + +static const char *GetLightTypeName( VertexShaderLightTypes_t type ) +{ + static const char *s_VertexShaderLightTypeNames[] = + { + "LIGHT_NONE", + "LIGHT_SPOT", + "LIGHT_POINT", + "LIGHT_DIRECTIONAL", + "LIGHT_STATIC", + "LIGHT_AMBIENTCUBE", + }; + return s_VertexShaderLightTypeNames[type+1]; +} + +#ifdef PROFILE_SHADER_CREATE +static FILE *GetDebugFileHandle( void ) +{ + static FILE *fp = NULL; + if( !fp ) + { + fp = fopen( "shadercreate.txt", "w" ); + Assert( fp ); + } + return fp; +} +#endif // PROFILE_SHADER_CREATE + +#ifdef DX_TO_GL_ABSTRACTION + // mat_autoload_glshaders instructs the engine to load a cached shader table at startup + // it will try for glshaders.cfg first, then fall back to glbaseshaders.cfg if not found +ConVar mat_autoload_glshaders( "mat_autoload_glshaders", "1" ); + + // mat_autosave_glshaders instructs the engine to save out the shader table at key points + // to the filename glshaders.cfg + // +ConVar mat_autosave_glshaders( "mat_autosave_glshaders", "1" ); + + +#endif +//----------------------------------------------------------------------------- +// Explicit instantiation of shader buffer implementation +//----------------------------------------------------------------------------- +template class CShaderBuffer< ID3DXBuffer >; + + +//----------------------------------------------------------------------------- +// Used to find unique shaders +//----------------------------------------------------------------------------- +#ifdef MEASURE_DRIVER_ALLOCATIONS +static CUtlMap< CRC32_t, int, int > s_UniqueVS( 0, 0, DefLessFunc( CRC32_t ) ); +static CUtlMap< CRC32_t, int, int > s_UniquePS( 0, 0, DefLessFunc( CRC32_t ) ); +static CUtlMap< IDirect3DVertexShader9*, CRC32_t, int > s_VSLookup( 0, 0, DefLessFunc( IDirect3DVertexShader9* ) ); +static CUtlMap< IDirect3DPixelShader9*, CRC32_t, int > s_PSLookup( 0, 0, DefLessFunc( IDirect3DPixelShader9* ) ); +#endif + +static int s_NumPixelShadersCreated = 0; +static int s_NumVertexShadersCreated = 0; + +static void RegisterVS( const void* pShaderBits, int nShaderSize, IDirect3DVertexShader9* pShader ) +{ +#ifdef MEASURE_DRIVER_ALLOCATIONS + CRC32_t crc; + CRC32_Init( &crc ); + CRC32_ProcessBuffer( &crc, pShaderBits, nShaderSize ); + CRC32_Final( &crc ); + + s_VSLookup.Insert( pShader, crc ); + + int nIndex = s_UniqueVS.Find( crc ); + if ( nIndex != s_UniqueVS.InvalidIndex() ) + { + ++s_UniqueVS[nIndex]; + } + else + { + int nMemUsed = 23 * 1024; + s_UniqueVS.Insert( crc, 1 ); + VPROF_INCREMENT_GROUP_COUNTER( "unique vs count", COUNTER_GROUP_NO_RESET, 1 ); + VPROF_INCREMENT_GROUP_COUNTER( "vs driver mem", COUNTER_GROUP_NO_RESET, nMemUsed ); + VPROF_INCREMENT_GROUP_COUNTER( "total driver mem", COUNTER_GROUP_NO_RESET, nMemUsed ); + } +#endif +} + +static void RegisterPS( const void* pShaderBits, int nShaderSize, IDirect3DPixelShader9* pShader ) +{ +#ifdef MEASURE_DRIVER_ALLOCATIONS + CRC32_t crc; + CRC32_Init( &crc ); + CRC32_ProcessBuffer( &crc, pShaderBits, nShaderSize ); + CRC32_Final( &crc ); + + s_PSLookup.Insert( pShader, crc ); + + int nIndex = s_UniquePS.Find( crc ); + if ( nIndex != s_UniquePS.InvalidIndex() ) + { + ++s_UniquePS[nIndex]; + } + else + { + int nMemUsed = 400; + s_UniquePS.Insert( crc, 1 ); + VPROF_INCREMENT_GROUP_COUNTER( "unique ps count", COUNTER_GROUP_NO_RESET, 1 ); + VPROF_INCREMENT_GROUP_COUNTER( "ps driver mem", COUNTER_GROUP_NO_RESET, nMemUsed ); + VPROF_INCREMENT_GROUP_COUNTER( "total driver mem", COUNTER_GROUP_NO_RESET, nMemUsed ); + } +#endif +} + +static void UnregisterVS( IDirect3DVertexShader9* pShader ) +{ +#ifdef MEASURE_DRIVER_ALLOCATIONS + int nCRCIndex = s_VSLookup.Find( pShader ); + if ( nCRCIndex == s_VSLookup.InvalidIndex() ) + return; + + CRC32_t crc = s_VSLookup[nCRCIndex]; + s_VSLookup.RemoveAt( nCRCIndex ); + + int nIndex = s_UniqueVS.Find( crc ); + if ( nIndex != s_UniqueVS.InvalidIndex() ) + { + if ( --s_UniqueVS[nIndex] <= 0 ) + { + int nMemUsed = 23 * 1024; + VPROF_INCREMENT_GROUP_COUNTER( "unique vs count", COUNTER_GROUP_NO_RESET, -1 ); + VPROF_INCREMENT_GROUP_COUNTER( "vs driver mem", COUNTER_GROUP_NO_RESET, -nMemUsed ); + VPROF_INCREMENT_GROUP_COUNTER( "total driver mem", COUNTER_GROUP_NO_RESET, -nMemUsed ); + s_UniqueVS.Remove( nIndex ); + } + } +#endif +} + +static void UnregisterPS( IDirect3DPixelShader9* pShader ) +{ +#ifdef MEASURE_DRIVER_ALLOCATIONS + int nCRCIndex = s_PSLookup.Find( pShader ); + if ( nCRCIndex == s_PSLookup.InvalidIndex() ) + return; + + CRC32_t crc = s_PSLookup[nCRCIndex]; + s_PSLookup.RemoveAt( nCRCIndex ); + + int nIndex = s_UniquePS.Find( crc ); + if ( nIndex != s_UniquePS.InvalidIndex() ) + { + if ( --s_UniquePS[nIndex] <= 0 ) + { + int nMemUsed = 400; + VPROF_INCREMENT_GROUP_COUNTER( "unique ps count", COUNTER_GROUP_NO_RESET, -1 ); + VPROF_INCREMENT_GROUP_COUNTER( "ps driver mem", COUNTER_GROUP_NO_RESET, -nMemUsed ); + VPROF_INCREMENT_GROUP_COUNTER( "total driver mem", COUNTER_GROUP_NO_RESET, -nMemUsed ); + s_UniquePS.Remove( nIndex ); + } + } +#endif +} + +//----------------------------------------------------------------------------- +// The lovely low-level dx call to create a vertex shader +//----------------------------------------------------------------------------- +static HardwareShader_t CreateD3DVertexShader( DWORD *pByteCode, int numBytes, const char *pShaderName, char *debugLabel = NULL ) +{ + MEM_ALLOC_D3D_CREDIT(); + + if ( !pByteCode ) + { + Assert( 0 ); + return INVALID_HARDWARE_SHADER; + } + + // Compute the vertex specification + HardwareShader_t hShader; + + #ifdef DX_TO_GL_ABSTRACTION + HRESULT hr = Dx9Device()->CreateVertexShader( pByteCode, (IDirect3DVertexShader9 **)&hShader, pShaderName, debugLabel ); + #else + if ( IsEmulatingGL() ) + { + DWORD dwVersion = D3DXGetShaderVersion( pByteCode ); + REFERENCE( dwVersion ); + Assert ( D3DSHADER_VERSION_MAJOR( dwVersion ) == 2 ); + } + + #if defined(_X360) || !defined(DX_TO_GL_ABSTRACTION) + HRESULT hr = Dx9Device()->CreateVertexShader( pByteCode, (IDirect3DVertexShader9 **)&hShader ); + #else + HRESULT hr = Dx9Device()->CreateVertexShader( pByteCode, (IDirect3DVertexShader9 **)&hShader, pShaderName ); +#endif + + #endif + + // NOTE: This isn't recorded before the CreateVertexShader because + // we don't know the value of shader until after the CreateVertexShader. + RECORD_COMMAND( DX8_CREATE_VERTEX_SHADER, 3 ); + RECORD_INT( ( int )hShader ); // hack hack hack + RECORD_INT( numBytes ); + RECORD_STRUCT( pByteCode, numBytes ); + + if ( FAILED( hr ) ) + { + Assert( 0 ); + hShader = INVALID_HARDWARE_SHADER; + } + else + { + s_NumVertexShadersCreated++; + RegisterVS( pByteCode, numBytes, (IDirect3DVertexShader9 *)hShader ); + } + return hShader; +} + +static void PatchPixelShaderForAtiMsaaHack(DWORD *pShader, DWORD dwTexCoordMask) +{ + if ( IsPC() ) + { + bool bIsSampler, bIsTexCoord; + + // Should be able to patch only ps2.0 + if (*pShader != 0xFFFF0200) + return; + + pShader++; + + while (pShader) + { + switch (*pShader & D3DSI_OPCODE_MASK) + { + case D3DSIO_COMMENT: + // Process comment + pShader = pShader + (*pShader >> 16) + 1; + break; + + case D3DSIO_END: + // End of shader + return; + + case D3DSIO_DCL: + bIsSampler = (*(pShader + 1) & D3DSP_TEXTURETYPE_MASK) != D3DSTT_UNKNOWN; + bIsTexCoord = (((*(pShader + 2) & D3DSP_REGTYPE_MASK) >> D3DSP_REGTYPE_SHIFT) + + ((*(pShader + 2) & D3DSP_REGTYPE_MASK2) >> D3DSP_REGTYPE_SHIFT2)) == D3DSPR_TEXTURE; + + if (!bIsSampler && bIsTexCoord) + { + DWORD dwTexCoord = *(pShader + 2) & D3DSP_REGNUM_MASK; + DWORD mask = 0x01; + for (DWORD i = 0; i < 16; i++) + { + if (((dwTexCoordMask & mask) == mask) && (dwTexCoord == i)) + { + // If found -- patch and get out + // *(pShader + 2) |= D3DSPDM_PARTIALPRECISION; + *(pShader + 2) |= D3DSPDM_MSAMPCENTROID; + break; + } + mask <<= 1; + } + } + // Intentionally fall through... + + default: + // Skip instruction + pShader = pShader + ((*pShader & D3DSI_INSTLENGTH_MASK) >> D3DSI_INSTLENGTH_SHIFT) + 1; + } + } + } +} + +static ConVar mat_force_ps_patch( "mat_force_ps_patch", "0" ); +static ConVar mat_disable_ps_patch( "mat_disable_ps_patch", "0", FCVAR_CHEAT ); + +//----------------------------------------------------------------------------- +// The lovely low-level dx call to create a pixel shader +//----------------------------------------------------------------------------- +static HardwareShader_t CreateD3DPixelShader( DWORD *pByteCode, unsigned int nCentroidMask, int numBytes, const char* pShaderName, char *debugLabel = NULL ) +{ + MEM_ALLOC_D3D_CREDIT(); + + if ( !pByteCode ) + return INVALID_HARDWARE_SHADER; + + if ( IsPC() && nCentroidMask && + ( HardwareConfig()->NeedsATICentroidHack() || + mat_force_ps_patch.GetInt() ) ) + { + if ( !mat_disable_ps_patch.GetInt() ) + { + PatchPixelShaderForAtiMsaaHack( pByteCode, nCentroidMask ); + } + } + + HardwareShader_t shader; + #if defined( DX_TO_GL_ABSTRACTION ) + #if defined( OSX ) + HRESULT hr = Dx9Device()->CreatePixelShader( pByteCode, ( IDirect3DPixelShader ** )&shader, pShaderName, debugLabel ); + #else + HRESULT hr = Dx9Device()->CreatePixelShader( pByteCode, ( IDirect3DPixelShader ** )&shader, pShaderName, debugLabel, &nCentroidMask ); + #endif + #else + if ( IsEmulatingGL() ) + { + DWORD dwVersion; + dwVersion = D3DXGetShaderVersion( pByteCode ); + Assert ( D3DSHADER_VERSION_MAJOR( dwVersion ) == 2 ); + } +#if defined(_X360) || !defined(DX_TO_GL_ABSTRACTION) + HRESULT hr = Dx9Device()->CreatePixelShader( pByteCode, ( IDirect3DPixelShader ** )&shader ); +#else + HRESULT hr = Dx9Device()->CreatePixelShader( pByteCode, ( IDirect3DPixelShader ** )&shader, pShaderName ); + #endif + #endif + + // NOTE: We have to do this after creating the pixel shader since we don't know + // lookup.m_PixelShader yet!!!!!!! + RECORD_COMMAND( DX8_CREATE_PIXEL_SHADER, 3 ); + RECORD_INT( ( int )shader ); // hack hack hack + RECORD_INT( numBytes ); + RECORD_STRUCT( pByteCode, numBytes ); + + if ( FAILED( hr ) ) + { + Assert(0); + shader = INVALID_HARDWARE_SHADER; + } + else + { + s_NumPixelShadersCreated++; + RegisterPS( pByteCode, numBytes, ( IDirect3DPixelShader9* )shader ); + } + + return shader; +} + +template int BinarySearchCombos( uint32 nStaticComboID, int nCombos, T const *pRecords ) +{ + // Use binary search - data is sorted + int nLowerIdx = 1; + int nUpperIdx = nCombos; + for (;;) + { + if ( nUpperIdx < nLowerIdx ) + return -1; + + int nMiddleIndex = ( nLowerIdx + nUpperIdx ) / 2; + uint32 nProbe = pRecords[nMiddleIndex-1].m_nStaticComboID; + if ( nStaticComboID < nProbe ) + { + nUpperIdx = nMiddleIndex - 1; + } + else + { + if ( nStaticComboID > nProbe ) + nLowerIdx = nMiddleIndex + 1; + else + return nMiddleIndex - 1; + } + } +} + +inline int FindShaderStaticCombo( uint32 nStaticComboID, const ShaderHeader_t& header, StaticComboRecord_t *pRecords ) +{ + if ( header.m_nVersion < 5 ) + return -1; + + return BinarySearchCombos( nStaticComboID, header.m_nNumStaticCombos, pRecords ); +} + +// cache redundant i/o fetched components of the vcs files +struct ShaderFileCache_t +{ + CUtlSymbol m_Name; + CUtlSymbol m_Filename; + ShaderHeader_t m_Header; + bool m_bVertexShader; + + // valid for diff version only - contains the microcode used as the reference for diff algorithm + CUtlBuffer m_ReferenceCombo; + + // valid for ver5 only - contains the directory + CUtlVector< StaticComboRecord_t > m_StaticComboRecords; + CUtlVector< StaticComboAliasRecord_t > m_StaticComboDupRecords; + + ShaderFileCache_t() + { + // invalid until version established + m_Header.m_nVersion = 0; + } + + bool IsValid() const + { + return m_Header.m_nVersion != 0; + } + + bool IsOldVersion() const + { + return m_Header.m_nVersion < 5; + } + + int IsVersion6() const + { + return ( m_Header.m_nVersion == 6 ); + } + + int FindCombo( uint32 nStaticComboID ) + { + int nSearchAliases = BinarySearchCombos( nStaticComboID, m_StaticComboDupRecords.Count(), m_StaticComboDupRecords.Base() ); + if ( nSearchAliases != -1 ) + nStaticComboID = m_StaticComboDupRecords[nSearchAliases].m_nSourceStaticCombo; + return FindShaderStaticCombo( nStaticComboID, m_Header, m_StaticComboRecords.Base() ); + } + + bool operator==( const ShaderFileCache_t& a ) const + { + return m_Name == a.m_Name && m_bVertexShader == a.m_bVertexShader; + } +}; + + +//----------------------------------------------------------------------------- +// Vertex + pixel shader manager +//----------------------------------------------------------------------------- +class CShaderManager : public IShaderManager +{ +public: + CShaderManager(); + virtual ~CShaderManager(); + + // Methods of IShaderManager + virtual void Init(); + virtual void Shutdown(); + virtual IShaderBuffer *CompileShader( const char *pProgram, size_t nBufLen, const char *pShaderVersion ); + virtual VertexShaderHandle_t CreateVertexShader( IShaderBuffer* pShaderBuffer ); + virtual void DestroyVertexShader( VertexShaderHandle_t hShader ); + virtual PixelShaderHandle_t CreatePixelShader( IShaderBuffer* pShaderBuffer ); + virtual void DestroyPixelShader( PixelShaderHandle_t hShader ); + virtual VertexShader_t CreateVertexShader( const char *pVertexShaderFile, int nStaticVshIndex = 0, char *debugLabel = NULL ); + virtual PixelShader_t CreatePixelShader( const char *pPixelShaderFile, int nStaticPshIndex = 0, char *debugLabel = NULL ); + virtual void SetVertexShader( VertexShader_t shader ); + virtual void SetPixelShader( PixelShader_t shader ); + virtual void BindVertexShader( VertexShaderHandle_t shader ); + virtual void BindPixelShader( PixelShaderHandle_t shader ); + virtual void *GetCurrentVertexShader(); + virtual void *GetCurrentPixelShader(); + virtual void ResetShaderState(); + void FlushShaders(); + virtual void ClearVertexAndPixelShaderRefCounts(); + virtual void PurgeUnusedVertexAndPixelShaders(); + void SpewVertexAndPixelShaders(); + const char *GetActiveVertexShaderName(); + const char *GetActivePixelShaderName(); + bool CreateDynamicCombos_Ver4( void *pContext, uint8 *pComboBuffer ); + bool CreateDynamicCombos_Ver5( void *pContext, uint8 *pComboBuffer, char *debugLabel = NULL ); + +#if defined( DX_TO_GL_ABSTRACTION ) + virtual void DoStartupShaderPreloading(); +#endif + + static void QueuedLoaderCallback( void *pContext, void *pContext2, const void *pData, int nSize, LoaderError_t loaderError ); + +private: + typedef CUtlFixedLinkedList< IDirect3DVertexShader9* >::IndexType_t VertexShaderIndex_t; + typedef CUtlFixedLinkedList< IDirect3DPixelShader9* >::IndexType_t PixelShaderIndex_t; + + struct ShaderStaticCombos_t + { + int m_nCount; + + // Can't use CUtlVector here since you CUtlLinkedList> doesn't work. + HardwareShader_t *m_pHardwareShaders; + struct ShaderCreationData_t + { + CUtlVector ByteCode; + uint32 iCentroidMask; + }; + + ShaderCreationData_t *m_pCreationData; + }; + + struct ShaderLookup_t + { + CUtlSymbol m_Name; + int m_nStaticIndex; + ShaderStaticCombos_t m_ShaderStaticCombos; + DWORD m_Flags; + int m_nRefCount; + unsigned int m_hShaderFileCache; + + // for queued loading, bias an aligned optimal buffer forward to correct location + int m_nDataOffset; + + // diff version, valid during load only + ShaderDictionaryEntry_t *m_pComboDictionary; + + ShaderLookup_t() + { + m_Flags = 0; + m_nRefCount = 0; + m_ShaderStaticCombos.m_nCount = 0; + m_ShaderStaticCombos.m_pHardwareShaders = 0; + m_ShaderStaticCombos.m_pCreationData = 0; + m_pComboDictionary = NULL; + } + void IncRefCount() + { + m_nRefCount++; + } + bool operator==( const ShaderLookup_t& a ) const + { + return m_Name == a.m_Name && m_nStaticIndex == a.m_nStaticIndex; + } + }; + +#ifdef DYNAMIC_SHADER_COMPILE + struct Combo_t + { + CUtlSymbol m_ComboName; + int m_nMin; + int m_nMax; + }; + + struct ShaderCombos_t + { + CUtlVector m_StaticCombos; + CUtlVector m_DynamicCombos; + int GetNumDynamicCombos( void ) const + { + int combos = 1; + int i; + for( i = 0; i < m_DynamicCombos.Count(); i++ ) + { + combos *= ( m_DynamicCombos[i].m_nMax - m_DynamicCombos[i].m_nMin + 1 ); + } + return combos; + } + int GetNumStaticCombos( void ) const + { + int combos = 1; + int i; + for( i = 0; i < m_StaticCombos.Count(); i++ ) + { + combos *= ( m_StaticCombos[i].m_nMax - m_StaticCombos[i].m_nMin + 1 ); + } + return combos; + } + }; +#endif + +private: + void CreateStaticShaders(); + void DestroyStaticShaders(); + +#if defined ( DYNAMIC_SHADER_COMPILE ) && defined( REMOTE_DYNAMIC_SHADER_COMPILE ) + void InitRemoteShaderCompile(); + void DeinitRemoteShaderCompile(); +#endif + + // The low-level dx call to set the vertex shader state + void SetVertexShaderState( HardwareShader_t shader, DataCacheHandle_t hCachedShader = DC_INVALID_HANDLE ); + + // The low-level dx call to set the pixel shader state + void SetPixelShaderState( HardwareShader_t shader, DataCacheHandle_t hCachedShader = DC_INVALID_HANDLE ); + + // Destroys all shaders + void DestroyAllShaders(); + + // Destroy a particular vertex shader + void DestroyVertexShader( VertexShader_t shader ); + // Destroy a particular pixel shader + void DestroyPixelShader( PixelShader_t shader ); + + bool LoadAndCreateShaders( ShaderLookup_t &lookup, bool bVertexShader, char *debugLabel = NULL ); + FileHandle_t OpenFileAndLoadHeader( const char *pFileName, ShaderHeader_t *pHeader ); + +#ifdef DYNAMIC_SHADER_COMPILE + bool LoadAndCreateShaders_Dynamic( ShaderLookup_t &lookup, bool bVertexShader ); + const ShaderCombos_t *FindOrCreateShaderCombos( const char *pShaderName ); + HardwareShader_t CompileShader( const char *pShaderName, int nStaticIndex, int nDynamicIndex, bool bVertexShader ); +#endif + + void DisassembleShader( ShaderLookup_t *pLookup, int dynamicCombo, uint8 *pByteCode ); + void WriteTranslatedFile( ShaderLookup_t *pLookup, int dynamicCombo, char *pFileContents, char *pFileExtension ); + + // DX_TO_GL_ABSTRACTION only, no-op otherwise + + void SaveShaderCache( char *cacheName ); // query GLM pair cache for all active shader pairs and write them to disk in named file + bool LoadShaderCache( char *cacheName ); // read named file, establish compiled shader sets for each vertex+static and pixel+static, then link pairs as listed in table + // return true on success, false if file not found + + // old void WarmShaderCache(); + + CUtlFixedLinkedList< ShaderLookup_t > m_VertexShaderDict; + CUtlFixedLinkedList< ShaderLookup_t > m_PixelShaderDict; + + CUtlSymbolTable m_ShaderSymbolTable; + +#ifdef DYNAMIC_SHADER_COMPILE + typedef HRESULT (__stdcall *ShaderCompileFromFileFunc_t)( LPCSTR pSrcFile, CONST D3DXMACRO* pDefines, + LPD3DXINCLUDE pInclude, LPCSTR pFunctionName, LPCSTR pProfile, DWORD Flags, + LPD3DXBUFFER* ppShader, LPD3DXBUFFER * ppErrorMsgs, LPD3DXCONSTANTTABLE * ppConstantTable ); + CUtlStringMap m_ShaderNameToCombos; + CSysModule *m_pShaderCompiler30; + ShaderCompileFromFileFunc_t m_ShaderCompileFileFunc30; +#endif + + // The current vertex and pixel shader + HardwareShader_t m_HardwareVertexShader; + HardwareShader_t m_HardwarePixelShader; + + CUtlFixedLinkedList< IDirect3DVertexShader9* > m_RawVertexShaderDict; + CUtlFixedLinkedList< IDirect3DPixelShader9* > m_RawPixelShaderDict; + + CUtlFixedLinkedList< ShaderFileCache_t > m_ShaderFileCache; + + // false, creates during init. + // true, creates on access, helps reduce d3d memory for tools, but causes i/o hitches. + bool m_bCreateShadersOnDemand; + +#if defined( _DEBUG ) + // for debugging (can't resolve UtlSym) + // need some history because 360 d3d has rips related to sequencing + char vshDebugName[MAX_SHADER_HISTORY][64]; + int vshDebugIndex; + char pshDebugName[MAX_SHADER_HISTORY][64]; + int pshDebugIndex; +#endif + +#if defined ( DYNAMIC_SHADER_COMPILE ) && defined( REMOTE_DYNAMIC_SHADER_COMPILE ) + SOCKET m_RemoteShaderCompileSocket; +#endif + +}; + + +//----------------------------------------------------------------------------- +// Singleton accessor +//----------------------------------------------------------------------------- +static CShaderManager s_ShaderManager; +IShaderManager *g_pShaderManager = &s_ShaderManager; + +//----------------------------------------------------------------------------- +// Constructor, destructor +//----------------------------------------------------------------------------- +CShaderManager::CShaderManager() : + m_ShaderSymbolTable( 0, 32, true /* caseInsensitive */ ), + m_VertexShaderDict( 32 ), + m_PixelShaderDict( 32 ), + m_ShaderFileCache( 32 ) +{ + m_bCreateShadersOnDemand = false; + +#ifdef DYNAMIC_SHADER_COMPILE + m_pShaderCompiler30 = 0; + m_ShaderCompileFileFunc30 = 0; +#ifdef REMOTE_DYNAMIC_SHADER_COMPILE + m_RemoteShaderCompileSocket = INVALID_SOCKET; +#endif +#endif + +#ifdef _DEBUG + vshDebugIndex = 0; + pshDebugIndex = 0; +#endif +} + +CShaderManager::~CShaderManager() +{ +#if defined ( DYNAMIC_SHADER_COMPILE ) && defined( REMOTE_DYNAMIC_SHADER_COMPILE ) + DeinitRemoteShaderCompile(); +#endif +} + +#define REMOTE_SHADER_COMPILE_PORT "20000" + +#if defined ( DYNAMIC_SHADER_COMPILE ) && defined( REMOTE_DYNAMIC_SHADER_COMPILE ) +void CShaderManager::InitRemoteShaderCompile() +{ + DeinitRemoteShaderCompile(); + + int nResult = 0; + #ifdef _WIN32 + WSADATA wsaData; + nResult = WSAStartup( 0x101, &wsaData ); + if ( nResult != 0 ) + { + Warning( "CShaderManager::Init - Could not init socket for remote dynamic shader compilation\n" ); + } + #endif + + struct addrinfo hints; + ZeroMemory( &hints, sizeof(hints) ); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = IPPROTO_TCP; + + // Resolve the server address and port + struct addrinfo *result = NULL; + nResult = getaddrinfo( mat_remoteshadercompile.GetString(), REMOTE_SHADER_COMPILE_PORT, &hints, &result ); + if ( nResult != 0 ) + { + Warning( "getaddrinfo failed: %d\n", nResult ); + #ifdef _WIN32 + WSACleanup(); + #endif + Assert( 0 ); + } + + // Attempt to connect to an address until one succeeds + for( struct addrinfo *ptr = result; ptr != NULL; ptr = ptr->ai_next ) + { + // Create a SOCKET for connecting to remote shader compilation server + m_RemoteShaderCompileSocket = socket( ptr->ai_family, ptr->ai_socktype, ptr->ai_protocol ); + if ( m_RemoteShaderCompileSocket == INVALID_SOCKET ) + { + Warning( "Error at socket(): %ld\n", WSAGetLastError() ); + freeaddrinfo( result ); + #ifdef _WIN32 + WSACleanup(); + #endif + Assert( 0 ); + continue; + } + + // Connect to server. + nResult = connect( m_RemoteShaderCompileSocket, ptr->ai_addr, (int)ptr->ai_addrlen); + if ( nResult == SOCKET_ERROR ) + { + closesocket( m_RemoteShaderCompileSocket ); + m_RemoteShaderCompileSocket = INVALID_SOCKET; + continue; + } + break; + } + + freeaddrinfo( result ); + + if ( m_RemoteShaderCompileSocket == INVALID_SOCKET ) + { + Warning( "Unable to connect to remote shader compilation server!\n" ); + #ifdef _WIN32 + WSACleanup(); + #endif + Assert ( 0 ); + } +} + +void CShaderManager::DeinitRemoteShaderCompile() +{ + if ( m_RemoteShaderCompileSocket != INVALID_SOCKET ) + { + if ( shutdown( m_RemoteShaderCompileSocket, SD_SEND ) == SOCKET_ERROR ) + { + Warning( "Remote shader compilation shutdown failed: %d\n", WSAGetLastError() ); + } + closesocket( m_RemoteShaderCompileSocket ); + m_RemoteShaderCompileSocket = INVALID_SOCKET; + } +} +#endif // defined ( DYNAMIC_SHADER_COMPILE ) && defined( REMOTE_DYNAMIC_SHADER_COMPILE ) + +//----------------------------------------------------------------------------- +// Initialization, shutdown +//----------------------------------------------------------------------------- +void CShaderManager::Init() +{ + // incompatible with the 360, violates loading system + // only used by PC to help tools reduce d3d footprint + m_bCreateShadersOnDemand = IsPC() && ( ShaderUtil()->InEditorMode() || CommandLine()->CheckParm( "-shadersondemand" ) ); + +#ifdef DYNAMIC_SHADER_COMPILE + if( !IsX360() ) + { + +#ifdef REMOTE_DYNAMIC_SHADER_COMPILE + InitRemoteShaderCompile(); +#else // REMOTE_DYNAMIC_SHADER_COMPILE + +#ifdef _DEBUG + m_pShaderCompiler30 = Sys_LoadModule( "d3dx9d_33.dll" ); +#endif + if (!m_pShaderCompiler30) + { + m_pShaderCompiler30 = Sys_LoadModule( "d3dx9_33.dll" ); + } + + if ( m_pShaderCompiler30 ) + { + m_ShaderCompileFileFunc30 = (ShaderCompileFromFileFunc_t)GetProcAddress( (HMODULE)m_pShaderCompiler30, "D3DXCompileShaderFromFileA" ); + } + +#endif + + } +#endif // DYNAMIC_SHADER_COMPILE + + CreateStaticShaders(); +} + +void CShaderManager::Shutdown() +{ +#ifdef DYNAMIC_SHADER_COMPILE + if ( m_pShaderCompiler30 ) + { + Sys_UnloadModule( m_pShaderCompiler30 ); + m_pShaderCompiler30 = 0; + m_ShaderCompileFileFunc30 = 0; + } +#endif + +#ifdef DX_TO_GL_ABSTRACTION + if (mat_autosave_glshaders.GetInt()) + { + SaveShaderCache("glshaders.cfg"); + } +#endif + + DestroyAllShaders(); + DestroyStaticShaders(); +} + + +//----------------------------------------------------------------------------- +// Compiles shaders +//----------------------------------------------------------------------------- +IShaderBuffer *CShaderManager::CompileShader( const char *pProgram, size_t nBufLen, const char *pShaderVersion ) +{ + int nCompileFlags = D3DXSHADER_AVOID_FLOW_CONTROL; + +#ifdef _DEBUG + nCompileFlags |= D3DXSHADER_DEBUG; +#endif + + LPD3DXBUFFER pCompiledShader, pErrorMessages; + HRESULT hr = D3DXCompileShader( pProgram, nBufLen, + NULL, NULL, "main", pShaderVersion, nCompileFlags, + &pCompiledShader, &pErrorMessages, NULL ); + + if ( FAILED( hr ) ) + { + if ( pErrorMessages ) + { + const char *pErrorMessage = (const char *)pErrorMessages->GetBufferPointer(); + Warning( "Shader compilation failed! Reported the following errors:\n%s\n", pErrorMessage ); + pErrorMessages->Release(); + } + return NULL; + } + + // NOTE: This uses small block heap allocator; so I'm not going + // to bother creating a memory pool. + CShaderBuffer< ID3DXBuffer > *pShaderBuffer = new CShaderBuffer< ID3DXBuffer >( pCompiledShader ); + if ( pErrorMessages ) + { + pErrorMessages->Release(); + } + + return pShaderBuffer; +} + + +VertexShaderHandle_t CShaderManager::CreateVertexShader( IShaderBuffer* pShaderBuffer ) +{ + // Create the vertex shader + IDirect3DVertexShader9 *pVertexShader = NULL; + +#if defined(_X360) || !defined(DX_TO_GL_ABSTRACTION) + HRESULT hr = Dx9Device()->CreateVertexShader( (const DWORD*)pShaderBuffer->GetBits(), &pVertexShader ); +#else + HRESULT hr = Dx9Device()->CreateVertexShader( (const DWORD*)pShaderBuffer->GetBits(), &pVertexShader, NULL ); +#endif + + if ( FAILED( hr ) || !pVertexShader ) + return VERTEX_SHADER_HANDLE_INVALID; + + s_NumVertexShadersCreated++; + RegisterVS( pShaderBuffer->GetBits(), pShaderBuffer->GetSize(), pVertexShader ); + + // Insert the shader into the dictionary of shaders + VertexShaderIndex_t i = m_RawVertexShaderDict.AddToTail( pVertexShader ); + return (VertexShaderHandle_t)i; +} + +void CShaderManager::DestroyVertexShader( VertexShaderHandle_t hShader ) +{ + if ( hShader == VERTEX_SHADER_HANDLE_INVALID ) + return; + + VertexShaderIndex_t i = (VertexShaderIndex_t)hShader; + IDirect3DVertexShader9 *pVertexShader = m_RawVertexShaderDict[ i ]; + + UnregisterVS( pVertexShader ); + + VerifyEquals( (int)pVertexShader->Release(), 0 ); + m_RawVertexShaderDict.Remove( i ); +} + +PixelShaderHandle_t CShaderManager::CreatePixelShader( IShaderBuffer* pShaderBuffer ) +{ + // Create the vertex shader + IDirect3DPixelShader9 *pPixelShader = NULL; +#if defined(_X360) || !defined(DX_TO_GL_ABSTRACTION) + HRESULT hr = Dx9Device()->CreatePixelShader( (const DWORD*)pShaderBuffer->GetBits(), &pPixelShader ); +#else + HRESULT hr = Dx9Device()->CreatePixelShader( (const DWORD*)pShaderBuffer->GetBits(), &pPixelShader, NULL ); +#endif + + if ( FAILED( hr ) || !pPixelShader ) + return PIXEL_SHADER_HANDLE_INVALID; + + s_NumPixelShadersCreated++; + + RegisterPS( pShaderBuffer->GetBits(), pShaderBuffer->GetSize(), pPixelShader ); + + // Insert the shader into the dictionary of shaders + PixelShaderIndex_t i = m_RawPixelShaderDict.AddToTail( pPixelShader ); + return (PixelShaderHandle_t)i; +} + +void CShaderManager::DestroyPixelShader( PixelShaderHandle_t hShader ) +{ + if ( hShader == PIXEL_SHADER_HANDLE_INVALID ) + return; + + PixelShaderIndex_t i = (PixelShaderIndex_t)hShader; + IDirect3DPixelShader9 *pPixelShader = m_RawPixelShaderDict[ i ]; + + UnregisterPS( pPixelShader ); + + VerifyEquals( (int)pPixelShader->Release(), 0 ); + m_RawPixelShaderDict.Remove( i ); +} + + +//----------------------------------------------------------------------------- +// Globals +//----------------------------------------------------------------------------- +HardwareShader_t s_pIllegalMaterialPS = INVALID_HARDWARE_SHADER; + +//----------------------------------------------------------------------------- +// Static methods +//----------------------------------------------------------------------------- +void CShaderManager::CreateStaticShaders() +{ + MEM_ALLOC_D3D_CREDIT(); + + if ( !HardwareConfig()->SupportsVertexAndPixelShaders() ) + { + return; + } + + if ( IsPC() ) + { + // GR - hack for illegal materials + const DWORD psIllegalMaterial[] = + { + #ifdef DX_TO_GL_ABSTRACTION + // Use a PS 2.0 binary shader on DX_TO_GL_ABSTRACTION + 0xffff0200, 0x05000051, 0xa00f0000, 0x3f800000, + 0x00000000, 0x3f800000, 0x3f800000, 0x02000001, + 0x800f0000, 0xa0e40000, 0x02000001, 0x800f0800, + 0x80e40000, 0x0000ffff + #else + 0xffff0101, 0x00000051, 0xa00f0000, 0x00000000, 0x3f800000, 0x00000000, + 0x3f800000, 0x00000001, 0x800f0000, 0xa0e40000, 0x0000ffff + #endif + }; + // create default shader +#if defined(_X360) || !defined(DX_TO_GL_ABSTRACTION) + Dx9Device()->CreatePixelShader( psIllegalMaterial, ( IDirect3DPixelShader9 ** )&s_pIllegalMaterialPS ); +#else + Dx9Device()->CreatePixelShader( psIllegalMaterial, ( IDirect3DPixelShader9 ** )&s_pIllegalMaterialPS, NULL ); +#endif + } +} + +void CShaderManager::DestroyStaticShaders() +{ + // GR - invalid material hack + // destroy internal shader + if ( s_pIllegalMaterialPS != INVALID_HARDWARE_SHADER ) + { + ( ( IDirect3DPixelShader9 * )s_pIllegalMaterialPS )->Release(); + s_pIllegalMaterialPS = INVALID_HARDWARE_SHADER; + } +} + +#ifdef DYNAMIC_SHADER_COMPILE +static const char *GetShaderSourcePath( void ) +{ + static char shaderDir[MAX_PATH]; + // GR - just in case init this... + static bool bHaveShaderDir = false; + if( !bHaveShaderDir ) + { + bHaveShaderDir = true; +# if ( defined( DYNAMIC_SHADER_COMPILE_CUSTOM_PATH ) ) + { + Q_strncpy( shaderDir, DYNAMIC_SHADER_COMPILE_CUSTOM_PATH, MAX_PATH ); + } +# else + { +# if ( defined( _X360 ) ) + { + char hostName[128] = ""; + const char *pHostName = CommandLine()->ParmValue( "-host" ); + if ( !pHostName ) + { + // the 360 machine name must be _360 + DWORD length = sizeof( hostName ); + DmGetXboxName( hostName, &length ); + char *p = strstr( hostName, "_360" ); + *p = '\0'; + pHostName = hostName; + } + + Q_snprintf( shaderDir, MAX_PATH, "net:\\smb\\%s\\stdshaders", pHostName ); + } +# else + { + Q_strncpy( shaderDir, __FILE__, MAX_PATH ); + Q_StripFilename( shaderDir ); + Q_StripLastDir( shaderDir, MAX_PATH ); + Q_strncat( shaderDir, "stdshaders", MAX_PATH, COPY_ALL_CHARACTERS ); + } +# endif + } +# endif + } + return shaderDir; +} +#endif + +#ifdef DYNAMIC_SHADER_COMPILE +const CShaderManager::ShaderCombos_t *CShaderManager::FindOrCreateShaderCombos( const char *pShaderName ) +{ + if( m_ShaderNameToCombos.Defined( pShaderName ) ) + { + return &m_ShaderNameToCombos[pShaderName]; + } + ShaderCombos_t &combos = m_ShaderNameToCombos[pShaderName]; + char filename[MAX_PATH]; + // try the vsh dir first. + Q_strncpy( filename, GetShaderSourcePath(), MAX_PATH ); + Q_strncat( filename, "\\", MAX_PATH, COPY_ALL_CHARACTERS ); + Q_strncat( filename, pShaderName, MAX_PATH, COPY_ALL_CHARACTERS ); + Q_strncat( filename, ".vsh", MAX_PATH, COPY_ALL_CHARACTERS ); + CUtlInplaceBuffer bffr( 0, 0, CUtlInplaceBuffer::TEXT_BUFFER ); + + bool bOpenResult = g_pFullFileSystem->ReadFile( filename, NULL, bffr ); + + if ( bOpenResult ) + { + NULL; + } + else + { + // try the fxc dir. + Q_strncpy( filename, GetShaderSourcePath(), MAX_PATH ); + Q_strncat( filename, "\\", MAX_PATH, COPY_ALL_CHARACTERS ); + Q_strncat( filename, pShaderName, MAX_PATH, COPY_ALL_CHARACTERS ); + Q_strncat( filename, ".fxc", MAX_PATH, COPY_ALL_CHARACTERS ); + bOpenResult = g_pFullFileSystem->ReadFile( filename, NULL, bffr ); + + if ( !bOpenResult ) + { + // Maybe this is a specific version [20 & 20b] -> [2x] + if ( Q_strlen( pShaderName ) >= 3 ) + { + char *pszEndFilename = filename + strlen( filename ); + if ( !Q_stricmp( pszEndFilename - 6, "30.fxc" ) ) + { + // Total hack. Who knows what builds that 30 shader? + strcpy( pszEndFilename - 6, "20b.fxc" ); + bOpenResult = g_pFullFileSystem->ReadFile( filename, NULL, bffr ); + if ( !bOpenResult ) + { + strcpy( pszEndFilename - 6, "2x.fxc" ); + bOpenResult = g_pFullFileSystem->ReadFile( filename, NULL, bffr ); + } + if ( !bOpenResult ) + { + strcpy( pszEndFilename - 6, "20.fxc" ); + bOpenResult = g_pFullFileSystem->ReadFile( filename, NULL, bffr ); + } + } + else + { + if ( !stricmp( pszEndFilename - 6, "20.fxc" ) ) + { + pszEndFilename[ -5 ] = 'x'; + } + else if ( !stricmp( pszEndFilename - 7, "20b.fxc" ) ) + { + strcpy( pszEndFilename - 7, "2x.fxc" ); + --pszEndFilename; + } + else if ( !stricmp( pszEndFilename - 6, "11.fxc" ) ) + { + strcpy( pszEndFilename - 6, "xx.fxc" ); + } + + bOpenResult = g_pFullFileSystem->ReadFile( filename, NULL, bffr ); + if ( !bOpenResult ) + { + if ( !stricmp( pszEndFilename - 6, "2x.fxc" ) ) + { + pszEndFilename[ -6 ] = 'x'; + bOpenResult = g_pFullFileSystem->ReadFile( filename, NULL, bffr ); + } + } + } + } + } + + if ( !bOpenResult ) + { + Assert( 0 ); + return NULL; + } + } + + while( char *line = bffr.InplaceGetLinePtr() ) + { + // dear god perl is better at this kind of shit! + int begin = 0; + int end = 0; + + // check if the line starts with '//' + if( line[0] != '/' || line[1] != '/' ) + { + continue; + } + + // Check if line intended for platform lines + if( IsX360() ) + { + if ( Q_stristr( line, "[PC]" ) ) + continue; + } + else + { + if ( Q_stristr( line, "[360]" ) || Q_stristr( line, "[XBOX]" ) ) + continue; + } + + // Skip any lines intended for other shader version + if ( Q_stristr( pShaderName, "_ps20" ) && !Q_stristr( pShaderName, "_ps20b" ) && + Q_stristr( line, "[ps" ) && !Q_stristr( line, "[ps20]" ) ) + continue; + if ( Q_stristr( pShaderName, "_ps20b" ) && + Q_stristr( line, "[ps" ) && !Q_stristr( line, "[ps20b]" ) ) + continue; + if ( Q_stristr( pShaderName, "_ps30" ) && + Q_stristr( line, "[ps" ) && !Q_stristr( line, "[ps30]" ) ) + continue; + if ( Q_stristr( pShaderName, "_vs20" ) && + Q_stristr( line, "[vs" ) && !Q_stristr( line, "[vs20]" ) ) + continue; + if ( Q_stristr( pShaderName, "_vs30" ) && + Q_stristr( line, "[vs" ) && !Q_stristr( line, "[vs30]" ) ) + continue; + + char *pScan = &line[2]; + while( *pScan == ' ' || *pScan == '\t' ) + { + pScan++; + } + + bool bDynamic; + if( Q_strncmp( pScan, "DYNAMIC", 7 ) == 0 ) + { + bDynamic = true; + pScan += 7; + } + else if( Q_strncmp( pScan, "STATIC", 6 ) == 0 ) + { + bDynamic = false; + pScan += 6; + } + else + { + continue; + } + + // skip whitespace + while( *pScan == ' ' || *pScan == '\t' ) + { + pScan++; + } + + // check for colon + if( *pScan != ':' ) + { + continue; + } + pScan++; + + // skip whitespace + while( *pScan == ' ' || *pScan == '\t' ) + { + pScan++; + } + + // check for quote + if( *pScan != '\"' ) + { + continue; + } + pScan++; + + char *pBeginningOfName = pScan; + while( 1 ) + { + if( *pScan == '\0' ) + { + break; + } + if( *pScan == '\"' ) + { + break; + } + pScan++; + } + + if( *pScan == '\0' ) + { + continue; + } + + // must have hit a quote. .done with string. + // slam a NULL at the end quote of the string so that we have the string at pBeginningOfName. + *pScan = '\0'; + pScan++; + + // skip whitespace + while( *pScan == ' ' || *pScan == '\t' ) + { + pScan++; + } + + // check for quote + if( *pScan != '\"' ) + { + continue; + } + pScan++; + + // make sure that we have a number after the quote. + if( !isdigit( *pScan ) ) + { + continue; + } + + while( isdigit( *pScan ) ) + { + begin = begin * 10 + ( *pScan - '0' ); + pScan++; + } + + if( pScan[0] != '.' || pScan[1] != '.' ) + { + continue; + } + pScan += 2; + + // make sure that we have a number + if( !isdigit( *pScan ) ) + { + continue; + } + + while( isdigit( *pScan ) ) + { + end = end * 10 + ( *pScan - '0' ); + pScan++; + } + + if( pScan[0] != '\"' ) + { + continue; + } + + // sweet freaking jesus. .done parsing the line. +// char buf[1024]; +// sprintf( buf, "\"%s\" \"%s\" %d %d\n", bDynamic ? "DYNAMIC" : "STATIC", pBeginningOfName, begin, end ); +// Plat_DebugString( buf ); + + Combo_t *pCombo = NULL; + if( bDynamic ) + { + pCombo = &combos.m_DynamicCombos[combos.m_DynamicCombos.AddToTail()]; + } + else + { + pCombo = &combos.m_StaticCombos[combos.m_StaticCombos.AddToTail()]; + } + + pCombo->m_ComboName = m_ShaderSymbolTable.AddString( pBeginningOfName ); + pCombo->m_nMin = begin; + pCombo->m_nMax = end; + } + + return &combos; +} +#endif // DYNAMIC_SHADER_COMPILE + +#ifdef DYNAMIC_SHADER_COMPILE +#ifndef DX_TO_GL_ABSTRACTION +//----------------------------------------------------------------------------- +// Used to deal with include files +//----------------------------------------------------------------------------- +class CDxInclude : public ID3DXInclude +{ +public: + CDxInclude( const char *pMainFileName ); + +#if defined( _X360 ) + virtual HRESULT WINAPI Open( D3DXINCLUDE_TYPE IncludeType, LPCSTR pFileName, LPCVOID pParentData, LPCVOID * ppData, UINT * pBytes, LPSTR pFullPath, DWORD cbFullPath ); +#else + STDMETHOD(Open)(THIS_ D3DXINCLUDE_TYPE IncludeType, LPCSTR pFileName, LPCVOID pParentData, LPCVOID *ppData, UINT *pBytes); +#endif + + STDMETHOD(Close)(THIS_ LPCVOID pData); + +private: + char m_pBasePath[MAX_PATH]; + +#if defined( _X360 ) + char m_pFullPath[MAX_PATH]; +#endif +}; + +CDxInclude::CDxInclude( const char *pMainFileName ) +{ + Q_ExtractFilePath( pMainFileName, m_pBasePath, sizeof(m_pBasePath) ); +} + + +#if defined( _X360 ) +HRESULT CDxInclude::Open( D3DXINCLUDE_TYPE IncludeType, LPCSTR pFileName, LPCVOID pParentData, LPCVOID * ppData, UINT * pBytes, LPSTR pFullPath, DWORD cbFullPath ) +#else +HRESULT CDxInclude::Open( D3DXINCLUDE_TYPE IncludeType, LPCSTR pFileName, LPCVOID pParentData, LPCVOID * ppData, UINT * pBytes ) +#endif +{ + char pTemp[MAX_PATH]; + if ( !Q_IsAbsolutePath( pFileName ) && ( IncludeType == D3DXINC_LOCAL ) ) + { + Q_ComposeFileName( m_pBasePath, pFileName, pTemp, sizeof(pTemp) ); + pFileName = pTemp; + } + + CUtlBuffer buf( 0, 0, CUtlBuffer::TEXT_BUFFER ); + if ( !g_pFullFileSystem->ReadFile( pFileName, NULL, buf ) ) + return E_FAIL; + + *pBytes = buf.TellMaxPut(); + void *pMem = malloc( *pBytes ); + memcpy( pMem, buf.Base(), *pBytes ); + *ppData = pMem; + +# if ( defined( _X360 ) ) + { + Q_ComposeFileName( m_pBasePath, pFileName, m_pFullPath, sizeof(m_pFullPath) ); + pFullPath = m_pFullPath; + cbFullPath = MAX_PATH; + } +# endif + + return S_OK; +} + +HRESULT CDxInclude::Close( LPCVOID pData ) +{ + void *pMem = const_cast( pData ); + free( pMem ); + return S_OK; +} +#endif // not DX_TO_GL_ABSTRACTION + +static const char *FileNameToShaderModel( const char *pShaderName, bool bVertexShader ) +{ + // Figure out the shader model + const char *pShaderModel = NULL; + if( bVertexShader ) + { + if( Q_stristr( pShaderName, "vs20" ) ) + { + pShaderModel = "vs_2_0"; + bVertexShader = true; + } + else if( Q_stristr( pShaderName, "vs11" ) ) + { + pShaderModel = "vs_1_1"; + bVertexShader = true; + } + else if( Q_stristr( pShaderName, "vs14" ) ) + { + pShaderModel = "vs_1_1"; + bVertexShader = true; + } + else if( Q_stristr( pShaderName, "vs30" ) ) + { + pShaderModel = "vs_3_0"; + bVertexShader = true; + } + else + { +#ifdef _DEBUG + Error( "Failed dynamic shader compiled\nBuild shaderapidx9.dll in debug to find problem\n" ); +#else + Assert( 0 ); +#endif + } + } + else + { + if( Q_stristr( pShaderName, "ps20b" ) ) + { + pShaderModel = "ps_2_b"; + } + else if( Q_stristr( pShaderName, "ps20" ) ) + { + pShaderModel = "ps_2_0"; + } + else if( Q_stristr( pShaderName, "ps11" ) ) + { + pShaderModel = "ps_1_1"; + } + else if( Q_stristr( pShaderName, "ps14" ) ) + { + pShaderModel = "ps_1_4"; + } + else if( Q_stristr( pShaderName, "ps30" ) ) + { + pShaderModel = "ps_3_0"; + } + else + { +#ifdef _DEBUG + Error( "Failed dynamic shader compiled\nBuild shaderapidx9.dll in debug to find problem\n" ); +#else + Assert( 0 ); +#endif + } + } + return pShaderModel; +} +#endif + +#ifdef DYNAMIC_SHADER_COMPILE + +#if defined( _X360 ) +static ConVar mat_flushshaders_generate_updbs( "mat_flushshaders_generate_updbs", "0", 0, "Generates UPDBs whenever you flush shaders." ); +#endif + +HardwareShader_t CShaderManager::CompileShader( const char *pShaderName, + int nStaticIndex, int nDynamicIndex, bool bVertexShader ) +{ + VPROF_BUDGET( "CompileShader", "CompileShader" ); + Assert( m_ShaderNameToCombos.Defined( pShaderName ) ); + if( !m_ShaderNameToCombos.Defined( pShaderName ) ) + { + return INVALID_HARDWARE_SHADER; + } + const ShaderCombos_t &combos = m_ShaderNameToCombos[pShaderName]; +#ifdef _DEBUG + int numStaticCombos = combos.GetNumStaticCombos(); + int numDynamicCombos = combos.GetNumDynamicCombos(); +#endif + Assert( nStaticIndex % numDynamicCombos == 0 ); + Assert( ( nStaticIndex % numDynamicCombos ) >= 0 && ( nStaticIndex % numDynamicCombos ) < numStaticCombos ); + Assert( nDynamicIndex >= 0 && nDynamicIndex < numDynamicCombos ); + +# ifdef DYNAMIC_SHADER_COMPILE_VERBOSE + + //Warning( "Compiling %s %s\n\tdynamic:", bVertexShader ? "vsh" : "psh", pShaderName ); + Warning( "Compiling " ); + if ( bVertexShader ) + ConColorMsg( Color( 0, 255, 0, 255 ), "vsh - %s ", pShaderName ); + else + ConColorMsg( Color( 0, 255, 255, 255 ), "psh - %s ", pShaderName ); + Warning( "\n\tdynamic:" ); + +# endif + + CUtlVector macros; + // plus 1 for null termination, plus 1 for #define SHADER_MODEL_*, and plus 1 for #define _X360 on 360 + macros.SetCount( combos.m_DynamicCombos.Count() + combos.m_StaticCombos.Count() + 2 + ( IsX360() ? 1 : 0 ) ); + + int nCombo = nStaticIndex + nDynamicIndex; + int macroIndex = 0; + int i; + for( i = 0; i < combos.m_DynamicCombos.Count(); i++ ) + { + int countForCombo = combos.m_DynamicCombos[i].m_nMax - combos.m_DynamicCombos[i].m_nMin + 1; + int val = nCombo % countForCombo + combos.m_DynamicCombos[i].m_nMin; + nCombo /= countForCombo; + macros[macroIndex].Name = m_ShaderSymbolTable.String( combos.m_DynamicCombos[i].m_ComboName ); + char buf[16]; + sprintf( buf, "%d", val ); + CUtlSymbol valSymbol( buf ); + macros[macroIndex].Definition = valSymbol.String(); +# ifdef DYNAMIC_SHADER_COMPILE_VERBOSE + Warning( " %s=%s", macros[macroIndex].Name, macros[macroIndex].Definition ); +# endif + macroIndex++; + } + +# ifdef DYNAMIC_SHADER_COMPILE_VERBOSE + Warning( "\n\tstatic:" ); +# endif + for( i = 0; i < combos.m_StaticCombos.Count(); i++ ) + { + int countForCombo = combos.m_StaticCombos[i].m_nMax - combos.m_StaticCombos[i].m_nMin + 1; + int val = nCombo % countForCombo + combos.m_StaticCombos[i].m_nMin; + nCombo /= countForCombo; + macros[macroIndex].Name = m_ShaderSymbolTable.String( combos.m_StaticCombos[i].m_ComboName ); + char buf[16]; + sprintf( buf, "%d", val ); + CUtlSymbol valSymbol( buf ); + macros[macroIndex].Definition = valSymbol.String(); +# ifdef DYNAMIC_SHADER_COMPILE_VERBOSE + Warning( " %s=%s", macros[macroIndex].Name, macros[macroIndex].Definition ); +# endif + macroIndex++; + } + +# ifdef DYNAMIC_SHADER_COMPILE_VERBOSE + Warning( "\n" ); +# endif + + char filename[MAX_PATH]; + Q_strncpy( filename, GetShaderSourcePath(), MAX_PATH ); + Q_strncat( filename, "\\", MAX_PATH, COPY_ALL_CHARACTERS ); + Q_strncat( filename, pShaderName, MAX_PATH, COPY_ALL_CHARACTERS ); + Q_strncat( filename, ".fxc", MAX_PATH, COPY_ALL_CHARACTERS ); + + const char *pShaderModel = FileNameToShaderModel( pShaderName, bVertexShader ); + + // define the shader model + char shaderModelDefineString[1024]; + Q_snprintf( shaderModelDefineString, 1024, "SHADER_MODEL_%s", pShaderModel ); + Q_strupr( shaderModelDefineString ); + macros[macroIndex].Name = shaderModelDefineString; + macros[macroIndex].Definition = "1"; + macroIndex++; + + char x360DefineString[1024]; + if( IsX360() ) + { + Q_snprintf( x360DefineString, 1024, "_X360", pShaderModel ); + Q_strupr( x360DefineString ); + macros[macroIndex].Name = x360DefineString; + macros[macroIndex].Definition = "1"; + macroIndex++; + } + + // NULL terminate. + macros[macroIndex].Name = NULL; + macros[macroIndex].Definition = NULL; + + // Instead of erroring out, infinite-loop on shader compilation + // (i.e. give developers a chance to fix the shader code w/out restarting the game) +#ifndef _DEBUG + int retriesLeft = 20; +retry_compile: +#endif + + // Try and open the file to see if it exists + FileHandle_t fp = g_pFullFileSystem->Open( filename, "r" ); + + if ( fp == FILESYSTEM_INVALID_HANDLE ) + { + // Maybe this is a specific version [20 & 20b] -> [2x] + if ( strlen( pShaderName ) >= 3 ) + { + char *pszEndFilename = filename + strlen( filename ); + if ( !Q_stricmp( pszEndFilename - 6, "30.fxc" ) ) + { + strcpy( pszEndFilename - 6, "20b.fxc" ); + fp = g_pFullFileSystem->Open( filename, "r" ); + if ( fp == FILESYSTEM_INVALID_HANDLE ) + { + strcpy( pszEndFilename - 6, "2x.fxc" ); + fp = g_pFullFileSystem->Open( filename, "r" ); + } + if ( fp == FILESYSTEM_INVALID_HANDLE ) + { + strcpy( pszEndFilename - 6, "20.fxc" ); + fp = g_pFullFileSystem->Open( filename, "r" ); + } + } + else + { + if ( !Q_stricmp( pszEndFilename - 6, "20.fxc" ) ) + { + pszEndFilename[ -5 ] = 'x'; + fp = g_pFullFileSystem->Open( filename, "r" ); + } + else if ( !Q_stricmp( pszEndFilename - 7, "20b.fxc" ) ) + { + strcpy( pszEndFilename - 7, "2x.fxc" ); + fp = g_pFullFileSystem->Open( filename, "r" ); + } + else if ( !stricmp( pszEndFilename - 6, "11.fxc" ) ) + { + strcpy( pszEndFilename - 6, "xx.fxc" ); + fp = g_pFullFileSystem->Open( filename, "r" ); + } + + if ( fp == FILESYSTEM_INVALID_HANDLE ) + { + if ( !stricmp( pszEndFilename - 6, "2x.fxc" ) ) + { + pszEndFilename[ -6 ] = 'x'; + fp = g_pFullFileSystem->Open( filename, "r" ); + } + } + } + } + } + + if ( fp != FILESYSTEM_INVALID_HANDLE ) + { + g_pFullFileSystem->Close( fp ); + } + +#ifdef REMOTE_DYNAMIC_SHADER_COMPILE + #define SEND_BUF_SIZE 40000 + #define RECV_BUF_SIZE 40000 + + // Remotely-compiled shader code + uint32 *pRemotelyCompiledShader = NULL; + uint32 nRemotelyCompiledShaderLength = 0; + + if ( m_RemoteShaderCompileSocket == INVALID_SOCKET ) + { + InitRemoteShaderCompile(); + } + + // In this case, we're going to use a remote service to do our compiling + if ( m_RemoteShaderCompileSocket != INVALID_SOCKET ) + { + // Build up command list for remote shader compiler + char pSendbuf[SEND_BUF_SIZE], pRecvbuf[RECV_BUF_SIZE], pFixedFilename[MAX_PATH], buf[MAX_PATH]; + V_FixupPathName( pFixedFilename, MAX_PATH, filename ); + V_FileBase( pFixedFilename, buf, MAX_PATH ); // Just find base filename + V_strncat( buf, ".fxc", MAX_PATH ); + V_snprintf( pSendbuf, SEND_BUF_SIZE, "%s\n", buf ); + V_strncat( pSendbuf, pShaderModel, SEND_BUF_SIZE ); + V_strncat( pSendbuf, "\n", SEND_BUF_SIZE ); + V_snprintf( buf, MAX_PATH, "%d\n", macros.Count() ); + V_strncat( pSendbuf, buf, SEND_BUF_SIZE ); + for ( int i=0; i < macros.Count(); i++ ) + { + V_snprintf( buf, MAX_PATH, "%s\n%s\n", macros[i].Name, macros[i].Definition ); + V_strncat( pSendbuf, buf, SEND_BUF_SIZE ); + } + V_strncat( pSendbuf, "", SEND_BUF_SIZE ); + + // Send commands to remote shader compiler + int nResult = send( m_RemoteShaderCompileSocket, pSendbuf, (int)strlen( pSendbuf ), 0 ); + if ( nResult == SOCKET_ERROR ) + { + Warning( "send failed: %d\n", WSAGetLastError() ); + DeinitRemoteShaderCompile(); + } + + if ( m_RemoteShaderCompileSocket != INVALID_SOCKET ) + { + // Block here until we get a result back from the server + nResult = recv( m_RemoteShaderCompileSocket, pRecvbuf, RECV_BUF_SIZE, 0 ); + if ( nResult == 0 ) + { + Warning( "Connection closed\n" ); + DeinitRemoteShaderCompile(); + } + else if ( nResult < 0 ) + { + Warning( "recv failed: %d\n", WSAGetLastError() ); + DeinitRemoteShaderCompile(); + } + + if ( m_RemoteShaderCompileSocket != INVALID_SOCKET ) + { + // Grab the first 32 bits, which tell us what the rest of the data is + uint32 nCompileResultCode; + memcpy( &nCompileResultCode, pRecvbuf, sizeof( nCompileResultCode ) ); + + // If is zero, we have an error, so the rest of the data is a text string from the compiler + if ( nCompileResultCode == 0x00000000 ) + { + Warning( "Remote shader compile error: %s\n", pRecvbuf+4 ); + } + else // we have an actual binary shader blob coming back + { + nRemotelyCompiledShaderLength = nCompileResultCode; + pRemotelyCompiledShader = (uint32 *) pRecvbuf; + pRemotelyCompiledShader++; + } + } + } + } // End using remote compile service +#endif // REMOTE_DYNAMIC_SHADER_COMPILE + +#if defined( DYNAMIC_SHADER_COMPILE ) + bool bShadersNeedFlush = false; +#endif + +#if defined( DYNAMIC_SHADER_COMPILE ) && !defined( REMOTE_DYNAMIC_SHADER_COMPILE ) + LPD3DXBUFFER pShader = NULL; + LPD3DXBUFFER pErrorMessages = NULL; + HRESULT hr = S_OK; + bool b30Shader = !Q_stricmp( pShaderModel, "vs_3_0" ) || !Q_stricmp( pShaderModel, "ps_3_0" ); + + if ( m_ShaderCompileFileFunc30 && b30Shader ) + { + CDxInclude dxInclude( filename ); + hr = m_ShaderCompileFileFunc30( filename, macros.Base(), &dxInclude, + "main", pShaderModel, 0 /* DWORD Flags */, &pShader, &pErrorMessages, NULL /* LPD3DXCONSTANTTABLE *ppConstantTable */ ); + } + else + { +# if ( !defined( _X360 ) ) + { + if ( b30Shader ) + { + Warning( "Compiling with a stale version of d3dx. Should have d3d9x_33.dll installed (Apr 2007)\n" ); + } + hr = D3DXCompileShaderFromFile( filename, macros.Base(), NULL /* LPD3DXINCLUDE */, + "main", pShaderModel, 0 /* DWORD Flags */, &pShader, &pErrorMessages, NULL /* LPD3DXCONSTANTTABLE *ppConstantTable */ ); + + +#ifdef REMOTE_DYNAMIC_SHADER_COMPILE + // If we're using the remote compiling service, let's double-check against a local compile + if ( ( m_RemoteShaderCompileSocket != INVALID_SOCKET ) && pRemotelyCompiledShader ) + { + if ( ( memcmp( pRemotelyCompiledShader, pShader->GetBufferPointer(), pShader->GetBufferSize() ) != 0 ) || + ( pShader->GetBufferSize() != nRemotelyCompiledShaderLength) ) + { + Warning( "Remote and local shaders don't match!\n" ); + return INVALID_HARDWARE_SHADER; + } + } +#endif // REMOTE_DYNAMIC_SHADER_COMPILE + + } +# else + { + D3DXSHADER_COMPILE_PARAMETERS compileParams; + memset( &compileParams, 0, sizeof( compileParams ) ); + + char pUPDBOutputFile[MAX_PATH] = ""; //where we write the file + char pUPDBPIXLookup[MAX_PATH] = ""; //where PIX (on a pc) looks for the file + + compileParams.Flags |= D3DXSHADEREX_OPTIMIZE_UCODE; + + if( mat_flushshaders_generate_updbs.GetBool() ) + { + //UPDB generation for PIX debugging + compileParams.Flags |= D3DXSHADEREX_GENERATE_UPDB; + compileParams.UPDBPath = pUPDBPIXLookup; + + Q_snprintf( pUPDBOutputFile, MAX_PATH, "%s\\UPDB_X360\\%s_S%d_D%d.updb", GetShaderSourcePath(), pShaderName, nStaticIndex, nDynamicIndex ); + + //replace "net:\smb" with another "\" turning the xbox network address format into the pc network address format + V_strcpy_safe( pUPDBPIXLookup, &pUPDBOutputFile[7] ); + pUPDBPIXLookup[0] = '\\'; + } + + hr = D3DXCompileShaderFromFileEx( filename, macros.Base(), NULL /* LPD3DXINCLUDE */, + "main", pShaderModel, 0 /* DWORD Flags */, &pShader, &pErrorMessages, NULL /* LPD3DXCONSTANTTABLE *ppConstantTable */, &compileParams ); + + if( (pUPDBOutputFile[0] != '\0') && compileParams.pUPDBBuffer ) //Did we generate a updb? + { + CUtlBuffer outbuffer; + DWORD dataSize = compileParams.pUPDBBuffer->GetBufferSize(); + outbuffer.EnsureCapacity( dataSize ); + memcpy( outbuffer.Base(), compileParams.pUPDBBuffer->GetBufferPointer(), dataSize ); + outbuffer.SeekPut( CUtlBuffer::SEEK_CURRENT, dataSize ); + g_pFullFileSystem->WriteFile( pUPDBOutputFile, NULL, outbuffer ); + + compileParams.pUPDBBuffer->Release(); + } + } +# endif + } + + if ( hr != D3D_OK ) + { + const char *pErrorMessageString = ( const char * )pErrorMessages->GetBufferPointer(); + Plat_DebugString( pErrorMessageString ); + Plat_DebugString( "\n" ); + +#ifndef _DEBUG + if ( retriesLeft-- > 0 ) + { + DevMsg( 0, "Failed dynamic shader compiled - fix the shader while the debugger is at the breakpoint, then continue\n" ); + DebuggerBreakIfDebugging(); +#if defined( DYNAMIC_SHADER_COMPILE ) + bShadersNeedFlush = true; +#endif + goto retry_compile; + } + if( !IsX360() ) //errors make the 360 puke and die. We have a better solution for this particular error + Error( "Failed dynamic shader compile\nBuild shaderapidx9.dll in debug to find problem\n" ); +#else + Assert( 0 ); + +#endif // _DEBUG + + return INVALID_HARDWARE_SHADER; + } + else +#endif // #if defined( DYNAMIC_SHADER_COMPILE ) && !defined( REMOTE_DYNAMIC_SHADER_COMPILE ) + + { +#ifdef DYNAMIC_SHADER_COMPILE_WRITE_ASSEMBLY + // enable to dump the disassembly for shader validation + char exampleCommandLine[2048]; + Q_strncpy( exampleCommandLine, "// Run from stdshaders\n// ..\\..\\dx9sdk\\utilities\\fxc.exe ", sizeof( exampleCommandLine ) ); + int i; + for( i = 0; macros[i].Name; i++ ) + { + Q_strncat( exampleCommandLine, "/D", sizeof( exampleCommandLine ) ); + Q_strncat( exampleCommandLine, macros[i].Name, sizeof( exampleCommandLine ) ); + Q_strncat( exampleCommandLine, "=", sizeof( exampleCommandLine ) ); + Q_strncat( exampleCommandLine, macros[i].Definition, sizeof( exampleCommandLine ) ); + Q_strncat( exampleCommandLine, " ", sizeof( exampleCommandLine ) ); + } + + Q_strncat( exampleCommandLine, "/T", sizeof( exampleCommandLine ) ); + Q_strncat( exampleCommandLine, pShaderModel, sizeof( exampleCommandLine ) ); + Q_strncat( exampleCommandLine, " ", sizeof( exampleCommandLine ) ); + Q_strncat( exampleCommandLine, filename, sizeof( exampleCommandLine ) ); + Q_strncat( exampleCommandLine, "\n", sizeof( exampleCommandLine ) ); + + ID3DXBuffer *pd3dxBuffer; + HRESULT hr; + hr = D3DXDisassembleShader( ( DWORD* )pShader->GetBufferPointer(), false, NULL, &pd3dxBuffer ); + Assert( hr == D3D_OK ); + CUtlBuffer tempBuffer; + tempBuffer.SetBufferType( true, false ); + int exampleCommandLineLength = strlen( exampleCommandLine ); + tempBuffer.EnsureCapacity( pd3dxBuffer->GetBufferSize() + exampleCommandLineLength ); + memcpy( tempBuffer.Base(), exampleCommandLine, exampleCommandLineLength ); + memcpy( ( char * )tempBuffer.Base() + exampleCommandLineLength, pd3dxBuffer->GetBufferPointer(), pd3dxBuffer->GetBufferSize() ); + tempBuffer.SeekPut( CUtlBuffer::SEEK_CURRENT, pd3dxBuffer->GetBufferSize() + exampleCommandLineLength ); + char filename[MAX_PATH]; + sprintf( filename, "%s_%d_%d.asm", pShaderName, nStaticIndex, nDynamicIndex ); + g_pFullFileSystem->WriteFile( filename, "DEFAULT_WRITE_PATH", tempBuffer ); +#endif + +#ifdef REMOTE_DYNAMIC_SHADER_COMPILE + if ( bVertexShader ) + { + return CreateD3DVertexShader( ( DWORD * )pRemotelyCompiledShader, nRemotelyCompiledShaderLength, pShaderName ); + } + else + { + return CreateD3DPixelShader( ( DWORD * )pRemotelyCompiledShader, 0, nRemotelyCompiledShaderLength, pShaderName ); // hack hack hack! need to get centroid info from the source + } +#else // local compile, not remote + if ( bVertexShader ) + { + return CreateD3DVertexShader( ( DWORD * )pShader->GetBufferPointer(), pShader->GetBufferSize(), pShaderName ); + } + else + { + return CreateD3DPixelShader( ( DWORD * )pShader->GetBufferPointer(), 0, pShader->GetBufferSize(), pShaderName ); // hack hack hack! need to get centroid info from the source + } +#endif + +#if defined( DYNAMIC_SHADER_COMPILE ) + // We keep up with whether we hit a compile error above. If we did, then we likely need to recompile everything again since we could have changed global code. + if ( bShadersNeedFlush ) + { + MatFlushShaders(); + } +#endif + } + +#ifndef REMOTE_DYNAMIC_SHADER_COMPILE + if ( pShader ) + { + pShader->Release(); + } +#endif + +#if defined( DYNAMIC_SHADER_COMPILE ) && !defined( REMOTE_DYNAMIC_SHADER_COMPILE ) + if ( pErrorMessages ) + { + pErrorMessages->Release(); + } +#endif + +} +#endif + +#ifdef DYNAMIC_SHADER_COMPILE +bool CShaderManager::LoadAndCreateShaders_Dynamic( ShaderLookup_t &lookup, bool bVertexShader ) +{ + const char *pName = m_ShaderSymbolTable.String( lookup.m_Name ); + const ShaderCombos_t *pCombos = FindOrCreateShaderCombos( pName ); + if ( !pCombos ) + { + return false; + } + + int numDynamicCombos = pCombos->GetNumDynamicCombos(); + lookup.m_ShaderStaticCombos.m_pHardwareShaders = new HardwareShader_t[numDynamicCombos]; + lookup.m_ShaderStaticCombos.m_nCount = numDynamicCombos; + lookup.m_ShaderStaticCombos.m_pCreationData = new ShaderStaticCombos_t::ShaderCreationData_t[numDynamicCombos]; + + int i; + for( i = 0; i < numDynamicCombos; i++ ) + { + lookup.m_ShaderStaticCombos.m_pHardwareShaders[i] = INVALID_HARDWARE_SHADER; + } + return true; +} +#endif + +//----------------------------------------------------------------------------- +// Open the shader file, optionally gets the header +//----------------------------------------------------------------------------- +FileHandle_t CShaderManager::OpenFileAndLoadHeader( const char *pFileName, ShaderHeader_t *pHeader ) +{ + FileHandle_t fp = g_pFullFileSystem->Open( pFileName, "rb", "GAME" ); + if ( fp == FILESYSTEM_INVALID_HANDLE ) + { + return FILESYSTEM_INVALID_HANDLE; + } + + if ( pHeader ) + { + // read the header + g_pFullFileSystem->Read( pHeader, sizeof( ShaderHeader_t ), fp ); + + switch ( pHeader->m_nVersion ) + { + case 4: + // version with combos done as diffs vs a reference combo + // vsh/psh or older fxc + break; + + case 5: + case 6: + // version with optimal dictionary and compressed combo block + break; + + default: + Assert( 0 ); + Warning( "Shader %s is the wrong version %d, expecting %d\n", pFileName, pHeader->m_nVersion, SHADER_VCS_VERSION_NUMBER ); + g_pFullFileSystem->Close( fp ); + return FILESYSTEM_INVALID_HANDLE; + } + } + + return fp; +} + +//--------------------------------------------------------------------------------------------------------- +// Writes text files named for looked-up shaders. Used by GL shader translator to dump code for debugging +//--------------------------------------------------------------------------------------------------------- +void CShaderManager::WriteTranslatedFile( ShaderLookup_t *pLookup, int dynamicCombo, char *pFileContents, char *pFileExtension ) +{ + const char *pName = m_ShaderSymbolTable.String( pLookup->m_Name ); + int nNumChars = V_strlen( pFileContents ); + + CUtlBuffer tempBuffer; + tempBuffer.SetBufferType( true, false ); + tempBuffer.EnsureCapacity( nNumChars ); + memcpy( ( char * )tempBuffer.Base(), pFileContents, nNumChars ); + tempBuffer.SeekPut( CUtlBuffer::SEEK_CURRENT, nNumChars ); + + char filename[MAX_PATH]; + sprintf( filename, "%s_%d_%d.%s", pName, pLookup->m_nStaticIndex, dynamicCombo, pFileExtension ); + g_pFullFileSystem->WriteFile( filename, "DEFAULT_WRITE_PATH", tempBuffer ); +} + +//----------------------------------------------------------------------------- +// Disassemble a shader for debugging. Writes .asm files. +//----------------------------------------------------------------------------- +void CShaderManager::DisassembleShader( ShaderLookup_t *pLookup, int dynamicCombo, uint8 *pByteCode ) +{ +#if defined( WRITE_ASSEMBLY ) + const char *pName = m_ShaderSymbolTable.String( pLookup->m_Name ); + + ID3DXBuffer *pd3dxBuffer; + HRESULT hr; + hr = D3DXDisassembleShader( (DWORD*)pByteCode, false, NULL, &pd3dxBuffer ); + Assert( hr == D3D_OK ); + + CUtlBuffer tempBuffer; + tempBuffer.SetBufferType( true, false ); + tempBuffer.EnsureCapacity( pd3dxBuffer->GetBufferSize() ); + memcpy( ( char * )tempBuffer.Base(), pd3dxBuffer->GetBufferPointer(), pd3dxBuffer->GetBufferSize() ); + tempBuffer.SeekPut( CUtlBuffer::SEEK_CURRENT, pd3dxBuffer->GetBufferSize() ); + + char filename[MAX_PATH]; + sprintf( filename, "%s_%d_%d.asm", pName, pLookup->m_nStaticIndex, dynamicCombo ); + g_pFullFileSystem->WriteFile( filename, "DEFAULT_WRITE_PATH", tempBuffer ); +#endif +} + +//----------------------------------------------------------------------------- +// Create dynamic combos +//----------------------------------------------------------------------------- +bool CShaderManager::CreateDynamicCombos_Ver4( void *pContext, uint8 *pComboBuffer ) +{ + ShaderLookup_t* pLookup = (ShaderLookup_t *)pContext; + + ShaderFileCache_t *pFileCache = &m_ShaderFileCache[pLookup->m_hShaderFileCache]; + ShaderHeader_t *pHeader = &pFileCache->m_Header; + + int nReferenceComboSizeForDiffs = ((ShaderHeader_t_v4 *)pHeader)->m_nDiffReferenceSize; + + uint8 *pReferenceShader = NULL; + uint8 *pDiffOutputBuffer = NULL; + if ( nReferenceComboSizeForDiffs ) + { + // reference combo is *always* the largest combo, so safe worst case size for uncompression buffer + pReferenceShader = (uint8 *)pFileCache->m_ReferenceCombo.Base(); + pDiffOutputBuffer = (uint8 *)_alloca( nReferenceComboSizeForDiffs ); + } + + // build this shader's dynamic combos + bool bOK = true; + int nStartingOffset = 0; + for ( int i = 0; i < pHeader->m_nDynamicCombos; i++ ) + { + if ( pLookup->m_pComboDictionary[i].m_Offset == -1 ) + { + // skipped + continue; + } + + if ( !nStartingOffset ) + { + nStartingOffset = pLookup->m_pComboDictionary[i].m_Offset; + } + + // offsets better be sequentially ascending + Assert( nStartingOffset <= pLookup->m_pComboDictionary[i].m_Offset ); + + if ( pLookup->m_pComboDictionary[i].m_Size <= 0 ) + { + // skipped + continue; + } + + // get the right byte code from the monolithic buffer + uint8 *pByteCode = (uint8 *)pComboBuffer + pLookup->m_nDataOffset + pLookup->m_pComboDictionary[i].m_Offset - nStartingOffset; + int nByteCodeSize = pLookup->m_pComboDictionary[i].m_Size; + + if ( pReferenceShader ) + { + // reference combo better be the largest combo, otherwise memory corruption + Assert( nReferenceComboSizeForDiffs >= nByteCodeSize ); + + // use the differencing algorithm to recover the full shader + int nOriginalSize; + ApplyDiffs( + pReferenceShader, + pByteCode, + nReferenceComboSizeForDiffs, + nByteCodeSize, + nOriginalSize, + pDiffOutputBuffer, + nReferenceComboSizeForDiffs ); + + pByteCode = pDiffOutputBuffer; + nByteCodeSize = nOriginalSize; + } + +#if defined( WRITE_ASSEMBLY ) + DisassembleShader( pLookup, i, pByteCode ); +#endif + HardwareShader_t hardwareShader = INVALID_HARDWARE_SHADER; + + if ( IsPC() && m_bCreateShadersOnDemand ) + { + // cache the code off for later + pLookup->m_ShaderStaticCombos.m_pCreationData[i].ByteCode.SetSize( nByteCodeSize ); + V_memcpy( pLookup->m_ShaderStaticCombos.m_pCreationData[i].ByteCode.Base(), pByteCode, nByteCodeSize ); + pLookup->m_ShaderStaticCombos.m_pCreationData[i].iCentroidMask = pFileCache->m_bVertexShader ? 0 : pHeader->m_nCentroidMask; + } + else + { + const char *pShaderName = m_ShaderSymbolTable.String( pLookup->m_Name ); + if ( pFileCache->m_bVertexShader ) + { + hardwareShader = CreateD3DVertexShader( reinterpret_cast< DWORD *>( pByteCode ), nByteCodeSize, pShaderName ); + } + else + { + hardwareShader = CreateD3DPixelShader( reinterpret_cast< DWORD *>( pByteCode ), pHeader->m_nCentroidMask, nByteCodeSize, pShaderName ); + } + + if ( hardwareShader == INVALID_HARDWARE_SHADER ) + { + Assert( 0 ); + bOK = false; + break; + } + } + pLookup->m_ShaderStaticCombos.m_pHardwareShaders[i] = hardwareShader; + } + + delete [] pLookup->m_pComboDictionary; + pLookup->m_pComboDictionary = NULL; + + return bOK; +} + +//----------------------------------------------------------------------------- +// Create dynamic combos +//----------------------------------------------------------------------------- +static uint32 NextULONG( uint8 * &pData ) +{ + // handle unaligned read + uint32 nRet; + memcpy( &nRet, pData, sizeof( nRet ) ); + pData += sizeof( nRet ); + return nRet; +} + + +bool CShaderManager::CreateDynamicCombos_Ver5( void *pContext, uint8 *pComboBuffer, char *debugLabel ) +{ + ShaderLookup_t* pLookup = (ShaderLookup_t *)pContext; + ShaderFileCache_t *pFileCache = &m_ShaderFileCache[pLookup->m_hShaderFileCache]; + uint8 *pCompressedShaders = pComboBuffer + pLookup->m_nDataOffset; + + uint8 *pUnpackBuffer = new uint8[MAX_SHADER_UNPACKED_BLOCK_SIZE]; + + char *debugLabelPtr = debugLabel; // can be moved to point at something else if need be + + // now, loop through all blocks + bool bOK = true; + while ( bOK ) + { + uint32 nBlockSize = NextULONG( pCompressedShaders ); + if ( nBlockSize == 0xffffffff ) + { + // any more blocks? + break; + } + + switch( nBlockSize & 0xc0000000 ) + { + case 0: // bzip2 + { + // uncompress + uint32 nOutsize = MAX_SHADER_UNPACKED_BLOCK_SIZE; + int nRslt = BZ2_bzBuffToBuffDecompress( + reinterpret_cast( pUnpackBuffer ), + &nOutsize, + reinterpret_cast( pCompressedShaders ), + nBlockSize, 1, 0 ); + if ( nRslt < 0 ) + { + // errors are negative for bzip + Assert( 0 ); + Warning( "BZIP Error (%d) decompressing shader", nRslt ); + bOK = false; + } + + pCompressedShaders += nBlockSize; + nBlockSize = nOutsize; // how much data there is + } + break; + + case 0x80000000: // uncompressed + { + // not compressed, as is + nBlockSize &= 0x3fffffff; + memcpy( pUnpackBuffer, pCompressedShaders, nBlockSize ); + pCompressedShaders += nBlockSize; + } + break; + + case 0x40000000: // lzma compressed + { + nBlockSize &= 0x3fffffff; + + size_t nOutsize = CLZMA::Uncompress( + reinterpret_cast( pCompressedShaders ), + pUnpackBuffer ); + pCompressedShaders += nBlockSize; + nBlockSize = nOutsize; // how much data there is + } + break; + + default: + { + Assert( 0 ); + Error(" unrecognized shader compression type = file corrupt?"); + bOK = false; + } + } + + uint8 *pReadPtr = pUnpackBuffer; + while ( pReadPtr < pUnpackBuffer+nBlockSize ) + { + uint32 nCombo_ID = NextULONG( pReadPtr ); + uint32 nShaderSize = NextULONG( pReadPtr ); + +#if defined( WRITE_ASSEMBLY ) + DisassembleShader( pLookup, nCombo_ID, pReadPtr ); +#endif + HardwareShader_t hardwareShader = INVALID_HARDWARE_SHADER; + + int iIndex = nCombo_ID; + if ( iIndex >= pLookup->m_nStaticIndex ) + iIndex -= pLookup->m_nStaticIndex; // ver5 stores combos as full combo, ver6 as dynamic combo # only + if ( IsPC() && m_bCreateShadersOnDemand ) + { + // cache the code off for later + pLookup->m_ShaderStaticCombos.m_pCreationData[iIndex].ByteCode.SetSize( nShaderSize ); + V_memcpy( pLookup->m_ShaderStaticCombos.m_pCreationData[iIndex].ByteCode.Base(), pReadPtr, nShaderSize ); + pLookup->m_ShaderStaticCombos.m_pCreationData[iIndex].iCentroidMask = pFileCache->m_bVertexShader ? 0 : pFileCache->m_Header.m_nCentroidMask; + } + else + { + const char *pShaderName = m_ShaderSymbolTable.String( pLookup->m_Name ); + + if ( pFileCache->m_bVertexShader ) + { +#if 0 + // this is all test code + CUtlBuffer bufGLCode( 1000, 50000, CUtlBuffer::TEXT_BUFFER ); + CUtlBuffer bufNewGLCode( 1000, 50000, CUtlBuffer::TEXT_BUFFER ); + CUtlBuffer bufGLSLCode( 1000, 50000, CUtlBuffer::TEXT_BUFFER ); + bool bVertexShader; + + uint32 nOptions = 0; + nOptions |= D3DToGL_OptionUseEnvParams; + nOptions |= D3DToGL_OptionDoFixupZ; + nOptions |= D3DToGL_OptionDoFixupY; + //options |= D3DToGL_OptionSpew; + +// sg_D3DToOpenGLTranslator.TranslateShader( (uint32 *) pReadPtr, (char *)bufGLCode.Base(), bufGLCode.Size(), &bVertexShader, nOptions, -1, debugLabel ); +// sg_NewD3DToOpenGLTranslator.TranslateShader( (uint32 *) pReadPtr, &bufNewGLCode, &bVertexShader, nOptions, -1, debugLabel ); + + +// bool bDumpGLSL = false; +// if ( !stricmp( "vs-file vertexlit_and_unlit_generic_bump_vs20 vs-index 144", debugLabel ) && ( iIndex == 0 ) ) +// { +// DisassembleShader( pLookup, iIndex, pReadPtr ); // Direct3D +// bDumpGLSL = true; +// } + + + // GLSL options + nOptions |= D3DToGL_OptionGLSL; // | D3DToGL_AddHexComments | D3DToGL_PutHexCommentsAfterLines; + if ( !IsOSX() ) + { + nOptions |= D3DToGL_OptionAllowStaticControlFlow; + } + sg_NewD3DToOpenGLTranslator.TranslateShader( (uint32 *) pReadPtr, &bufGLSLCode, &bVertexShader, nOptions, -1, 0, debugLabel ); + Assert( bVertexShader ); + + // Test to make sure these are identical +// if ( bDumpGLSL )//V_strcmp( (char *)bufGLCode.Base(), (char *)bufNewGLCode.Base() ) ) +// { +// WriteTranslatedFile( pLookup, iIndex, (char *)bufGLCode.Base(), "avp" ); // Old +// WriteTranslatedFile( pLookup, iIndex, (char *)bufNewGLCode.Base(), "avp2" ); // New + WriteTranslatedFile( pLookup, iIndex, (char *)bufGLSLCode.Base(), "glsl_v" ); // GLSL +// DisassembleShader( pLookup, iIndex, pReadPtr ); // Direct3D +// } + + #if defined( WRITE_ASSEMBLY ) + WriteTranslatedFile( pLookup, iIndex, (char *)bufGLCode.Base(), "avp" ); + #endif +#endif // 0 + +#ifdef DX_TO_GL_ABSTRACTION + // munge the debug label a bit to aid in decoding... catenate the iIndex on the end + char temp[1024]; + sprintf(temp, "%s vs-combo %d", (debugLabel)?debugLabel:"none", iIndex ); + debugLabelPtr = temp; +#endif + // pass binary code to d3d interface, on GL it will invoke the translator back to asm + hardwareShader = CreateD3DVertexShader( reinterpret_cast< DWORD *>( pReadPtr ), nShaderSize, pShaderName, debugLabelPtr ); + } + else + { +#if 0 + // this is all test code +// CUtlBuffer bufGLCode( 1000, 50000, CUtlBuffer::TEXT_BUFFER ); +// CUtlBuffer bufNewGLCode( 1000, 50000, CUtlBuffer::TEXT_BUFFER ); + CUtlBuffer bufGLSLCode( 1000, 50000, CUtlBuffer::TEXT_BUFFER ); + bool bVertexShader; + + + uint32 nOptions = D3DToGL_OptionUseEnvParams; + +// sg_D3DToOpenGLTranslator.TranslateShader( (uint32 *) pReadPtr, (char *)bufGLCode.Base(), bufGLCode.Size(), &bVertexShader, D3DToGL_OptionUseEnvParams, -1, debugLabel ); +// sg_NewD3DToOpenGLTranslator.TranslateShader( (uint32 *) pReadPtr, &bufNewGLCode, &bVertexShader, D3DToGL_OptionUseEnvParams, -1, debugLabel ); + + // GLSL options + nOptions |= D3DToGL_OptionGLSL;// | D3DToGL_OptionSRGBWriteSuffix | D3DToGL_AddHexComments | D3DToGL_PutHexCommentsAfterLines; + if ( !IsOSX() ) + { + nOptions |= D3DToGL_OptionAllowStaticControlFlow; + } + sg_NewD3DToOpenGLTranslator.TranslateShader( (uint32 *) pReadPtr, &bufGLSLCode, &bVertexShader, nOptions, -1, 0, debugLabel ); + + Assert( !bVertexShader ); + + // Test to make sure these are identical +// if ( V_strcmp( (char *)bufGLCode.Base(), (char *)bufNewGLCode.Base() ) ) +// { +// WriteTranslatedFile( pLookup, iIndex, (char *)bufGLCode.Base(), "afp" ); // Old +// WriteTranslatedFile( pLookup, iIndex, (char *)bufNewGLCode.Base(), "afp2" ); // New + WriteTranslatedFile( pLookup, iIndex, (char *)bufGLSLCode.Base(), "glsl_p" ); // GLSL +// DisassembleShader( pLookup, iIndex, pReadPtr ); // Direct3D +// } + + #if defined( WRITE_ASSEMBLY ) + WriteTranslatedFile( pLookup, iIndex, (char *)bufGLCode.Base(), "afp" ); + #endif +#endif // 0 + +#ifdef DX_TO_GL_ABSTRACTION + // munge the debug label a bit to aid in decoding... catenate the iIndex on the end + char temp[1024]; + sprintf(temp, "%s ps-combo %d", (debugLabel)?debugLabel:"", iIndex ); + debugLabelPtr = temp; +#endif + + // pass binary code to d3d interface, on GL it will invoke the translator back to asm + hardwareShader = CreateD3DPixelShader( reinterpret_cast< DWORD *>( pReadPtr ), pFileCache->m_Header.m_nCentroidMask, nShaderSize, pShaderName, debugLabelPtr ); + } + if ( hardwareShader == INVALID_HARDWARE_SHADER ) + { + Warning( "failed to create shader\n" ); + Assert( 0 ); + bOK = false; + break; + } + } + pLookup->m_ShaderStaticCombos.m_pHardwareShaders[iIndex] = hardwareShader; + pReadPtr += nShaderSize; + } + } + + delete[] pUnpackBuffer; + + return bOK; +} + +//----------------------------------------------------------------------------- +// Static method, called by thread, don't call anything non-threadsafe from handler!!! +//----------------------------------------------------------------------------- +void CShaderManager::QueuedLoaderCallback( void *pContext, void *pContext2, const void *pData, int nSize, LoaderError_t loaderError ) +{ + ShaderLookup_t* pLookup = (ShaderLookup_t *)pContext; + + bool bOK = ( loaderError == LOADERERROR_NONE ); + if ( bOK ) + { + if ( pContext2 ) + { + // presence denotes diff version + bOK = s_ShaderManager.CreateDynamicCombos_Ver4( pContext, (uint8 *)pData ); + } + else + { + bOK = s_ShaderManager.CreateDynamicCombos_Ver5( pContext, (uint8 *)pData ); + } + } + if ( !bOK ) + { + pLookup->m_Flags |= SHADER_FAILED_LOAD; + } +} + +//----------------------------------------------------------------------------- +// Loads all shaders +//----------------------------------------------------------------------------- +bool CShaderManager::LoadAndCreateShaders( ShaderLookup_t &lookup, bool bVertexShader, char *debugLabel ) +{ + const char *pName = m_ShaderSymbolTable.String( lookup.m_Name ); + + // find it in the cache + // a cache hit prevents costly i/o for static components, i.e. header, ref combo, etc. + ShaderFileCache_t fileCacheLookup; + fileCacheLookup.m_Name = lookup.m_Name; + fileCacheLookup.m_bVertexShader = bVertexShader; + int fileCacheIndex = m_ShaderFileCache.Find( fileCacheLookup ); + if ( fileCacheIndex == m_ShaderFileCache.InvalidIndex() ) + { + // not found, create a new entry + fileCacheIndex = m_ShaderFileCache.AddToTail(); + } + + lookup.m_hShaderFileCache = fileCacheIndex; + + // fetch from cache + ShaderFileCache_t *pFileCache = &m_ShaderFileCache[fileCacheIndex]; + ShaderHeader_t *pHeader = &pFileCache->m_Header; + + FileHandle_t hFile = FILESYSTEM_INVALID_HANDLE; + if ( pFileCache->IsValid() ) + { + // using cached header, just open file, no read of header needed + hFile = OpenFileAndLoadHeader( m_ShaderSymbolTable.String( pFileCache->m_Filename ), NULL ); + if ( hFile == FILESYSTEM_INVALID_HANDLE ) + { + // shouldn't happen + Assert( 0 ); + return false; + } + } + else + { + V_memset( pHeader, 0, sizeof( ShaderHeader_t ) ); + + // try the vsh/psh dir first + char filename[MAX_PATH]; + Q_snprintf( filename, MAX_PATH, "shaders\\%s\\%s" SHADER_FNAME_EXTENSION, bVertexShader ? "vsh" : "psh", pName ); + hFile = OpenFileAndLoadHeader( filename, pHeader ); + if ( hFile == FILESYSTEM_INVALID_HANDLE ) + { +#ifdef DYNAMIC_SHADER_COMPILE + // Dynamically compile if it's HLSL. + if ( LoadAndCreateShaders_Dynamic( lookup, bVertexShader ) ) + { + return true; + } + else + { + return false; + } +#endif + // next, try the fxc dir + Q_snprintf( filename, MAX_PATH, "shaders\\fxc\\%s" SHADER_FNAME_EXTENSION, pName ); + hFile = OpenFileAndLoadHeader( filename, pHeader ); + if ( hFile == FILESYSTEM_INVALID_HANDLE ) + { + lookup.m_Flags |= SHADER_FAILED_LOAD; + Warning( "Couldn't load %s shader %s\n", bVertexShader ? "vertex" : "pixel", pName ); + return false; + } + } + + lookup.m_Flags = pHeader->m_nFlags; + + pFileCache->m_Name = lookup.m_Name; + pFileCache->m_Filename = m_ShaderSymbolTable.AddString( filename ); + pFileCache->m_bVertexShader = bVertexShader; + + if ( pFileCache->IsOldVersion() ) + { + int referenceComboSize = ((ShaderHeader_t_v4 *)pHeader)->m_nDiffReferenceSize; + if ( referenceComboSize ) + { + // cache the reference combo + pFileCache->m_ReferenceCombo.EnsureCapacity( referenceComboSize ); + g_pFullFileSystem->Read( pFileCache->m_ReferenceCombo.Base(), referenceComboSize, hFile ); + } + } + else + { + // cache the dictionary + pFileCache->m_StaticComboRecords.EnsureCount( pHeader->m_nNumStaticCombos ); + g_pFullFileSystem->Read( pFileCache->m_StaticComboRecords.Base(), pHeader->m_nNumStaticCombos * sizeof( StaticComboRecord_t ), hFile ); + if ( pFileCache->IsVersion6() ) + { + // read static combo alias records + int nNumDups; + g_pFullFileSystem->Read( &nNumDups, sizeof( nNumDups ), hFile ); + if ( nNumDups ) + { + pFileCache->m_StaticComboDupRecords.EnsureCount( nNumDups ); + g_pFullFileSystem->Read( pFileCache->m_StaticComboDupRecords.Base(), nNumDups * sizeof( StaticComboAliasRecord_t ), hFile ); + } + } + + } + } + + // FIXME: should make lookup and ShaderStaticCombos_t are pool allocated. + int i; + lookup.m_ShaderStaticCombos.m_nCount = pHeader->m_nDynamicCombos; + lookup.m_ShaderStaticCombos.m_pHardwareShaders = new HardwareShader_t[pHeader->m_nDynamicCombos]; + if ( IsPC() && m_bCreateShadersOnDemand ) + { + lookup.m_ShaderStaticCombos.m_pCreationData = new ShaderStaticCombos_t::ShaderCreationData_t[pHeader->m_nDynamicCombos]; + } + for ( i = 0; i < pHeader->m_nDynamicCombos; i++ ) + { + lookup.m_ShaderStaticCombos.m_pHardwareShaders[i] = INVALID_HARDWARE_SHADER; + } + + int nStartingOffset = 0; + int nEndingOffset = 0; + + if ( pFileCache->IsOldVersion() ) + { + int nDictionaryOffset = sizeof( ShaderHeader_t ) + ((ShaderHeader_t_v4 *)pHeader)->m_nDiffReferenceSize; + + // read in shader's dynamic combos directory + lookup.m_pComboDictionary = new ShaderDictionaryEntry_t[pHeader->m_nDynamicCombos]; + g_pFullFileSystem->Seek( hFile, nDictionaryOffset + lookup.m_nStaticIndex * sizeof( ShaderDictionaryEntry_t ), FILESYSTEM_SEEK_HEAD ); + g_pFullFileSystem->Read( lookup.m_pComboDictionary, pHeader->m_nDynamicCombos * sizeof( ShaderDictionaryEntry_t ), hFile ); + + // want single read of all this shader's dynamic combos into a target buffer + // shaders are written sequentially, determine starting offset and length + for ( i = 0; i < pHeader->m_nDynamicCombos; i++ ) + { + if ( lookup.m_pComboDictionary[i].m_Offset == -1 ) + { + // skipped + continue; + } + + // ensure offsets are in fact sequentially ascending + Assert( lookup.m_pComboDictionary[i].m_Offset >= nStartingOffset && lookup.m_pComboDictionary[i].m_Size >= 0 ); + + if ( !nStartingOffset ) + { + nStartingOffset = lookup.m_pComboDictionary[i].m_Offset; + } + nEndingOffset = lookup.m_pComboDictionary[i].m_Offset + lookup.m_pComboDictionary[i].m_Size; + } + if ( !nStartingOffset ) + { + g_pFullFileSystem->Close( hFile ); + Warning( "Shader '%s' - All dynamic combos skipped. This is bad!\n", m_ShaderSymbolTable.String( pFileCache->m_Filename ) ); + return false; + } + } + else + { + int nStaticComboIdx = pFileCache->FindCombo( lookup.m_nStaticIndex / pFileCache->m_Header.m_nDynamicCombos ); + if ( nStaticComboIdx == -1 ) + { + g_pFullFileSystem->Close( hFile ); + lookup.m_Flags |= SHADER_FAILED_LOAD; + Warning( "Shader '%s' - Couldn't load combo %d of shader (dyn=%d)\n", m_ShaderSymbolTable.String( pFileCache->m_Filename ), lookup.m_nStaticIndex, pFileCache->m_Header.m_nDynamicCombos ); + return false; + } + + nStartingOffset = pFileCache->m_StaticComboRecords[nStaticComboIdx].m_nFileOffset; + nEndingOffset = pFileCache->m_StaticComboRecords[nStaticComboIdx+1].m_nFileOffset; + } + + // align offsets for unbuffered optimal i/o - fastest i/o possible + unsigned nOffsetAlign, nSizeAlign, nBufferAlign; + g_pFullFileSystem->GetOptimalIOConstraints( hFile, &nOffsetAlign, &nSizeAlign, &nBufferAlign ); + unsigned int nAlignedOffset = AlignValue( ( nStartingOffset - nOffsetAlign ) + 1, nOffsetAlign ); + unsigned int nAlignedBytesToRead = AlignValue( nEndingOffset - nAlignedOffset, nSizeAlign ); + + // used for adjusting provided buffer to actual data + lookup.m_nDataOffset = nStartingOffset - nAlignedOffset; + + bool bOK = true; + if ( IsX360() && g_pQueuedLoader->IsMapLoading() ) + { + LoaderJob_t loaderJob; + loaderJob.m_pFilename = m_ShaderSymbolTable.String( pFileCache->m_Filename ); + loaderJob.m_pPathID = "GAME"; + loaderJob.m_pCallback = QueuedLoaderCallback; + loaderJob.m_pContext = (void *)&lookup; + loaderJob.m_pContext2 = (void *)pFileCache->IsOldVersion(); + loaderJob.m_Priority = LOADERPRIORITY_DURINGPRELOAD; + loaderJob.m_nBytesToRead = nAlignedBytesToRead; + loaderJob.m_nStartOffset = nAlignedOffset; + g_pQueuedLoader->AddJob( &loaderJob ); + } + else + { + //printf("\n CShaderManager::LoadAndCreateShaders - reading %d bytes from file offset %d", nAlignedBytesToRead, nAlignedOffset); + // single optimal read of all dynamic combos into monolithic buffer + uint8 *pOptimalBuffer = (uint8 *)g_pFullFileSystem->AllocOptimalReadBuffer( hFile, nAlignedBytesToRead, nAlignedOffset ); + g_pFullFileSystem->Seek( hFile, nAlignedOffset, FILESYSTEM_SEEK_HEAD ); + g_pFullFileSystem->Read( pOptimalBuffer, nAlignedBytesToRead, hFile ); + + if ( pFileCache->IsOldVersion() ) + { + bOK = CreateDynamicCombos_Ver4( &lookup, pOptimalBuffer ); + } + else + { + bOK = CreateDynamicCombos_Ver5( &lookup, pOptimalBuffer, debugLabel ); + } + + g_pFullFileSystem->FreeOptimalReadBuffer( pOptimalBuffer ); + } + + g_pFullFileSystem->Close( hFile ); + + if ( !bOK ) + { + lookup.m_Flags |= SHADER_FAILED_LOAD; + } + + return bOK; +} + + +//----------------------------------------------------------------------------------old code + +#if 0 + + // Set this convar internally to build or add to the shader cache file + // We really only expect this to work on DX_TO_GL_ABSTRACTION + ConVar mat_cacheshaders( "mat_cacheshaders", "0", FCVAR_DEVELOPMENTONLY ); + + #define SHADER_CACHE_FILE "shader_cache.cfg" + #define PROGRAM_CACHE_FILE "program_cache.cfg" + + static void WriteToShaderCache( const char *pShaderName, const int nIndex ) + { +#ifndef DX_TO_GL_ABSTRACTION + return; +#endif + + KeyValues *pShaderCache = new KeyValues( "shadercache" ); + // we don't load anything, it starts empty.. pShaderCache->LoadFromFile( g_pFullFileSystem, SHADER_CACHE_FILE, "MOD" ); + + if ( !pShaderCache ) + { + Warning( "Could not write to shader cache file!\n" ); + return; + } + + // Subkey for specific shader + KeyValues *pShaderKey = pShaderCache->FindKey( pShaderName, true ); + Assert( pShaderKey ); + + bool bFound = false; + int nKeys = 0; + char szIndex[8]; + FOR_EACH_VALUE( pShaderKey, pValues ) + { + if ( pValues->GetInt() == nIndex ) + { + bFound = true; + } + nKeys++; + } + + if ( !bFound ) + { + V_snprintf( szIndex, 8, "%d", nKeys ); + pShaderKey->SetInt( szIndex, nIndex ); + } + + pShaderCache->SaveToFile( g_pFullFileSystem, SHADER_CACHE_FILE, "MOD" ); + pShaderCache->deleteThis(); + } + +void CShaderManager::WarmShaderCache() + { +#ifndef DX_TO_GL_ABSTRACTION + return; +#endif + + // Don't access the cache if we're building it! + if ( mat_cacheshaders.GetBool() ) + return; + + // Don't warm the cache if we're just going to monkey with the shaders anyway + #ifdef DYNAMIC_SHADER_COMPILE + return; + #endif + + double st = Sys_FloatTime(); + + + // + // First we warm SHADERS =============================================== + // + + KeyValues *pShaderCache = new KeyValues( "shadercache" ); + pShaderCache->LoadFromFile( g_pFullFileSystem, SHADER_CACHE_FILE, "MOD" ); + + if ( !pShaderCache ) + { + Warning( "Could not find shader cache file!\n" ); + return; + } + + // Run through each shader in the cache + FOR_EACH_SUBKEY( pShaderCache, pShaderKey ) + { + const char *pShaderName = pShaderKey->GetName(); + bool bVertexShader = Q_stristr( pShaderName, "_vs20" ) || Q_stristr( pShaderName, "_vs30" ); + + FOR_EACH_VALUE( pShaderKey, pValue ) + { + char temp[1024]; + int staticIndex = pValue->GetInt(); + + if ( bVertexShader ) + { + V_snprintf( temp, sizeof(temp), "vs-file %s vs-index %d", pShaderName, staticIndex ); + CreateVertexShader( pShaderName, staticIndex, temp ); + } + else + { + V_snprintf( temp, sizeof(temp), "ps-file %s ps-index %d", pShaderName, staticIndex ); + CreatePixelShader( pShaderName, staticIndex, temp ); + } + } + } + + pShaderCache->deleteThis(); + + + // + // Next, we warm PROGRAMS (which are pairs of shaders) ================= + // + + KeyValues *pProgramCache = new KeyValues( "programcache" ); + pProgramCache->LoadFromFile( g_pFullFileSystem, PROGRAM_CACHE_FILE, "MOD" ); + + if ( !pProgramCache ) + { + Warning( "Could not find program cache file!\n" ); + return; + } + + // Run through each program in the cache + FOR_EACH_SUBKEY( pProgramCache, pProgramKey ) + { + KeyValues *pValue = pProgramKey->GetFirstValue(); + const char *pVertexShaderName = pValue->GetString(); + pValue = pValue->GetNextValue(); + const char *pPixelShaderName = pValue->GetString(); + pValue = pValue->GetNextValue(); + int nVertexShaderStaticIndex = pValue->GetInt(); + pValue = pValue->GetNextValue(); + int nPixelShaderStaticIndex = pValue->GetInt(); + pValue = pValue->GetNextValue(); + int nVertexShaderDynamicIndex = pValue->GetInt(); + pValue = pValue->GetNextValue(); + int nPixelShaderDynamicIndex = pValue->GetInt(); + + ShaderLookup_t vshLookup; + vshLookup.m_Name = m_ShaderSymbolTable.AddString( pVertexShaderName ); // TODO: use String() here and catch this odd case + vshLookup.m_nStaticIndex = nVertexShaderStaticIndex; + VertexShader_t vertexShader = m_VertexShaderDict.Find( vshLookup ); + + ShaderLookup_t pshLookup; + pshLookup.m_Name = m_ShaderSymbolTable.AddString( pPixelShaderName ); + pshLookup.m_nStaticIndex = nPixelShaderStaticIndex; + PixelShader_t pixelShader = m_PixelShaderDict.Find( pshLookup ); + + // If we found both shaders, do the link! + if ( ( vertexShader != m_VertexShaderDict.InvalidIndex() ) && ( pixelShader != m_PixelShaderDict.InvalidIndex() ) ) + { + #ifdef DX_TO_GL_ABSTRACTION + //HardwareShader_t hardwareVertexShader = vshLookup.m_ShaderStaticCombos.m_pHardwareShaders[nVertexShaderDynamicIndex]; + //HardwareShader_t hardwarePixelShader = pshLookup.m_ShaderStaticCombos.m_pHardwareShaders[nPixelShaderDynamicIndex]; + + HardwareShader_t hardwareVertexShader = m_VertexShaderDict[vertexShader].m_ShaderStaticCombos.m_pHardwareShaders[nVertexShaderDynamicIndex]; + HardwareShader_t hardwarePixelShader = m_PixelShaderDict[pixelShader].m_ShaderStaticCombos.m_pHardwareShaders[nPixelShaderDynamicIndex]; + + if ( ( hardwareVertexShader != INVALID_HARDWARE_SHADER ) && ( hardwarePixelShader != INVALID_HARDWARE_SHADER ) ) + { + if ( S_OK != Dx9Device()->LinkShaderPair( (IDirect3DVertexShader9 *)hardwareVertexShader, (IDirect3DPixelShader9 *)hardwarePixelShader ) ) + { + Warning( "Could not link OpenGL shaders: %s (%d, %d) : %s (%d, %d)\n", pVertexShaderName, nVertexShaderStaticIndex, nVertexShaderDynamicIndex, pPixelShaderName, nPixelShaderStaticIndex, nPixelShaderDynamicIndex ); + } + } + #endif + } + else + { + Warning( "Invalid shader linkage: %s (%d, %d) : %s (%d, %d)\n", pVertexShaderName, nVertexShaderStaticIndex, nVertexShaderDynamicIndex, pPixelShaderName, nPixelShaderStaticIndex, nPixelShaderDynamicIndex ); + } + } + + pProgramCache->deleteThis(); + + float elapsed = ( float )( Sys_FloatTime() - st ) * 1000.0; + DevMsg( "WarmShaderCache took %.3f msec\n", elapsed ); + } + +#endif +//----------------------------------------------------------------------------------old code + +#ifdef DX_TO_GL_ABSTRACTION +// if shaders are changed in a way that requires the client-side cache to be invalidated, +// increment this string - such changes include combo changes (skips, adding combos) +const char *k_pszShaderCacheRootKey = "glshadercachev002"; +#endif + +void CShaderManager::SaveShaderCache( char *cacheName ) +{ +#ifdef DX_TO_GL_ABSTRACTION // must ifdef, it uses calls which don't exist in the real DX9 interface + + KeyValues *pProgramCache = new KeyValues( k_pszShaderCacheRootKey ); + + if ( !pProgramCache ) + { + Warning( "Could not write to program cache file!\n" ); + return; + } + + int i=0; + GLMShaderPairInfo info; + + do + { + Dx9Device()->QueryShaderPair( i, &info ); + + if (info.m_status==1) + { + // found one + // extract values of interest which represent a pair of shaders + + if (info.m_vsName[0] && info.m_psName[0] && (info.m_vsDynamicIndex > -1) && (info.m_psDynamicIndex > -1) ) + { + // make up a key - this thing is really a list of tuples, so need not be keyed by anything particular + KeyValues *pProgramKey = pProgramCache->CreateNewKey(); + Assert( pProgramKey ); + + pProgramKey->SetString ( "vs", info.m_vsName ); + pProgramKey->SetString ( "ps", info.m_psName ); + + pProgramKey->SetInt ( "vs_static", info.m_vsStaticIndex ); + pProgramKey->SetInt ( "ps_static", info.m_psStaticIndex ); + + pProgramKey->SetInt ( "vs_dynamic", info.m_vsDynamicIndex ); + pProgramKey->SetInt ( "ps_dynamic", info.m_psDynamicIndex ); + } + } + i++; + } while( info.m_status >= 0 ); + + pProgramCache->SaveToFile( g_pFullFileSystem, cacheName, "MOD" ); + pProgramCache->deleteThis(); + + // done! whew +#endif +} + +bool CShaderManager::LoadShaderCache( char *cacheName ) +{ +#ifdef DX_TO_GL_ABSTRACTION + KeyValues *pProgramCache = new KeyValues( "" ); + bool found = pProgramCache->LoadFromFile( g_pFullFileSystem, cacheName, "MOD" ); + + if ( !found ) + { + Warning( "Could not load program cache file %s\n", cacheName ); + return false; + } + + if ( Q_stricmp( pProgramCache->GetName(), k_pszShaderCacheRootKey ) ) + { + Warning( "Ignoring out-of-date shader cache (%s) with root key %s\n", cacheName, pProgramCache->GetName() ); + return false; + } + + int nTotalLinkedShaders = 0; + int nTotalKeyValues = 0; + + // walk the table.. + FOR_EACH_SUBKEY( pProgramCache, pProgramKey ) + { + nTotalKeyValues++; + + // extract values decribing the specific active pair + // then see if either stage needs a compilation done + // then proceed to link + + KeyValues *pValue = pProgramKey->GetFirstValue(); + if (!pValue) + continue; + const char *pVertexShaderName = pValue->GetString(); + + pValue = pValue->GetNextValue(); + if (!pValue) + continue; + const char *pPixelShaderName = pValue->GetString(); + + pValue = pValue->GetNextValue(); + if (!pValue) + continue; + int nVertexShaderStaticIndex = pValue->GetInt(); + + pValue = pValue->GetNextValue(); + if (!pValue) + continue; + int nPixelShaderStaticIndex = pValue->GetInt(); + + pValue = pValue->GetNextValue(); + if (!pValue) + continue; + int nVertexShaderDynamicIndex = pValue->GetInt(); + + pValue = pValue->GetNextValue(); + if (!pValue) + continue; + int nPixelShaderDynamicIndex = pValue->GetInt(); + + ShaderLookup_t vshLookup; + vshLookup.m_Name = m_ShaderSymbolTable.AddString( pVertexShaderName ); // TODO: use String() here and catch this odd case + vshLookup.m_nStaticIndex = nVertexShaderStaticIndex; + VertexShader_t vertexShader = m_VertexShaderDict.Find( vshLookup ); + + // if the VS was not found - now is the time to build it + if( vertexShader == m_VertexShaderDict.InvalidIndex()) + { + char temp[1024]; + + V_snprintf( temp, sizeof(temp), "vs-file %s vs-index %d", pVertexShaderName, nVertexShaderStaticIndex ); + CreateVertexShader( pVertexShaderName, nVertexShaderStaticIndex, temp ); + + // this one should not fail + vertexShader = m_VertexShaderDict.Find( vshLookup ); + Assert( vertexShader != m_VertexShaderDict.InvalidIndex()); + } + + ShaderLookup_t pshLookup; + pshLookup.m_Name = m_ShaderSymbolTable.AddString( pPixelShaderName ); + pshLookup.m_nStaticIndex = nPixelShaderStaticIndex; + PixelShader_t pixelShader = m_PixelShaderDict.Find( pshLookup ); + + if( pixelShader == m_PixelShaderDict.InvalidIndex()) + { + char temp[1024]; + + V_snprintf( temp, sizeof(temp), "ps-file %s ps-index %d", pPixelShaderName, nPixelShaderStaticIndex ); + CreatePixelShader( pPixelShaderName, nPixelShaderStaticIndex, temp ); + + // this one should not fail + pixelShader = m_PixelShaderDict.Find( pshLookup ); + Assert( pixelShader != m_PixelShaderDict.InvalidIndex()); + } + + // If we found both shaders, do the link! + if ( ( vertexShader != m_VertexShaderDict.InvalidIndex() ) && ( pixelShader != m_PixelShaderDict.InvalidIndex() ) ) + { + // double check that the hardware shader arrays are actually instantiated.. bail on the attempt if not (odd...) + if (m_VertexShaderDict[vertexShader].m_ShaderStaticCombos.m_pHardwareShaders && m_PixelShaderDict[pixelShader].m_ShaderStaticCombos.m_pHardwareShaders) + { + // and sanity check the indices.. + if ( (nVertexShaderDynamicIndex>=0) && (nPixelShaderDynamicIndex>=0) ) + { + HardwareShader_t hardwareVertexShader = m_VertexShaderDict[vertexShader].m_ShaderStaticCombos.m_pHardwareShaders[nVertexShaderDynamicIndex]; + HardwareShader_t hardwarePixelShader = m_PixelShaderDict[pixelShader].m_ShaderStaticCombos.m_pHardwareShaders[nPixelShaderDynamicIndex]; + + if ( ( hardwareVertexShader != INVALID_HARDWARE_SHADER ) && ( hardwarePixelShader != INVALID_HARDWARE_SHADER ) ) + { + if ( S_OK != Dx9Device()->LinkShaderPair( (IDirect3DVertexShader9 *)hardwareVertexShader, (IDirect3DPixelShader9 *)hardwarePixelShader ) ) + { + Warning( "Could not link OpenGL shaders: %s (%d, %d) : %s (%d, %d)\n", pVertexShaderName, nVertexShaderStaticIndex, nVertexShaderDynamicIndex, pPixelShaderName, nPixelShaderStaticIndex, nPixelShaderDynamicIndex ); + } + else + { + nTotalLinkedShaders++; + } + } + } + else + { + Warning( "nVertexShaderDynamicIndex or nPixelShaderDynamicIndex was negative\n" ); + } + } + else + { + Warning( "m_pHardwareShaders was null\n" ); + } + } + else + { + Warning( "Invalid shader linkage: %s (%d, %d) : %s (%d, %d)\n", pVertexShaderName, nVertexShaderStaticIndex, nVertexShaderDynamicIndex, pPixelShaderName, nPixelShaderStaticIndex, nPixelShaderDynamicIndex ); + } + } + + Msg( "Loaded program cache file \"%s\", total keyvalues: %i, total successfully linked: %i\n", cacheName, nTotalKeyValues, nTotalLinkedShaders ); + + return true; + +#else + return false; // have to return a value on Windows build to appease compiler +#endif +} + + + +//----------------------------------------------------------------------------- +// Creates and destroys vertex shaders +//----------------------------------------------------------------------------- +VertexShader_t CShaderManager::CreateVertexShader( const char *pFileName, int nStaticVshIndex, char *debugLabel ) +{ + MEM_ALLOC_CREDIT(); + + if ( !pFileName ) + { + return INVALID_SHADER; + } + + #if 0 //old + if ( mat_cacheshaders.GetBool() ) + { + WriteToShaderCache( pFileName, nStaticVshIndex ); + } + #endif + + VertexShader_t shader; + ShaderLookup_t lookup; + lookup.m_Name = m_ShaderSymbolTable.AddString( pFileName ); + lookup.m_nStaticIndex = nStaticVshIndex; + shader = m_VertexShaderDict.Find( lookup ); + if ( shader == m_VertexShaderDict.InvalidIndex() ) + { + //printf("\nCShaderManager::CreateVertexShader( filename = %s, staticVshIndex = %d - not in cache", pFileName, nStaticVshIndex ); + + shader = m_VertexShaderDict.AddToTail( lookup ); + if ( !LoadAndCreateShaders( m_VertexShaderDict[shader], true, debugLabel ) ) + { + return INVALID_SHADER; + } + } + m_VertexShaderDict[shader].IncRefCount(); + return shader; +} + +//----------------------------------------------------------------------------- +// Create pixel shader +//----------------------------------------------------------------------------- +PixelShader_t CShaderManager::CreatePixelShader( const char *pFileName, int nStaticPshIndex, char *debugLabel ) +{ + MEM_ALLOC_CREDIT(); + + if ( !pFileName ) + { + return INVALID_SHADER; + } + + #if 0 //old + if ( mat_cacheshaders.GetBool() ) + { + WriteToShaderCache( pFileName, nStaticPshIndex ); + } + #endif + + PixelShader_t shader; + ShaderLookup_t lookup; + lookup.m_Name = m_ShaderSymbolTable.AddString( pFileName ); + lookup.m_nStaticIndex = nStaticPshIndex; + shader = m_PixelShaderDict.Find( lookup ); + if ( shader == m_PixelShaderDict.InvalidIndex() ) + { + shader = m_PixelShaderDict.AddToTail( lookup ); + if ( !LoadAndCreateShaders( m_PixelShaderDict[shader], false, debugLabel ) ) + { + return INVALID_SHADER; + } + } + m_PixelShaderDict[shader].IncRefCount(); + return shader; +} + +//----------------------------------------------------------------------------- +// Clear the refCounts to zero +//----------------------------------------------------------------------------- +void CShaderManager::ClearVertexAndPixelShaderRefCounts() +{ + for ( VertexShader_t vshIndex = m_VertexShaderDict.Head(); + vshIndex != m_VertexShaderDict.InvalidIndex(); + vshIndex = m_VertexShaderDict.Next( vshIndex ) ) + { + m_VertexShaderDict[vshIndex].m_nRefCount = 0; + } + + for ( PixelShader_t pshIndex = m_PixelShaderDict.Head(); + pshIndex != m_PixelShaderDict.InvalidIndex(); + pshIndex = m_PixelShaderDict.Next( pshIndex ) ) + { + m_PixelShaderDict[pshIndex].m_nRefCount = 0; + } +} + +//----------------------------------------------------------------------------- +// Destroy all shaders that have no reference +//----------------------------------------------------------------------------- +void CShaderManager::PurgeUnusedVertexAndPixelShaders() +{ + #ifdef DX_TO_GL_ABSTRACTION + if (mat_autosave_glshaders.GetInt()) + { + SaveShaderCache("glshaders.cfg"); + } + return; // don't purge shaders, it's too costly to put them back + #endif + + // iterate vertex shaders + for ( VertexShader_t vshIndex = m_VertexShaderDict.Head(); vshIndex != m_VertexShaderDict.InvalidIndex(); ) + { + Assert( m_VertexShaderDict[vshIndex].m_nRefCount >= 0 ); + + // Get the next one before we potentially delete the current one. + VertexShader_t next = m_VertexShaderDict.Next( vshIndex ); + if ( m_VertexShaderDict[vshIndex].m_nRefCount <= 0 ) + { + DestroyVertexShader( vshIndex ); + } + vshIndex = next; + } + + // iterate pixel shaders + for ( PixelShader_t pshIndex = m_PixelShaderDict.Head(); pshIndex != m_PixelShaderDict.InvalidIndex(); ) + { + Assert( m_PixelShaderDict[pshIndex].m_nRefCount >= 0 ); + + // Get the next one before we potentially delete the current one. + PixelShader_t next = m_PixelShaderDict.Next( pshIndex ); + if ( m_PixelShaderDict[pshIndex].m_nRefCount <= 0 ) + { + DestroyPixelShader( pshIndex ); + } + pshIndex = next; + } +} + + + +void* CShaderManager::GetCurrentVertexShader() +{ + return (void*)m_HardwareVertexShader; +} + +void* CShaderManager::GetCurrentPixelShader() +{ + return (void*)m_HardwarePixelShader; +} + + +//----------------------------------------------------------------------------- +// The low-level dx call to set the vertex shader state +//----------------------------------------------------------------------------- +void CShaderManager::SetVertexShaderState( HardwareShader_t shader, DataCacheHandle_t hCachedShader ) +{ + if ( m_HardwareVertexShader != shader ) + { + RECORD_COMMAND( DX8_SET_VERTEX_SHADER, 1 ); + RECORD_INT( ( int )shader ); // hack hack hack + + Dx9Device()->SetVertexShader( (IDirect3DVertexShader9*)shader ); + m_HardwareVertexShader = shader; + } +} + +void CShaderManager::BindVertexShader( VertexShaderHandle_t hVertexShader ) +{ + HardwareShader_t hHardwareShader = m_RawVertexShaderDict[ (VertexShaderIndex_t)hVertexShader] ; + SetVertexShaderState( hHardwareShader ); +} + + +//----------------------------------------------------------------------------- +// Sets a particular vertex shader as the current shader +//----------------------------------------------------------------------------- +void CShaderManager::SetVertexShader( VertexShader_t shader ) +{ + // Determine which vertex shader to use... + if ( shader == INVALID_SHADER ) + { + SetVertexShaderState( 0 ); + return; + } + + int vshIndex = m_nVertexShaderIndex; + Assert( vshIndex >= 0 ); + if( vshIndex < 0 ) + { + vshIndex = 0; + } + + ShaderLookup_t &vshLookup = m_VertexShaderDict[shader]; +// Warning( "vsh: %s static: %d dynamic: %d\n", m_ShaderSymbolTable.String( vshLookup.m_Name ), +// vshLookup.m_nStaticIndex, m_nVertexShaderIndex ); + +#ifdef DYNAMIC_SHADER_COMPILE + HardwareShader_t &dxshader = m_VertexShaderDict[shader].m_ShaderStaticCombos.m_pHardwareShaders[vshIndex]; + if ( dxshader == INVALID_HARDWARE_SHADER ) + { + // compile it since we haven't already! + dxshader = CompileShader( m_ShaderSymbolTable.String( vshLookup.m_Name ), vshLookup.m_nStaticIndex, vshIndex, true ); + Assert( dxshader != INVALID_HARDWARE_SHADER ); + + if( IsX360() ) + { + //360 does not respond well at all to bad shaders or Error() calls. So we're staying here until we get something that compiles + while( dxshader == INVALID_HARDWARE_SHADER ) + { + Warning( "A dynamically compiled vertex shader has failed to build. Pausing for 5 seconds and attempting rebuild.\n" ); +#ifdef _WIN32 + Sleep( 5000 ); +#elif POSIX + usleep( 5000 ); +#endif + dxshader = CompileShader( m_ShaderSymbolTable.String( vshLookup.m_Name ), vshLookup.m_nStaticIndex, vshIndex, true ); + } + } + } +#else + if ( vshLookup.m_Flags & SHADER_FAILED_LOAD ) + { + Assert( 0 ); + return; + } +#ifdef _DEBUG + vshDebugIndex = (vshDebugIndex + 1) % MAX_SHADER_HISTORY; + Q_strncpy( vshDebugName[vshDebugIndex], m_ShaderSymbolTable.String( vshLookup.m_Name ), sizeof( vshDebugName[0] ) ); +#endif + Assert( vshIndex < vshLookup.m_ShaderStaticCombos.m_nCount ); + HardwareShader_t dxshader = vshLookup.m_ShaderStaticCombos.m_pHardwareShaders[vshIndex]; +#endif + + if ( IsPC() && ( dxshader == INVALID_HARDWARE_SHADER ) && m_bCreateShadersOnDemand ) + { +#ifdef DYNAMIC_SHADER_COMPILE + ShaderStaticCombos_t::ShaderCreationData_t *pCreationData = &m_VertexShaderDict[shader].m_ShaderStaticCombos.m_pCreationData[vshIndex]; +#else + ShaderStaticCombos_t::ShaderCreationData_t *pCreationData = &vshLookup.m_ShaderStaticCombos.m_pCreationData[vshIndex]; +#endif + + dxshader = CreateD3DVertexShader( ( DWORD * )pCreationData->ByteCode.Base(), pCreationData->ByteCode.Count(), m_ShaderSymbolTable.String( vshLookup.m_Name ) ); + +#ifdef DYNAMIC_SHADER_COMPILE + // copy the compiled shader handle back to wherever it's supposed to be stored + m_VertexShaderDict[shader].m_ShaderStaticCombos.m_pHardwareShaders[vshIndex] = dxshader; +#else + vshLookup.m_ShaderStaticCombos.m_pHardwareShaders[vshIndex] = dxshader; +#endif + } + + Assert( dxshader ); + +#ifndef DYNAMIC_SHADER_COMPILE + if( !dxshader ) + { + Error( "!!!!!Using invalid shader combo!!!!! Consult a programmer and tell them to build debug materialsystem.dll and stdshader*.dll. Run with \"mat_bufferprimitives 0\" and look for CMaterial in the call stack and see what m_pDebugName is. You are likely using a shader combo that has been skipped.\n" ); + } +#endif + + SetVertexShaderState( dxshader ); +} + +//----------------------------------------------------------------------------- +// The low-level dx call to set the pixel shader state +//----------------------------------------------------------------------------- +void CShaderManager::SetPixelShaderState( HardwareShader_t shader, DataCacheHandle_t hCachedShader ) +{ + if ( m_HardwarePixelShader != shader ) + { + Dx9Device()->SetPixelShader( (IDirect3DPixelShader*)shader ); + m_HardwarePixelShader = shader; + } +} + +void CShaderManager::BindPixelShader( PixelShaderHandle_t hPixelShader ) +{ + HardwareShader_t hHardwareShader = m_RawPixelShaderDict[ (PixelShaderIndex_t)hPixelShader ]; + SetPixelShaderState( hHardwareShader ); +} + + +//----------------------------------------------------------------------------- +// Sets a particular pixel shader as the current shader +//----------------------------------------------------------------------------- +void CShaderManager::SetPixelShader( PixelShader_t shader ) +{ + if ( shader == INVALID_SHADER ) + { + SetPixelShaderState( 0 ); + return; + } + + int pshIndex = m_nPixelShaderIndex; + Assert( pshIndex >= 0 ); + ShaderLookup_t &pshLookup = m_PixelShaderDict[shader]; +// Warning( "psh: %s static: %d dynamic: %d\n", m_ShaderSymbolTable.String( pshLookup.m_Name ), +// pshLookup.m_nStaticIndex, m_nPixelShaderIndex ); + +#ifdef DYNAMIC_SHADER_COMPILE + HardwareShader_t &dxshader = m_PixelShaderDict[shader].m_ShaderStaticCombos.m_pHardwareShaders[pshIndex]; + if ( dxshader == INVALID_HARDWARE_SHADER ) + { + // compile it since we haven't already! + dxshader = CompileShader( m_ShaderSymbolTable.String( pshLookup.m_Name ), pshLookup.m_nStaticIndex, pshIndex, false ); +// Assert( dxshader != INVALID_HARDWARE_SHADER ); + + if( IsX360() ) + { + //360 does not respond well at all to bad shaders or Error() calls. So we're staying here until we get something that compiles + while( dxshader == INVALID_HARDWARE_SHADER ) + { + Warning( "A dynamically compiled pixel shader has failed to build. Pausing for 5 seconds and attempting rebuild.\n" ); +#ifdef _WIN32 + Sleep( 5000 ); +#elif POSIX + usleep( 5000 ); +#endif + dxshader = CompileShader( m_ShaderSymbolTable.String( pshLookup.m_Name ), pshLookup.m_nStaticIndex, pshIndex, false ); + } + } + } +#else + if ( pshLookup.m_Flags & SHADER_FAILED_LOAD ) + { + Assert( 0 ); + return; + } +#ifdef _DEBUG + pshDebugIndex = (pshDebugIndex + 1) % MAX_SHADER_HISTORY; + Q_strncpy( pshDebugName[pshDebugIndex], m_ShaderSymbolTable.String( pshLookup.m_Name ), sizeof( pshDebugName[0] ) ); +#endif + HardwareShader_t dxshader = pshLookup.m_ShaderStaticCombos.m_pHardwareShaders[pshIndex]; +#endif + + if ( IsPC() && ( dxshader == INVALID_HARDWARE_SHADER ) && m_bCreateShadersOnDemand ) + { +#ifdef DYNAMIC_SHADER_COMPILE + ShaderStaticCombos_t::ShaderCreationData_t *pCreationData = &m_PixelShaderDict[shader].m_ShaderStaticCombos.m_pCreationData[pshIndex]; +#else + ShaderStaticCombos_t::ShaderCreationData_t *pCreationData = &pshLookup.m_ShaderStaticCombos.m_pCreationData[pshIndex]; +#endif + + const char *pShaderName = m_ShaderSymbolTable.String( pshLookup.m_Name ); + dxshader = CreateD3DPixelShader( ( DWORD * )pCreationData->ByteCode.Base(), pCreationData->iCentroidMask, pCreationData->ByteCode.Count(), pShaderName ); + +#ifdef DYNAMIC_SHADER_COMPILE + // copy the compiled shader handle back to wherever it's supposed to be stored + m_PixelShaderDict[shader].m_ShaderStaticCombos.m_pHardwareShaders[pshIndex] = dxshader; +#else + pshLookup.m_ShaderStaticCombos.m_pHardwareShaders[pshIndex] = dxshader; +#endif + } + + AssertMsg( dxshader != INVALID_HARDWARE_SHADER, "Failed to set pixel shader." ); + SetPixelShaderState( dxshader ); +} + +//----------------------------------------------------------------------------- +// Resets the shader state +//----------------------------------------------------------------------------- +void CShaderManager::ResetShaderState() +{ + // This will force the calls to SetVertexShader + SetPixelShader to actually set the state + m_HardwareVertexShader = (HardwareShader_t)-1; + m_HardwarePixelShader = (HardwareShader_t)-1; + + SetVertexShader( INVALID_SHADER ); + SetPixelShader( INVALID_SHADER ); +} + +//----------------------------------------------------------------------------- +// Destroy a particular vertex shader +//----------------------------------------------------------------------------- +void CShaderManager::DestroyVertexShader( VertexShader_t shader ) +{ + ShaderStaticCombos_t &combos = m_VertexShaderDict[shader].m_ShaderStaticCombos; + int i; + for ( i = 0; i < combos.m_nCount; i++ ) + { + if ( combos.m_pHardwareShaders[i] != INVALID_HARDWARE_SHADER ) + { + IDirect3DVertexShader9* pShader = ( IDirect3DVertexShader9 * )combos.m_pHardwareShaders[i]; + UnregisterVS( pShader ); +#ifdef DBGFLAG_ASSERT + int nRetVal = +#endif + pShader->Release(); + Assert( nRetVal == 0 ); + } + } + delete [] combos.m_pHardwareShaders; + combos.m_pHardwareShaders = NULL; + + if ( combos.m_pCreationData != NULL ) + { + delete [] combos.m_pCreationData; + combos.m_pCreationData = NULL; + } + + m_VertexShaderDict.Remove( shader ); +} + +//----------------------------------------------------------------------------- +// Destroy a particular pixel shader +//----------------------------------------------------------------------------- +void CShaderManager::DestroyPixelShader( PixelShader_t pixelShader ) +{ + ShaderStaticCombos_t &combos = m_PixelShaderDict[pixelShader].m_ShaderStaticCombos; + int i; + for ( i = 0; i < combos.m_nCount; i++ ) + { + if ( combos.m_pHardwareShaders[i] != INVALID_HARDWARE_SHADER ) + { + IDirect3DPixelShader* pShader = ( IDirect3DPixelShader * )combos.m_pHardwareShaders[i]; + UnregisterPS( pShader ); +#ifdef DBGFLAG_ASSERT + int nRetVal = +#endif + pShader->Release(); + Assert( nRetVal == 0 ); + } + } + delete [] combos.m_pHardwareShaders; + combos.m_pHardwareShaders = NULL; + + if ( combos.m_pCreationData != NULL ) + { + delete [] combos.m_pCreationData; + combos.m_pCreationData = NULL; + } + + m_PixelShaderDict.Remove( pixelShader ); +} + + +//----------------------------------------------------------------------------- +// Destroys all shaders +//----------------------------------------------------------------------------- +void CShaderManager::DestroyAllShaders( void ) +{ + // Remarking this out because it's conflicting with dxabstract's shutdown resource leak detection code (we leak thousands of shaders at shutdown with this in place). + // I see no reason why we would want to do this in D3D9 but not GL? +//#ifdef DX_TO_GL_ABSTRACTION +// return; +//#endif + + for ( VertexShader_t vshIndex = m_VertexShaderDict.Head(); + vshIndex != m_VertexShaderDict.InvalidIndex(); ) + { + Assert( m_VertexShaderDict[vshIndex].m_nRefCount >= 0 ); + VertexShader_t next = m_VertexShaderDict.Next( vshIndex ); + DestroyVertexShader( vshIndex ); + vshIndex = next; + } + + for ( PixelShader_t pshIndex = m_PixelShaderDict.Head(); + pshIndex != m_PixelShaderDict.InvalidIndex(); ) + { + Assert( m_PixelShaderDict[pshIndex].m_nRefCount >= 0 ); + PixelShader_t next = m_PixelShaderDict.Next( pshIndex ); + DestroyPixelShader( pshIndex ); + pshIndex = next; + } + + // invalidate the file cache + m_ShaderFileCache.Purge(); +} + +//----------------------------------------------------------------------------- +// print all vertex and pixel shaders along with refcounts to the console +//----------------------------------------------------------------------------- +void CShaderManager::SpewVertexAndPixelShaders( void ) +{ + // only spew a populated shader file cache + Msg( "\nShader File Cache:\n" ); + for ( int cacheIndex = m_ShaderFileCache.Head(); + cacheIndex != m_ShaderFileCache.InvalidIndex(); + cacheIndex = m_ShaderFileCache.Next( cacheIndex ) ) + { + ShaderFileCache_t *pCache = &m_ShaderFileCache[cacheIndex]; + Msg( "Total Combos:%9d Static:%9d Dynamic:%7d SeekTable:%7d Ver:%d '%s'\n", + pCache->m_Header.m_nTotalCombos, + pCache->m_Header.m_nTotalCombos/pCache->m_Header.m_nDynamicCombos, + pCache->m_Header.m_nDynamicCombos, + pCache->IsOldVersion() ? 0 : pCache->m_Header.m_nNumStaticCombos, + pCache->m_Header.m_nVersion, + m_ShaderSymbolTable.String( pCache->m_Filename ) ); + } + Msg( "\n" ); + + // spew vertex shader dictionary + int totalVertexShaders = 0; + int totalVertexShaderSets = 0; + for ( VertexShader_t vshIndex = m_VertexShaderDict.Head(); + vshIndex != m_VertexShaderDict.InvalidIndex(); + vshIndex = m_VertexShaderDict.Next( vshIndex ) ) + { + const ShaderLookup_t &lookup = m_VertexShaderDict[vshIndex]; + const char *pName = m_ShaderSymbolTable.String( lookup.m_Name ); + Msg( "vsh 0x%8.8x: static combo:%9d dynamic combos:%6d refcount:%4d \"%s\"\n", vshIndex, + ( int )lookup.m_nStaticIndex, ( int )lookup.m_ShaderStaticCombos.m_nCount, + lookup.m_nRefCount, pName ); + totalVertexShaders += lookup.m_ShaderStaticCombos.m_nCount; + totalVertexShaderSets++; + } + + // spew pixel shader dictionary + int totalPixelShaders = 0; + int totalPixelShaderSets = 0; + for ( PixelShader_t pshIndex = m_PixelShaderDict.Head(); + pshIndex != m_PixelShaderDict.InvalidIndex(); + pshIndex = m_PixelShaderDict.Next( pshIndex ) ) + { + const ShaderLookup_t &lookup = m_PixelShaderDict[pshIndex]; + const char *pName = m_ShaderSymbolTable.String( lookup.m_Name ); + Msg( "psh 0x%8.8x: static combo:%9d dynamic combos:%6d refcount:%4d \"%s\"\n", pshIndex, + ( int )lookup.m_nStaticIndex, ( int )lookup.m_ShaderStaticCombos.m_nCount, + lookup.m_nRefCount, pName ); + totalPixelShaders += lookup.m_ShaderStaticCombos.m_nCount; + totalPixelShaderSets++; + } + + Msg( "Total unique vertex shaders: %d\n", totalVertexShaders ); + Msg( "Total vertex shader sets: %d\n", totalVertexShaderSets ); + Msg( "Total unique pixel shaders: %d\n", totalPixelShaders ); + Msg( "Total pixel shader sets: %d\n", totalPixelShaderSets ); +} + +CON_COMMAND( mat_spewvertexandpixelshaders, "Print all vertex and pixel shaders currently loaded to the console" ) +{ + ( ( CShaderManager * )ShaderManager() )->SpewVertexAndPixelShaders(); +} + +const char *CShaderManager::GetActiveVertexShaderName() +{ +#if !defined( _DEBUG ) + return ""; +#else + if ( !m_HardwareVertexShader ) + { + return "NULL"; + } + return vshDebugName[vshDebugIndex]; +#endif +} + +const char *CShaderManager::GetActivePixelShaderName() +{ +#if !defined( _DEBUG ) + return ""; +#else + if ( !m_HardwarePixelShader ) + { + return "NULL"; + } + return pshDebugName[pshDebugIndex]; +#endif +} + +#ifdef DYNAMIC_SHADER_COMPILE +void CShaderManager::FlushShaders( void ) +{ + for( VertexShader_t shader = m_VertexShaderDict.Head(); + shader != m_VertexShaderDict.InvalidIndex(); + shader = m_VertexShaderDict.Next( shader ) ) + { + int i; + ShaderStaticCombos_t &combos = m_VertexShaderDict[shader].m_ShaderStaticCombos; + for( i = 0; i < combos.m_nCount; i++ ) + { + if( combos.m_pHardwareShaders[i] != INVALID_HARDWARE_SHADER ) + { +#ifdef _DEBUG + int nRetVal= +#endif + ( ( IDirect3DVertexShader9 * )combos.m_pHardwareShaders[i] )->Release(); + Assert( nRetVal == 0 ); + } + combos.m_pHardwareShaders[i] = INVALID_HARDWARE_SHADER; + } + } + + for( PixelShader_t shader = m_PixelShaderDict.Head(); + shader != m_PixelShaderDict.InvalidIndex(); + shader = m_PixelShaderDict.Next( shader ) ) + { + int i; + ShaderStaticCombos_t &combos = m_PixelShaderDict[shader].m_ShaderStaticCombos; + for( i = 0; i < combos.m_nCount; i++ ) + { + if( combos.m_pHardwareShaders[i] != INVALID_HARDWARE_SHADER ) + { +#ifdef _DEBUG + int nRetVal = +#endif + ( ( IDirect3DPixelShader * )combos.m_pHardwareShaders[i] )->Release(); + Assert( nRetVal == 0 ); + } + combos.m_pHardwareShaders[i] = INVALID_HARDWARE_SHADER; + } + } + + // invalidate the file cache + m_ShaderFileCache.Purge(); +} +#endif + +#ifdef DYNAMIC_SHADER_COMPILE +static void MatFlushShaders( void ) +{ +#if defined( _X360 ) + XBX_rSyncShaderCache(); +#endif + ( ( CShaderManager * )ShaderManager() )->FlushShaders(); +} +#endif + +#ifdef DYNAMIC_SHADER_COMPILE +CON_COMMAND( mat_flushshaders, "flush all hardware shaders when using DYNAMIC_SHADER_COMPILE" ) +{ + MatFlushShaders(); +} +#endif + +CON_COMMAND( mat_shadercount, "display count of all shaders and reset that count" ) +{ + Warning( "Num Pixel Shaders = %d Vertex Shaders=%d\n", s_NumPixelShadersCreated, s_NumVertexShadersCreated ); + s_NumVertexShadersCreated = 0; + s_NumPixelShadersCreated = 0; +} + +#if defined( DX_TO_GL_ABSTRACTION ) +void CShaderManager::DoStartupShaderPreloading() +{ + if (mat_autoload_glshaders.GetInt()) + { + double flStartTime = Plat_FloatTime(); + + s_NumVertexShadersCreated = s_NumPixelShadersCreated = 0; + + // try base file +#ifdef OSX + if ( !LoadShaderCache("glbaseshaders_osx.cfg") ) // factory cache +#else + if ( !LoadShaderCache("glbaseshaders.cfg") ) // factory cache +#endif + { + Warning( "Could not find base GL shader cache file\n" ); + } + + if ( !LoadShaderCache("glshaders.cfg") ) // user mutable cache + { + Warning( "Could not find user GL shader cache file\n" ); + } + + double flEndTime = Plat_FloatTime(); + Msg( "Precache: Took %d ms, Vertex %d, Pixel %d\n", ( int )( ( flEndTime - flStartTime ) * 1000.0 ), s_NumVertexShadersCreated, s_NumPixelShadersCreated ); + } +} +#endif + diff --git a/materialsystem/shaderapidx9/vertexshaderdx8.h b/materialsystem/shaderapidx9/vertexshaderdx8.h new file mode 100644 index 0000000..5483eec --- /dev/null +++ b/materialsystem/shaderapidx9/vertexshaderdx8.h @@ -0,0 +1,135 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//===========================================================================// + +#ifndef VERTEXSHADERDX8_H +#define VERTEXSHADERDX8_H + +#ifdef _WIN32 +#pragma once +#endif + +#include "shaderapi/ishaderapi.h" +#include "locald3dtypes.h" + + +// uncomment to get dynamic compilation for HLSL shaders +// X360 NOTE: By default, the system looks for a shared folder named "stdshaders" on the host machine and is completely compatible with -dvd. Ensure that the share is writable if you plan on generating UPDB's. +//#define DYNAMIC_SHADER_COMPILE + +// Uncomment to use remoteshadercompiler.exe as a shader compile server +// Must also set mat_remoteshadercompile to remote shader compile machine name +//#define REMOTE_DYNAMIC_SHADER_COMPILE + +// uncomment to get spew about what combos are being compiled. +//#define DYNAMIC_SHADER_COMPILE_VERBOSE + +// Uncomment to use remoteshadercompiler.exe as a shader compile server +// Must also set mat_remoteshadercompile to remote shader compile machine name +//#define REMOTE_DYNAMIC_SHADER_COMPILE + +// uncomment and fill in with a path to use a specific set of shader source files. Meant for network use. +// PC path format is of style "\\\\somemachine\\sourcetreeshare\\materialsystem\\stdshaders" +// Xbox path format is of style "net:\\smb\\somemachine\\sourcetreeshare\\materialsystem\\stdshaders" +// - Xbox dynamic compiles without a custom path default to look directly for "stdshaders" share on host pc + +//#define DYNAMIC_SHADER_COMPILE_CUSTOM_PATH "" + +// uncomment to get disassembled (asm) shader code in your game dir as *.asm +//#define DYNAMIC_SHADER_COMPILE_WRITE_ASSEMBLY + +// uncomment to get disassembled (asm) shader code in your game dir as *.asm +//#define WRITE_ASSEMBLY + + +enum VertexShaderLightTypes_t +{ + LIGHT_NONE = -1, + LIGHT_SPOT = 0, + LIGHT_POINT = 1, + LIGHT_DIRECTIONAL = 2, + LIGHT_STATIC = 3, + LIGHT_AMBIENTCUBE = 4, +}; + +//----------------------------------------------------------------------------- +// Vertex + pixel shader manager +//----------------------------------------------------------------------------- +abstract_class IShaderManager +{ +protected: + + // The current vertex and pixel shader index + int m_nVertexShaderIndex; + int m_nPixelShaderIndex; + +public: + // Initialize, shutdown + virtual void Init() = 0; + virtual void Shutdown() = 0; + + // Compiles vertex shaders + virtual IShaderBuffer *CompileShader( const char *pProgram, size_t nBufLen, const char *pShaderVersion ) = 0; + + // New version of these methods [dx10 port] + virtual VertexShaderHandle_t CreateVertexShader( IShaderBuffer* pShaderBuffer ) = 0; + virtual void DestroyVertexShader( VertexShaderHandle_t hShader ) = 0; + virtual PixelShaderHandle_t CreatePixelShader( IShaderBuffer* pShaderBuffer ) = 0; + virtual void DestroyPixelShader( PixelShaderHandle_t hShader ) = 0; + + // Creates vertex, pixel shaders + virtual VertexShader_t CreateVertexShader( const char *pVertexShaderFile, int nStaticVshIndex = 0, char *debugLabel = NULL ) = 0; + virtual PixelShader_t CreatePixelShader( const char *pPixelShaderFile, int nStaticPshIndex = 0, char *debugLabel = NULL ) = 0; + + // Sets which dynamic version of the vertex + pixel shader to use + FORCEINLINE void SetVertexShaderIndex( int vshIndex ); + FORCEINLINE void SetPixelShaderIndex( int pshIndex ); + + // Sets the vertex + pixel shader render state + virtual void SetVertexShader( VertexShader_t shader ) = 0; + virtual void SetPixelShader( PixelShader_t shader ) = 0; + + // Resets the vertex + pixel shader state + virtual void ResetShaderState() = 0; + + // Returns the current vertex + pixel shaders + virtual void *GetCurrentVertexShader() = 0; + virtual void *GetCurrentPixelShader() = 0; + + virtual void ClearVertexAndPixelShaderRefCounts() = 0; + virtual void PurgeUnusedVertexAndPixelShaders() = 0; + + // The low-level dx call to set the vertex shader state + virtual void BindVertexShader( VertexShaderHandle_t shader ) = 0; + virtual void BindPixelShader( PixelShaderHandle_t shader ) = 0; + +#if defined( _X360 ) + virtual const char *GetActiveVertexShaderName() = 0; + virtual const char *GetActivePixelShaderName() = 0; +#endif + +#if defined( DX_TO_GL_ABSTRACTION ) + virtual void DoStartupShaderPreloading() = 0; +#endif +}; + +//----------------------------------------------------------------------------- +// +// Methods related to setting vertex + pixel shader state +// +//----------------------------------------------------------------------------- +FORCEINLINE void IShaderManager::SetVertexShaderIndex( int vshIndex ) +{ + m_nVertexShaderIndex = vshIndex; +} + +FORCEINLINE void IShaderManager::SetPixelShaderIndex( int pshIndex ) +{ + m_nPixelShaderIndex = pshIndex; +} + +#endif // VERTEXSHADERDX8_H diff --git a/materialsystem/shaderapidx9/winutils.cpp b/materialsystem/shaderapidx9/winutils.cpp new file mode 100644 index 0000000..5da2493 --- /dev/null +++ b/materialsystem/shaderapidx9/winutils.cpp @@ -0,0 +1,93 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// winutils.cpp +// +//===========================================================================// + +#include "winutils.h" + +#ifndef _WIN32 + +#include "appframework/ilaunchermgr.h" + +// LINUX path taken from //Steam/main/src/tier0/platform_posix.cpp - Returns installed RAM in MB. +static unsigned long GetInstalledRAM() +{ + unsigned long ulTotalRamMB = 2047; + +#ifdef LINUX + char rgchLine[256]; + FILE *fpMemInfo = fopen( "/proc/meminfo", "r" ); + if ( !fpMemInfo ) + return ulTotalRamMB; + + const char *pszSearchString = "MemTotal:"; + const uint cchSearchString = strlen( pszSearchString ); + while ( fgets( rgchLine, sizeof(rgchLine), fpMemInfo ) ) + { + if ( !strncasecmp( pszSearchString, rgchLine, cchSearchString ) ) + { + char *pszVal = rgchLine+cchSearchString; + while( isspace(*pszVal) ) + ++pszVal; + ulTotalRamMB = atol( pszVal ) / 1024; // go from kB to MB + break; + } + } + fclose( fpMemInfo ); +#endif + + // 128 Gb limit for now (should future proof us for a while) + ulTotalRamMB = MIN( ulTotalRamMB, 1024 * 128 ); + return ulTotalRamMB; +} + +void GlobalMemoryStatus( MEMORYSTATUS *pOut ) +{ + unsigned long nInstalledRamInMB = GetInstalledRAM(); + + // For safety assume at least 128MB + nInstalledRamInMB = MAX( nInstalledRamInMB, 128 ); + + uint64 ulTotalRam = static_cast( nInstalledRamInMB ) * ( 1024 * 1024 ); + ulTotalRam = MIN( ulTotalRam, 0xFFFFFFFF ); + + pOut->dwTotalPhys = static_cast( ulTotalRam ); +} + +void Sleep( unsigned int ms ) +{ + DebuggerBreak(); + ThreadSleep( ms ); +} + +bool IsIconic( VD3DHWND hWnd ) +{ + // FIXME for now just act non-minimized all the time + //DebuggerBreak(); + return false; +} + +BOOL ClientToScreen( VD3DHWND hWnd, LPPOINT pPoint ) +{ + DebuggerBreak(); + return true; +} + +void* GetCurrentThread() +{ + DebuggerBreak(); + return 0; +} + +void SetThreadAffinityMask( void *hThread, int nMask ) +{ + DebuggerBreak(); +} + +bool GUID::operator==( const struct _GUID &other ) const +{ + DebuggerBreak(); + return memcmp( this, &other, sizeof( GUID ) ) == 0; +} +#endif diff --git a/materialsystem/shaderapidx9/winutils.h b/materialsystem/shaderapidx9/winutils.h new file mode 100644 index 0000000..0874fba --- /dev/null +++ b/materialsystem/shaderapidx9/winutils.h @@ -0,0 +1,21 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// +// +//================================================================================================== +#ifndef WINUTILS_H +#define WINUTILS_H + +#include "togl/rendermechanism.h" // for win types + +#if !defined(_WIN32) + + void Sleep( unsigned int ms ); + bool IsIconic( VD3DHWND hWnd ); + BOOL ClientToScreen( VD3DHWND hWnd, LPPOINT pPoint ); + void* GetCurrentThread(); + void SetThreadAffinityMask( void *hThread, int nMask ); + void GlobalMemoryStatus( MEMORYSTATUS *pOut ); +#endif + +#endif // WINUTILS_H diff --git a/materialsystem/shaderapidx9/wmi.cpp b/materialsystem/shaderapidx9/wmi.cpp new file mode 100644 index 0000000..1b0644f --- /dev/null +++ b/materialsystem/shaderapidx9/wmi.cpp @@ -0,0 +1,182 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//============================================================================= + +#include "resource.h" + +// Avoid conflicts with MSVC headers and memdbgon.h +#undef PROTECTED_THINGS_ENABLE +#include "basetypes.h" + +#define _WIN32_DCOM +#include +#pragma warning( disable : 4127 ) // VS 2010 warning? +#pragma warning( disable : 4805 ) // VS 2013 warning: warning C4805: '==' : unsafe mix of type 'INT' and type 'bool' in operation +#include +#pragma warning( default : 4805 ) +#pragma warning( default : 4127 ) +#include + +// NOTE: This has to be the last file included! +#include "tier0/memdbgon.h" + + +# pragma comment(lib, "wbemuuid.lib") + +uint64 GetVidMemBytes( void ) +{ + static int bBeenHere = false; + static uint64 nBytes = 0; + + if( bBeenHere ) + { + return nBytes; + } + + bBeenHere = true; + + // Initialize COM + HRESULT hr = CoInitialize( NULL ); + if ( FAILED( hr ) ) + { + OutputDebugString ( "GetWMIDeviceStats - Unable to initialize COM library.\n"); + return 0; + } + + // Obtain the initial locator to WMI + IWbemLocator *pLoc = NULL; + + hr = CoCreateInstance( + CLSID_WbemLocator, + 0, + CLSCTX_INPROC_SERVER, + IID_IWbemLocator, (LPVOID *) &pLoc); + + if ( FAILED( hr ) ) + { + OutputDebugString ( "GetWMIDeviceStats - Failed to create IWbemLocator object.\n"); + CoUninitialize(); + return 0; + } + + // Connect to WMI through the IWbemLocator::ConnectServer method + + IWbemServices *pSvc = NULL; + + // Connect to the root\cimv2 namespace with + // the current user and obtain pointer pSvc + // to make IWbemServices calls. + hr = pLoc->ConnectServer( + _bstr_t(L"ROOT\\CIMV2"), // Object path of WMI namespace + NULL, // User name. NULL = current user + NULL, // User password. NULL = current + 0, // Locale. NULL indicates current + NULL, // Security flags. + 0, // Authority (e.g. Kerberos) + 0, // Context object + &pSvc // pointer to IWbemServices proxy + ); + + if ( FAILED( hr ) ) + { + OutputDebugString ( "GetWMIDeviceStats - Could not connect.\n"); + pLoc->Release(); + CoUninitialize(); + return 0; + } + +// OutputDebugString ( L"GetWMIDeviceStats - Connected to ROOT\\CIMV2 WMI namespace\n"); + + + // Set security levels on the proxy + + hr = CoSetProxyBlanket( + pSvc, // Indicates the proxy to set + RPC_C_AUTHN_WINNT, // RPC_C_AUTHN_xxx + RPC_C_AUTHZ_NONE, // RPC_C_AUTHZ_xxx + NULL, // Server principal name + RPC_C_AUTHN_LEVEL_CALL, // RPC_C_AUTHN_LEVEL_xxx + RPC_C_IMP_LEVEL_IMPERSONATE, // RPC_C_IMP_LEVEL_xxx + NULL, // client identity + EOAC_NONE // proxy capabilities + ); + + if ( FAILED( hr ) ) + { + OutputDebugString ( "GetWMIDeviceStats - Could not set proxy blanket.\n"); + pSvc->Release(); + pLoc->Release(); + CoUninitialize(); + return 0; + } + + + // Use the IWbemServices pointer to make requests of WMI + + // + // --- Win32_VideoController -------------------------------------------------- + // + + IEnumWbemClassObject* pEnumerator = NULL; + hr = pSvc->ExecQuery( bstr_t("WQL"), bstr_t("SELECT * FROM Win32_VideoController"), + WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY, NULL, &pEnumerator); + + if ( FAILED( hr ) ) + { + OutputDebugString ( "GetWMIDeviceStats - Query for Win32_VideoController failed.\n"); + + pSvc->Release(); + pLoc->Release(); + CoUninitialize(); + return 0; + } + + + // Get the data from the above query + IWbemClassObject *pclsObj = NULL; + ULONG uReturn = 0; + + while ( pEnumerator ) + { + HRESULT hr = pEnumerator->Next(WBEM_INFINITE, 1, &pclsObj, &uReturn); + + if(0 == uReturn) + { + break; + } + + VARIANT vtProp; + VariantInit(&vtProp); + + // Pluck a series of properties out of the query from Win32_VideoController + +// hr = pclsObj->Get(L"Description", 0, &vtProp, 0, 0); // Basically the same as "VideoProcessor" +// if ( SUCCEEDED( hr ) ) +// { +// wsprintf( pAdapter->m_szPrimaryAdapterDescription, vtProp.bstrVal ); +// } + + hr = pclsObj->Get(L"AdapterRAM", 0, &vtProp, 0, 0); + if ( SUCCEEDED( hr ) ) + { + nBytes = vtProp.ulVal; // Video RAM in bytes, AdatperRam is returned as the I4 type so we read it out as unsigned int, + // see http://msdn.microsoft.com/en-us/library/windows/desktop/aa394512(v=vs.85).aspx + } + + VariantClear(&vtProp); + } + + // Cleanup + pSvc->Release(); + pLoc->Release(); + pEnumerator->Release(); + if ( pclsObj ) + { + pclsObj->Release(); + } + CoUninitialize(); + + return nBytes; +} diff --git a/materialsystem/shaderapidx9/wmi.h b/materialsystem/shaderapidx9/wmi.h new file mode 100644 index 0000000..8f8fe13 --- /dev/null +++ b/materialsystem/shaderapidx9/wmi.h @@ -0,0 +1,17 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//============================================================================= + +#ifndef WMI_H +#define WMI_H +#ifdef _WIN32 +#pragma once +#endif + + +uint64 GetVidMemBytes( void ); + + +#endif // WMI_H diff --git a/studiorender/Release/studiorender.dll b/studiorender/Release/studiorender.dll deleted file mode 100644 index 889dfb4..0000000 Binary files a/studiorender/Release/studiorender.dll and /dev/null differ diff --git a/studiorender/Release/studiorender.lib b/studiorender/Release/studiorender.lib deleted file mode 100644 index 477c299..0000000 Binary files a/studiorender/Release/studiorender.lib and /dev/null differ diff --git a/studiorender/Release/studiorender.pdb b/studiorender/Release/studiorender.pdb deleted file mode 100644 index d0f7b00..0000000 Binary files a/studiorender/Release/studiorender.pdb and /dev/null differ diff --git a/studiorender/Release/vc120.pdb b/studiorender/Release/vc120.pdb deleted file mode 100644 index 6621763..0000000 Binary files a/studiorender/Release/vc120.pdb and /dev/null differ diff --git a/utils/bsppack/Release/bsppack.dll b/utils/bsppack/Release/bsppack.dll deleted file mode 100644 index fef61f7..0000000 Binary files a/utils/bsppack/Release/bsppack.dll and /dev/null differ diff --git a/utils/bsppack/Release/bsppack.lib b/utils/bsppack/Release/bsppack.lib deleted file mode 100644 index a5cf78d..0000000 Binary files a/utils/bsppack/Release/bsppack.lib and /dev/null differ diff --git a/utils/bsppack/Release/bsppack.pdb b/utils/bsppack/Release/bsppack.pdb deleted file mode 100644 index dd9e85a..0000000 Binary files a/utils/bsppack/Release/bsppack.pdb and /dev/null differ diff --git a/utils/bsppack/Release/vc120.pdb b/utils/bsppack/Release/vc120.pdb deleted file mode 100644 index 0c965c5..0000000 Binary files a/utils/bsppack/Release/vc120.pdb and /dev/null differ diff --git a/utils/bspzip/Release/bspzip.exe b/utils/bspzip/Release/bspzip.exe deleted file mode 100644 index b3916e9..0000000 Binary files a/utils/bspzip/Release/bspzip.exe and /dev/null differ diff --git a/utils/bspzip/Release/bspzip.lib b/utils/bspzip/Release/bspzip.lib deleted file mode 100644 index 21c88bd..0000000 Binary files a/utils/bspzip/Release/bspzip.lib and /dev/null differ diff --git a/utils/bspzip/Release/bspzip.pdb b/utils/bspzip/Release/bspzip.pdb deleted file mode 100644 index 6a8c1e7..0000000 Binary files a/utils/bspzip/Release/bspzip.pdb and /dev/null differ diff --git a/utils/bspzip/Release/vc120.pdb b/utils/bspzip/Release/vc120.pdb deleted file mode 100644 index 3ad298d..0000000 Binary files a/utils/bspzip/Release/vc120.pdb and /dev/null differ diff --git a/vpc_scripts/groups.vgc b/vpc_scripts/groups.vgc index 0b75fc9..7bb1ac8 100644 --- a/vpc_scripts/groups.vgc +++ b/vpc_scripts/groups.vgc @@ -25,6 +25,7 @@ $Group "game" "engine" "materialsystem" "studiorender" + "shaderapidx9" "filesystem_stdio" "filesystem_steam" "vpklib" @@ -36,6 +37,7 @@ $Group "game" $Group "shaders" { "game_shader_dx9" + "shaderapidx9" } $Group "everything" @@ -53,6 +55,7 @@ $Group "everything" "qc_eyes" "server" "serverplugin_empty" + "shaderapidx9" "tgadiff" "tier1" "vbsp" diff --git a/vpc_scripts/projects.vgc b/vpc_scripts/projects.vgc index d1e28c7..8d7e521 100644 --- a/vpc_scripts/projects.vgc +++ b/vpc_scripts/projects.vgc @@ -80,6 +80,11 @@ $Project "serverplugin_empty" "utils\serverplugin_sample\serverplugin_empty.vpc" [$WIN32||$POSIX] } +$Project "shaderapidx9" +{ + "materialsystem\shaderapidx9\shaderapidx9.vpc" +} + $Project "tgadiff" { "utils\tgadiff\tgadiff.vpc" [$WIN32]