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