1 //
2 // Copyright 2014 The ANGLE Project Authors. All rights reserved.
3 // Use of this source code is governed by a BSD-style license that can be
4 // found in the LICENSE file.
5 //
6
7 #include "libANGLE/renderer/d3d/HLSLCompiler.h"
8
9 #include <sstream>
10
11 #include "common/utilities.h"
12 #include "libANGLE/Program.h"
13 #include "libANGLE/features.h"
14 #include "libANGLE/histogram_macros.h"
15 #include "third_party/trace_event/trace_event.h"
16
17 #ifndef QT_D3DCOMPILER_DLL
18 #define QT_D3DCOMPILER_DLL D3DCOMPILER_DLL
19 #endif
20
21 #if ANGLE_APPEND_ASSEMBLY_TO_SHADER_DEBUG_INFO == ANGLE_ENABLED
22 namespace
23 {
24 #ifdef CREATE_COMPILER_FLAG_INFO
25 #undef CREATE_COMPILER_FLAG_INFO
26 #endif
27
28 #define CREATE_COMPILER_FLAG_INFO(flag) { flag, #flag }
29
30 struct CompilerFlagInfo
31 {
32 UINT mFlag;
33 const char *mName;
34 };
35
36 CompilerFlagInfo CompilerFlagInfos[] =
37 {
38 // NOTE: The data below is copied from d3dcompiler.h
39 // If something changes there it should be changed here as well
40 CREATE_COMPILER_FLAG_INFO(D3DCOMPILE_DEBUG), // (1 << 0)
41 CREATE_COMPILER_FLAG_INFO(D3DCOMPILE_SKIP_VALIDATION), // (1 << 1)
42 CREATE_COMPILER_FLAG_INFO(D3DCOMPILE_SKIP_OPTIMIZATION), // (1 << 2)
43 CREATE_COMPILER_FLAG_INFO(D3DCOMPILE_PACK_MATRIX_ROW_MAJOR), // (1 << 3)
44 CREATE_COMPILER_FLAG_INFO(D3DCOMPILE_PACK_MATRIX_COLUMN_MAJOR), // (1 << 4)
45 CREATE_COMPILER_FLAG_INFO(D3DCOMPILE_PARTIAL_PRECISION), // (1 << 5)
46 CREATE_COMPILER_FLAG_INFO(D3DCOMPILE_FORCE_VS_SOFTWARE_NO_OPT), // (1 << 6)
47 CREATE_COMPILER_FLAG_INFO(D3DCOMPILE_FORCE_PS_SOFTWARE_NO_OPT), // (1 << 7)
48 CREATE_COMPILER_FLAG_INFO(D3DCOMPILE_NO_PRESHADER), // (1 << 8)
49 CREATE_COMPILER_FLAG_INFO(D3DCOMPILE_AVOID_FLOW_CONTROL), // (1 << 9)
50 CREATE_COMPILER_FLAG_INFO(D3DCOMPILE_PREFER_FLOW_CONTROL), // (1 << 10)
51 CREATE_COMPILER_FLAG_INFO(D3DCOMPILE_ENABLE_STRICTNESS), // (1 << 11)
52 CREATE_COMPILER_FLAG_INFO(D3DCOMPILE_ENABLE_BACKWARDS_COMPATIBILITY), // (1 << 12)
53 CREATE_COMPILER_FLAG_INFO(D3DCOMPILE_IEEE_STRICTNESS), // (1 << 13)
54 CREATE_COMPILER_FLAG_INFO(D3DCOMPILE_OPTIMIZATION_LEVEL0), // (1 << 14)
55 CREATE_COMPILER_FLAG_INFO(D3DCOMPILE_OPTIMIZATION_LEVEL1), // 0
56 CREATE_COMPILER_FLAG_INFO(D3DCOMPILE_OPTIMIZATION_LEVEL2), // ((1 << 14) | (1 << 15))
57 CREATE_COMPILER_FLAG_INFO(D3DCOMPILE_OPTIMIZATION_LEVEL3), // (1 << 15)
58 CREATE_COMPILER_FLAG_INFO(D3DCOMPILE_RESERVED16), // (1 << 16)
59 CREATE_COMPILER_FLAG_INFO(D3DCOMPILE_RESERVED17), // (1 << 17)
60 CREATE_COMPILER_FLAG_INFO(D3DCOMPILE_WARNINGS_ARE_ERRORS) // (1 << 18)
61 };
62
63 #undef CREATE_COMPILER_FLAG_INFO
64
IsCompilerFlagSet(UINT mask,UINT flag)65 bool IsCompilerFlagSet(UINT mask, UINT flag)
66 {
67 bool isFlagSet = IsMaskFlagSet(mask, flag);
68
69 switch(flag)
70 {
71 case D3DCOMPILE_OPTIMIZATION_LEVEL0:
72 return isFlagSet && !IsMaskFlagSet(mask, UINT(D3DCOMPILE_OPTIMIZATION_LEVEL3));
73
74 case D3DCOMPILE_OPTIMIZATION_LEVEL1:
75 return (mask & D3DCOMPILE_OPTIMIZATION_LEVEL2) == UINT(0);
76
77 case D3DCOMPILE_OPTIMIZATION_LEVEL3:
78 return isFlagSet && !IsMaskFlagSet(mask, UINT(D3DCOMPILE_OPTIMIZATION_LEVEL0));
79
80 default:
81 return isFlagSet;
82 }
83 }
84 } // anonymous namespace
85 #endif // ANGLE_APPEND_ASSEMBLY_TO_SHADER_DEBUG_INFO == ANGLE_ENABLED
86
87 namespace rx
88 {
89
CompileConfig()90 CompileConfig::CompileConfig()
91 : flags(0),
92 name()
93 {
94 }
95
CompileConfig(UINT flags,const std::string & name)96 CompileConfig::CompileConfig(UINT flags, const std::string &name)
97 : flags(flags),
98 name(name)
99 {
100 }
101
HLSLCompiler()102 HLSLCompiler::HLSLCompiler()
103 : mInitialized(false),
104 mD3DCompilerModule(nullptr),
105 mD3DCompileFunc(nullptr),
106 mD3DDisassembleFunc(nullptr)
107 {
108 }
109
~HLSLCompiler()110 HLSLCompiler::~HLSLCompiler()
111 {
112 release();
113 }
114
ensureInitialized()115 gl::Error HLSLCompiler::ensureInitialized()
116 {
117 if (mInitialized)
118 {
119 return gl::NoError();
120 }
121
122 TRACE_EVENT0("gpu.angle", "HLSLCompiler::initialize");
123 #if !defined(ANGLE_ENABLE_WINDOWS_STORE)
124 #if defined(ANGLE_PRELOADED_D3DCOMPILER_MODULE_NAMES)
125 // Find a D3DCompiler module that had already been loaded based on a predefined list of versions.
126 static const char *d3dCompilerNames[] = ANGLE_PRELOADED_D3DCOMPILER_MODULE_NAMES;
127
128 for (size_t i = 0; i < ArraySize(d3dCompilerNames); ++i)
129 {
130 if (GetModuleHandleExA(0, d3dCompilerNames[i], &mD3DCompilerModule))
131 {
132 break;
133 }
134 }
135 #endif // ANGLE_PRELOADED_D3DCOMPILER_MODULE_NAMES
136
137 // Load the compiler DLL specified by the environment, or default to QT_D3DCOMPILER_DLL
138 const wchar_t *defaultCompiler = _wgetenv(L"QT_D3DCOMPILER_DLL");
139 if (!defaultCompiler)
140 defaultCompiler = QT_D3DCOMPILER_DLL;
141
142 const wchar_t *compilerDlls[] = {
143 defaultCompiler,
144 L"d3dcompiler_47.dll",
145 L"d3dcompiler_46.dll",
146 L"d3dcompiler_43.dll",
147 0
148 };
149
150 // Load the first available known compiler DLL
151 for (int i = 0; compilerDlls[i]; ++i)
152 {
153 mD3DCompilerModule = LoadLibrary(compilerDlls[i]);
154 if (mD3DCompilerModule)
155 break;
156 }
157
158 if (!mD3DCompilerModule)
159 {
160 // Load the version of the D3DCompiler DLL associated with the Direct3D version ANGLE was built with.
161 mD3DCompilerModule = LoadLibrary(D3DCOMPILER_DLL);
162 }
163
164 if (!mD3DCompilerModule)
165 {
166 ERR() << "D3D compiler module not found.";
167 return gl::OutOfMemory() << "D3D compiler module not found.";
168 }
169
170 mD3DCompileFunc = reinterpret_cast<pD3DCompile>(GetProcAddress(mD3DCompilerModule, "D3DCompile"));
171 ASSERT(mD3DCompileFunc);
172
173 mD3DDisassembleFunc = reinterpret_cast<pD3DDisassemble>(GetProcAddress(mD3DCompilerModule, "D3DDisassemble"));
174 ASSERT(mD3DDisassembleFunc);
175
176 #else
177 // D3D Shader compiler is linked already into this module, so the export
178 // can be directly assigned.
179 mD3DCompilerModule = nullptr;
180 mD3DCompileFunc = reinterpret_cast<pD3DCompile>(D3DCompile);
181 mD3DDisassembleFunc = reinterpret_cast<pD3DDisassemble>(D3DDisassemble);
182 #endif
183
184 if (mD3DCompileFunc == nullptr)
185 {
186 return gl::OutOfMemory() << "Error finding D3DCompile entry point.";
187 }
188
189 mInitialized = true;
190 return gl::NoError();
191 }
192
release()193 void HLSLCompiler::release()
194 {
195 if (mInitialized)
196 {
197 FreeLibrary(mD3DCompilerModule);
198 mD3DCompilerModule = nullptr;
199 mD3DCompileFunc = nullptr;
200 mD3DDisassembleFunc = nullptr;
201 mInitialized = false;
202 }
203 }
204
compileToBinary(gl::InfoLog & infoLog,const std::string & hlsl,const std::string & profile,const std::vector<CompileConfig> & configs,const D3D_SHADER_MACRO * overrideMacros,ID3DBlob ** outCompiledBlob,std::string * outDebugInfo)205 gl::Error HLSLCompiler::compileToBinary(gl::InfoLog &infoLog, const std::string &hlsl, const std::string &profile,
206 const std::vector<CompileConfig> &configs, const D3D_SHADER_MACRO *overrideMacros,
207 ID3DBlob **outCompiledBlob, std::string *outDebugInfo)
208 {
209 ASSERT(mInitialized);
210
211 #if !defined(ANGLE_ENABLE_WINDOWS_STORE)
212 ASSERT(mD3DCompilerModule);
213 #endif
214 ASSERT(mD3DCompileFunc);
215
216 #if !defined(ANGLE_ENABLE_WINDOWS_STORE)
217 if (gl::DebugAnnotationsActive())
218 {
219 std::string sourcePath = getTempPath();
220 std::ostringstream stream;
221 stream << "#line 2 \"" << sourcePath << "\"\n\n" << hlsl;
222 std::string sourceText = stream.str();
223 writeFile(sourcePath.c_str(), sourceText.c_str(), sourceText.size());
224 }
225 #endif
226
227 const D3D_SHADER_MACRO *macros = overrideMacros ? overrideMacros : nullptr;
228
229 for (size_t i = 0; i < configs.size(); ++i)
230 {
231 ID3DBlob *errorMessage = nullptr;
232 ID3DBlob *binary = nullptr;
233 HRESULT result = S_OK;
234
235 {
236 TRACE_EVENT0("gpu.angle", "D3DCompile");
237 SCOPED_ANGLE_HISTOGRAM_TIMER("GPU.ANGLE.D3DCompileMS");
238 result = mD3DCompileFunc(hlsl.c_str(), hlsl.length(), gl::g_fakepath, macros, nullptr,
239 "main", profile.c_str(), configs[i].flags, 0, &binary,
240 &errorMessage);
241 }
242
243 if (errorMessage)
244 {
245 std::string message = reinterpret_cast<const char*>(errorMessage->GetBufferPointer());
246 SafeRelease(errorMessage);
247
248 infoLog.appendSanitized(message.c_str());
249
250 // This produces unbelievable amounts of spam in about:gpu.
251 // WARN() << std::endl << hlsl;
252
253 WARN() << std::endl << message;
254
255 if ((message.find("error X3531:") != std::string::npos || // "can't unroll loops marked with loop attribute"
256 message.find("error X4014:") != std::string::npos) && // "cannot have gradient operations inside loops with divergent flow control",
257 // even though it is counter-intuitive to disable unrolling for this error,
258 // some very long shaders have trouble deciding which loops to unroll and
259 // turning off forced unrolls allows them to compile properly.
260 macros != nullptr)
261 {
262 macros = nullptr; // Disable [loop] and [flatten]
263
264 // Retry without changing compiler flags
265 i--;
266 continue;
267 }
268 }
269
270 if (SUCCEEDED(result))
271 {
272 *outCompiledBlob = binary;
273
274 (*outDebugInfo) += "// COMPILER INPUT HLSL BEGIN\n\n" + hlsl + "\n// COMPILER INPUT HLSL END\n";
275
276 #if ANGLE_APPEND_ASSEMBLY_TO_SHADER_DEBUG_INFO == ANGLE_ENABLED
277 (*outDebugInfo) += "\n\n// ASSEMBLY BEGIN\n\n";
278 (*outDebugInfo) += "// Compiler configuration: " + configs[i].name + "\n// Flags:\n";
279 for (size_t fIx = 0; fIx < ArraySize(CompilerFlagInfos); ++fIx)
280 {
281 if (IsCompilerFlagSet(configs[i].flags, CompilerFlagInfos[fIx].mFlag))
282 {
283 (*outDebugInfo) += std::string("// ") + CompilerFlagInfos[fIx].mName + "\n";
284 }
285 }
286
287 (*outDebugInfo) += "// Macros:\n";
288 if (macros == nullptr)
289 {
290 (*outDebugInfo) += "// - : -\n";
291 }
292 else
293 {
294 for (const D3D_SHADER_MACRO *mIt = macros; mIt->Name != nullptr; ++mIt)
295 {
296 (*outDebugInfo) += std::string("// ") + mIt->Name + " : " + mIt->Definition + "\n";
297 }
298 }
299
300 std::string disassembly;
301 ANGLE_TRY(disassembleBinary(binary, &disassembly));
302 (*outDebugInfo) += "\n" + disassembly + "\n// ASSEMBLY END\n";
303 #endif // ANGLE_APPEND_ASSEMBLY_TO_SHADER_DEBUG_INFO == ANGLE_ENABLED
304 return gl::NoError();
305 }
306
307 if (result == E_OUTOFMEMORY)
308 {
309 *outCompiledBlob = nullptr;
310 return gl::OutOfMemory()
311 << "HLSL compiler had an unexpected failure, " << gl::FmtHR(result);
312 }
313
314 infoLog << "Warning: D3D shader compilation failed with " << configs[i].name << " flags. ("
315 << profile << ")";
316
317 if (i + 1 < configs.size())
318 {
319 infoLog << " Retrying with " << configs[i + 1].name;
320 }
321 }
322
323 // None of the configurations succeeded in compiling this shader but the compiler is still intact
324 *outCompiledBlob = nullptr;
325 return gl::NoError();
326 }
327
disassembleBinary(ID3DBlob * shaderBinary,std::string * disassemblyOut)328 gl::Error HLSLCompiler::disassembleBinary(ID3DBlob *shaderBinary, std::string *disassemblyOut)
329 {
330 ANGLE_TRY(ensureInitialized());
331
332 // Retrieve disassembly
333 UINT flags = D3D_DISASM_ENABLE_DEFAULT_VALUE_PRINTS | D3D_DISASM_ENABLE_INSTRUCTION_NUMBERING;
334 ID3DBlob *disassembly = nullptr;
335 pD3DDisassemble disassembleFunc = reinterpret_cast<pD3DDisassemble>(mD3DDisassembleFunc);
336 LPCVOID buffer = shaderBinary->GetBufferPointer();
337 SIZE_T bufSize = shaderBinary->GetBufferSize();
338 HRESULT result = disassembleFunc(buffer, bufSize, flags, "", &disassembly);
339
340 if (SUCCEEDED(result))
341 {
342 *disassemblyOut = std::string(reinterpret_cast<const char*>(disassembly->GetBufferPointer()));
343 }
344 else
345 {
346 *disassemblyOut = "";
347 }
348
349 SafeRelease(disassembly);
350
351 return gl::NoError();
352 }
353
354 } // namespace rx
355