summaryrefslogtreecommitdiff
path: root/utils/shadercompile/d3dxfxc.cpp
blob: 98327e940827646c1ee3f25d1704be95b88a1c8b (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
//========= 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