You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
259 lines
7.0 KiB
259 lines
7.0 KiB
//========= Copyright Valve Corporation, All rights reserved. ============// |
|
// |
|
// Purpose: D3DX command implementation. |
|
// |
|
// $NoKeywords: $ |
|
// |
|
//=============================================================================// |
|
|
|
#include "shadercompile.h" |
|
|
|
#include "d3dxfxc.h" |
|
#include "cmdsink.h" |
|
|
|
// Required to compile using D3DX* routines in the same process |
|
#include <d3dx9shader.h> |
|
#include "dx_proxy/dx_proxy.h" |
|
|
|
#include <tier0/icommandline.h> |
|
#include <tier1/strtools.h> |
|
|
|
#define D3DXSHADER_MICROCODE_BACKEND_OLD_DEPRECATED ( 1 << 25 ) |
|
|
|
namespace InterceptFxc |
|
{ |
|
|
|
// The command that is intercepted by this namespace routines |
|
static const char *s_pszCommand = "fxc.exe "; |
|
static size_t s_uCommandLen = strlen( s_pszCommand ); |
|
|
|
namespace Private |
|
{ |
|
// |
|
// Response implementation |
|
// |
|
class CResponse : public CmdSink::IResponse |
|
{ |
|
public: |
|
explicit CResponse( LPD3DXBUFFER pShader, LPD3DXBUFFER pListing, HRESULT hr ); |
|
~CResponse( void ); |
|
|
|
public: |
|
virtual bool Succeeded( void ) { return m_pShader && (m_hr == D3D_OK); } |
|
virtual size_t GetResultBufferLen( void ) { return ( Succeeded() ? m_pShader->GetBufferSize() : 0 ); } |
|
virtual const void * GetResultBuffer( void ) { return ( Succeeded() ? m_pShader->GetBufferPointer() : NULL ); } |
|
virtual const char * GetListing( void ) { return (const char *) ( m_pListing ? m_pListing->GetBufferPointer() : NULL ); } |
|
|
|
protected: |
|
LPD3DXBUFFER m_pShader; |
|
LPD3DXBUFFER m_pListing; |
|
HRESULT m_hr; |
|
}; |
|
|
|
CResponse::CResponse( LPD3DXBUFFER pShader, LPD3DXBUFFER pListing, HRESULT hr ) : |
|
m_pShader(pShader), |
|
m_pListing(pListing), |
|
m_hr(hr) |
|
{ |
|
NULL; |
|
} |
|
|
|
CResponse::~CResponse( void ) |
|
{ |
|
if ( m_pShader ) |
|
m_pShader->Release(); |
|
|
|
if ( m_pListing ) |
|
m_pListing->Release(); |
|
} |
|
|
|
// |
|
// Perform a fast shader file compilation. |
|
// TODO: avoid writing "shader.o" and "output.txt" files to avoid extra filesystem access. |
|
// |
|
// @param pszFilename the filename to compile (e.g. "debugdrawenvmapmask_vs20.fxc") |
|
// @param pMacros null-terminated array of macro-defines |
|
// @param pszModel shader model for compilation |
|
// |
|
void FastShaderCompile( const char *pszFilename, const D3DXMACRO *pMacros, const char *pszModel, CmdSink::IResponse **ppResponse ) |
|
{ |
|
LPD3DXBUFFER pShader = NULL; // NOTE: Must release the COM interface later |
|
LPD3DXBUFFER pErrorMessages = NULL; // NOTE: Must release COM interface later |
|
|
|
// DxProxyModule |
|
static DxProxyModule s_dxModule; |
|
|
|
// X360TEMP: This needs to be moved to an external semantic (or fixed) |
|
bool bIsX360 = false; |
|
for ( int i=0; ;i++ ) |
|
{ |
|
if ( !pMacros[i].Name ) |
|
{ |
|
break; |
|
} |
|
if ( V_stristr( pMacros[i].Name, "_X360" ) && atoi( pMacros[i].Definition ) ) |
|
{ |
|
bIsX360 = true; |
|
break; |
|
} |
|
} |
|
|
|
HRESULT hr = s_dxModule.D3DXCompileShaderFromFile( pszFilename, pMacros, NULL /* LPD3DXINCLUDE */, |
|
"main", pszModel, 0, &pShader, &pErrorMessages, |
|
NULL /* LPD3DXCONSTANTTABLE *ppConstantTable */ ); |
|
|
|
if ( ppResponse ) |
|
{ |
|
*ppResponse = new CResponse( pShader, pErrorMessages, hr ); |
|
} |
|
else |
|
{ |
|
if ( pShader ) |
|
{ |
|
pShader->Release(); |
|
} |
|
|
|
if ( pErrorMessages ) |
|
{ |
|
pErrorMessages->Release(); |
|
} |
|
} |
|
} |
|
|
|
}; // namespace Private |
|
|
|
// |
|
// Completely mimic the behaviour of "fxc.exe" in the specific cases related |
|
// to shader compilations. |
|
// |
|
// @param pCommand the command in form |
|
// "fxc.exe /DSHADERCOMBO=1 /DTOTALSHADERCOMBOS=4 /DCENTROIDMASK=0 /DNUMDYNAMICCOMBOS=4 /DFLAGS=0x0 /DNUM_BONES=1 /Dmain=main /Emain /Tvs_2_0 /DSHADER_MODEL_VS_2_0=1 /D_X360=1 /nologo /Foshader.o debugdrawenvmapmask_vs20.fxc>output.txt 2>&1" |
|
// |
|
void ExecuteCommand( const char *pCommand, CmdSink::IResponse **ppResponse ) |
|
{ |
|
// Expect that the command passed is exactly "fxc.exe" |
|
Assert( !strncmp( pCommand, s_pszCommand, s_uCommandLen ) ); |
|
pCommand += s_uCommandLen; |
|
|
|
// A duplicate portion of memory for modifications |
|
void *bufEditableCommand = alloca( strlen( pCommand ) + 1 ); |
|
char *pEditableCommand = strcpy( (char *) bufEditableCommand, pCommand ); |
|
|
|
// Macros to be defined for D3DX |
|
CUtlVector<D3DXMACRO> macros; |
|
|
|
// Shader model (determined when parsing "/D" flags) |
|
const char *pszShaderModel = NULL; |
|
|
|
// Iterate over the command line and find all "/D...=..." settings |
|
for ( char *pszFlag = pEditableCommand; |
|
( pszFlag = strstr( pszFlag, "/D" ) ) != NULL; |
|
/* advance inside */ ) |
|
{ |
|
// Make sure this is a command-line flag (basic check for preceding space) |
|
if ( pszFlag > pEditableCommand && |
|
pszFlag[-1] && |
|
' ' != pszFlag[-1] ) |
|
{ |
|
++ pszFlag; |
|
continue; |
|
} |
|
|
|
// Name is immediately after "/D" |
|
char *pszFlagName = pszFlag + 2; // 2 = length of "/D" |
|
// Value will be determined later |
|
char *pszValue = ""; |
|
|
|
if ( char *pchEq = strchr( pszFlag, '=' ) ) |
|
{ |
|
// Value is after '=' sign |
|
*pchEq = 0; |
|
pszValue = pchEq + 1; |
|
pszFlag = pszValue; |
|
} |
|
|
|
if ( char *pchSpace = strchr( pszFlag, ' ' ) ) |
|
{ |
|
// Space is designating the end of the flag |
|
*pchSpace = 0; |
|
pszFlag = pchSpace + 1; |
|
} |
|
else |
|
{ |
|
// Reached end of command line |
|
pszFlag = ""; |
|
} |
|
|
|
// Shader model extraction |
|
if ( !strncmp(pszFlagName, "SHADER_MODEL_", 13) ) |
|
{ |
|
pszShaderModel = pszFlagName + 13; |
|
} |
|
|
|
// Add the macro definition to the macros array |
|
int iMacroIdx = macros.AddToTail(); |
|
D3DXMACRO &m = macros[iMacroIdx]; |
|
|
|
// Fill the macro data |
|
m.Name = pszFlagName; |
|
m.Definition = pszValue; |
|
} |
|
|
|
// Add a NULL-terminator |
|
{ |
|
D3DXMACRO nullTerminatorMacro = { NULL, NULL }; |
|
macros.AddToTail( nullTerminatorMacro ); |
|
} |
|
|
|
// Convert shader model to lowercase |
|
char chShaderModel[20] = {0}; |
|
if(pszShaderModel) |
|
{ |
|
Q_strncpy( chShaderModel, pszShaderModel, sizeof(chShaderModel) - 1 ); |
|
} |
|
Q_strlower( chShaderModel ); |
|
|
|
// Determine the file name (at the end of the command line before redirection) |
|
char const *pszFilename = ""; |
|
if ( const char *pchCmdRedirect = strstr( pCommand, ">output.txt " ) ) |
|
{ |
|
size_t uCmdEndOffset = ( pchCmdRedirect - pCommand ); |
|
|
|
pEditableCommand[uCmdEndOffset] = 0; |
|
pszFilename = &pEditableCommand[uCmdEndOffset]; |
|
|
|
while ( pszFilename > pEditableCommand && |
|
pszFilename[-1] && |
|
' ' != pszFilename[-1] ) |
|
{ |
|
-- pszFilename; |
|
} |
|
} |
|
|
|
// Compile the stuff |
|
Private::FastShaderCompile( pszFilename, macros.Base(), chShaderModel, ppResponse ); |
|
} |
|
|
|
bool TryExecuteCommand( const char *pCommand, CmdSink::IResponse **ppResponse ) |
|
{ |
|
{ |
|
static bool s_bNoIntercept = ( CommandLine()->FindParm("-nointercept") != 0 ); |
|
static int s_dummy = ( Msg( s_bNoIntercept ? |
|
"[shadercompile] Using old slow technique - runs 'fxc.exe'.\n" : |
|
"[shadercompile] Using new faster Vitaliy's implementation.\n" ), 1 ); |
|
if ( !s_bNoIntercept && !strncmp(pCommand, InterceptFxc::s_pszCommand, InterceptFxc::s_uCommandLen) ) |
|
{ |
|
// Trap "fxc.exe" so that we did not spawn extra process every time |
|
InterceptFxc::ExecuteCommand( pCommand, ppResponse ); |
|
return true; |
|
} |
|
} |
|
|
|
return false; |
|
} |
|
|
|
}; // namespace InterceptFxc |
|
|
|
|
|
|
|
|
|
|