tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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