Team Fortress 2 Source Code as on 22/4/2020
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

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: D3DX command implementation.
  4. //
  5. // $NoKeywords: $
  6. //
  7. //=============================================================================//
  8. #include "shadercompile.h"
  9. #include "d3dxfxc.h"
  10. #include "cmdsink.h"
  11. // Required to compile using D3DX* routines in the same process
  12. #include <d3dx9shader.h>
  13. #include "dx_proxy/dx_proxy.h"
  14. #include <tier0/icommandline.h>
  15. #include <tier1/strtools.h>
  16. #define D3DXSHADER_MICROCODE_BACKEND_OLD_DEPRECATED ( 1 << 25 )
  17. namespace InterceptFxc
  18. {
  19. // The command that is intercepted by this namespace routines
  20. static const char *s_pszCommand = "fxc.exe ";
  21. static size_t s_uCommandLen = strlen( s_pszCommand );
  22. namespace Private
  23. {
  24. //
  25. // Response implementation
  26. //
  27. class CResponse : public CmdSink::IResponse
  28. {
  29. public:
  30. explicit CResponse( LPD3DXBUFFER pShader, LPD3DXBUFFER pListing, HRESULT hr );
  31. ~CResponse( void );
  32. public:
  33. virtual bool Succeeded( void ) { return m_pShader && (m_hr == D3D_OK); }
  34. virtual size_t GetResultBufferLen( void ) { return ( Succeeded() ? m_pShader->GetBufferSize() : 0 ); }
  35. virtual const void * GetResultBuffer( void ) { return ( Succeeded() ? m_pShader->GetBufferPointer() : NULL ); }
  36. virtual const char * GetListing( void ) { return (const char *) ( m_pListing ? m_pListing->GetBufferPointer() : NULL ); }
  37. protected:
  38. LPD3DXBUFFER m_pShader;
  39. LPD3DXBUFFER m_pListing;
  40. HRESULT m_hr;
  41. };
  42. CResponse::CResponse( LPD3DXBUFFER pShader, LPD3DXBUFFER pListing, HRESULT hr ) :
  43. m_pShader(pShader),
  44. m_pListing(pListing),
  45. m_hr(hr)
  46. {
  47. NULL;
  48. }
  49. CResponse::~CResponse( void )
  50. {
  51. if ( m_pShader )
  52. m_pShader->Release();
  53. if ( m_pListing )
  54. m_pListing->Release();
  55. }
  56. //
  57. // Perform a fast shader file compilation.
  58. // TODO: avoid writing "shader.o" and "output.txt" files to avoid extra filesystem access.
  59. //
  60. // @param pszFilename the filename to compile (e.g. "debugdrawenvmapmask_vs20.fxc")
  61. // @param pMacros null-terminated array of macro-defines
  62. // @param pszModel shader model for compilation
  63. //
  64. void FastShaderCompile( const char *pszFilename, const D3DXMACRO *pMacros, const char *pszModel, CmdSink::IResponse **ppResponse )
  65. {
  66. LPD3DXBUFFER pShader = NULL; // NOTE: Must release the COM interface later
  67. LPD3DXBUFFER pErrorMessages = NULL; // NOTE: Must release COM interface later
  68. // DxProxyModule
  69. static DxProxyModule s_dxModule;
  70. // X360TEMP: This needs to be moved to an external semantic (or fixed)
  71. bool bIsX360 = false;
  72. for ( int i=0; ;i++ )
  73. {
  74. if ( !pMacros[i].Name )
  75. {
  76. break;
  77. }
  78. if ( V_stristr( pMacros[i].Name, "_X360" ) && atoi( pMacros[i].Definition ) )
  79. {
  80. bIsX360 = true;
  81. break;
  82. }
  83. }
  84. HRESULT hr = s_dxModule.D3DXCompileShaderFromFile( pszFilename, pMacros, NULL /* LPD3DXINCLUDE */,
  85. "main", pszModel, 0, &pShader, &pErrorMessages,
  86. NULL /* LPD3DXCONSTANTTABLE *ppConstantTable */ );
  87. if ( ppResponse )
  88. {
  89. *ppResponse = new CResponse( pShader, pErrorMessages, hr );
  90. }
  91. else
  92. {
  93. if ( pShader )
  94. {
  95. pShader->Release();
  96. }
  97. if ( pErrorMessages )
  98. {
  99. pErrorMessages->Release();
  100. }
  101. }
  102. }
  103. }; // namespace Private
  104. //
  105. // Completely mimic the behaviour of "fxc.exe" in the specific cases related
  106. // to shader compilations.
  107. //
  108. // @param pCommand the command in form
  109. // "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"
  110. //
  111. void ExecuteCommand( const char *pCommand, CmdSink::IResponse **ppResponse )
  112. {
  113. // Expect that the command passed is exactly "fxc.exe"
  114. Assert( !strncmp( pCommand, s_pszCommand, s_uCommandLen ) );
  115. pCommand += s_uCommandLen;
  116. // A duplicate portion of memory for modifications
  117. void *bufEditableCommand = alloca( strlen( pCommand ) + 1 );
  118. char *pEditableCommand = strcpy( (char *) bufEditableCommand, pCommand );
  119. // Macros to be defined for D3DX
  120. CUtlVector<D3DXMACRO> macros;
  121. // Shader model (determined when parsing "/D" flags)
  122. const char *pszShaderModel = NULL;
  123. // Iterate over the command line and find all "/D...=..." settings
  124. for ( char *pszFlag = pEditableCommand;
  125. ( pszFlag = strstr( pszFlag, "/D" ) ) != NULL;
  126. /* advance inside */ )
  127. {
  128. // Make sure this is a command-line flag (basic check for preceding space)
  129. if ( pszFlag > pEditableCommand &&
  130. pszFlag[-1] &&
  131. ' ' != pszFlag[-1] )
  132. {
  133. ++ pszFlag;
  134. continue;
  135. }
  136. // Name is immediately after "/D"
  137. char *pszFlagName = pszFlag + 2; // 2 = length of "/D"
  138. // Value will be determined later
  139. char *pszValue = "";
  140. if ( char *pchEq = strchr( pszFlag, '=' ) )
  141. {
  142. // Value is after '=' sign
  143. *pchEq = 0;
  144. pszValue = pchEq + 1;
  145. pszFlag = pszValue;
  146. }
  147. if ( char *pchSpace = strchr( pszFlag, ' ' ) )
  148. {
  149. // Space is designating the end of the flag
  150. *pchSpace = 0;
  151. pszFlag = pchSpace + 1;
  152. }
  153. else
  154. {
  155. // Reached end of command line
  156. pszFlag = "";
  157. }
  158. // Shader model extraction
  159. if ( !strncmp(pszFlagName, "SHADER_MODEL_", 13) )
  160. {
  161. pszShaderModel = pszFlagName + 13;
  162. }
  163. // Add the macro definition to the macros array
  164. int iMacroIdx = macros.AddToTail();
  165. D3DXMACRO &m = macros[iMacroIdx];
  166. // Fill the macro data
  167. m.Name = pszFlagName;
  168. m.Definition = pszValue;
  169. }
  170. // Add a NULL-terminator
  171. {
  172. D3DXMACRO nullTerminatorMacro = { NULL, NULL };
  173. macros.AddToTail( nullTerminatorMacro );
  174. }
  175. // Convert shader model to lowercase
  176. char chShaderModel[20] = {0};
  177. if(pszShaderModel)
  178. {
  179. Q_strncpy( chShaderModel, pszShaderModel, sizeof(chShaderModel) - 1 );
  180. }
  181. Q_strlower( chShaderModel );
  182. // Determine the file name (at the end of the command line before redirection)
  183. char const *pszFilename = "";
  184. if ( const char *pchCmdRedirect = strstr( pCommand, ">output.txt " ) )
  185. {
  186. size_t uCmdEndOffset = ( pchCmdRedirect - pCommand );
  187. pEditableCommand[uCmdEndOffset] = 0;
  188. pszFilename = &pEditableCommand[uCmdEndOffset];
  189. while ( pszFilename > pEditableCommand &&
  190. pszFilename[-1] &&
  191. ' ' != pszFilename[-1] )
  192. {
  193. -- pszFilename;
  194. }
  195. }
  196. // Compile the stuff
  197. Private::FastShaderCompile( pszFilename, macros.Base(), chShaderModel, ppResponse );
  198. }
  199. bool TryExecuteCommand( const char *pCommand, CmdSink::IResponse **ppResponse )
  200. {
  201. {
  202. static bool s_bNoIntercept = ( CommandLine()->FindParm("-nointercept") != 0 );
  203. static int s_dummy = ( Msg( s_bNoIntercept ?
  204. "[shadercompile] Using old slow technique - runs 'fxc.exe'.\n" :
  205. "[shadercompile] Using new faster Vitaliy's implementation.\n" ), 1 );
  206. if ( !s_bNoIntercept && !strncmp(pCommand, InterceptFxc::s_pszCommand, InterceptFxc::s_uCommandLen) )
  207. {
  208. // Trap "fxc.exe" so that we did not spawn extra process every time
  209. InterceptFxc::ExecuteCommand( pCommand, ppResponse );
  210. return true;
  211. }
  212. }
  213. return false;
  214. }
  215. }; // namespace InterceptFxc