HLSLcc Documentation

repository·master·Indexed 21 days ago

https://github.com/unity-technologies/hlslcc

A DirectX shader bytecode cross-compiler used by Unity to translate DX bytecode into GLSL, GLSL ES, Vulkan-compatible GLSL, and Metal Shading Language. It features temp register type analysis, loop transformation, and a reflection interface. The library provides the TranslateHLSLFromMem() entry point, the HLSLCrossCompilerContext for state management, and the HLSLccToolkit for utility functions and shader code snippets.

Tokens
2.9K
Snippets
9
Records
18
Agent score
74%

What's inside HLSLcc

  1. What is HLSLcc and what does it do?

    master

    HLSLcc is a DirectX shader bytecode cross-compiler. It takes DirectX bytecode as input and translates it into several target languages:

    • GLSL (OpenGL 3.2 and later)
    • GLSL ES (OpenGL ES 2.0 and later)
    • GLSL for Vulkan (intended as input for Glslang to generate SPIR-V)
    • Metal Shading Language

    It is used by Unity to generate shaders for OpenGL, OpenGL ES 3.0+, Metal, and Vulkan. Key features include temp register type analysis to infer data types, loop transformation to restore for-loop constructs, support for partial precision variables (e.g., min16float), and a reflection interface to retrieve shader inputs and their types.

  2. Build HLSLcc from source

    master

    You can build HLSLcc using two methods:

    Manual Compilation

    Compile all files in src/*.cpp (using C++11 mode) and src/cbstring/*.c with the following include paths:

    • include
    • src/internal_includes
    • src/cbstrinc
    • src

    CMake

    Use the provided CMakeLists.txt to build the project using CMake.

  3. Use HLSLCrossCompilerContext for shader translation and reflection

    master

    The HLSLCrossCompilerContext class serves as the central state container for the HLSLcc translation process. It manages the current GLSL output string, indentation levels, compiler flags, and shader phase information. It also holds a reference to HLSLccReflection to provide callbacks for bindings and diagnostic information during the translation lifecycle.

    Key responsibilities include:

    • Managing shader extensions via RequireExtension and EnableExtension.
    • Tracking the current translation phase and active Translator.
    • Resolving declared input and output names for operands using GetDeclaredInputName and GetDeclaredOutputName.
    • Handling dependency data and shader phase analysis via DoDataTypeAnalysis and ClearDependencyData.
    // Example initialization (conceptual)
    HLSLccReflection reflection;
    HLSLCrossCompilerContext context(reflection);
    
    // The context is then used throughout the translation process to manage state,
    // such as checking for Vulkan support or managing extensions.
    if (context.IsVulkan()) {
        // ... handle Vulkan specific logic
    }
  4. Manage shader data and stages with Shader and ShaderPhase

    master

    HLSLcc uses two primary classes to represent shader programs: Shader and ShaderPhase.

    • Shader: Represents the entire shader program. It contains metadata such as the shader type (eShaderType), target language (eTargetLanguage), versioning, and a collection of ShaderPhase objects (asPhases). It also manages global shader information like resource dimensions, texture samplers, and function tables.
    • ShaderPhase: Represents a specific stage or phase within a shader (e.g., the main execution phase or specific phases in a Hull shader). Each phase maintains its own set of declarations (psDecl), instructions (psInst), and temporary registers. It also provides access to a Control Flow Graph (CFG) via GetCFG().
  5. Use TranslateHLSLFromMem() to translate shaders

    master

    The main entry point for the library is the TranslateHLSLFromMem() function located in HLSLcc.cpp. This function accepts DirectX bytecode as input to perform the translation.

    // The main entry point is TranslateHLSLFromMem() function in HLSLcc.cpp (taking DX bytecode as input).
  6. Retrieve type-specific constructors for GLSL and Metal

    master

    When generating code for specific shading languages, use these functions to get the appropriate constructor string for a given type and component count:

    • GetConstructorForTypeGLSL: Returns the GLSL constructor string. Supports a useGLSLPrecision flag.
    • GetConstructorForTypeMetal: Returns the Metal constructor string.
    • GetConstructorForType: A general constructor retriever (behavior depends on context/type).
    // GLSL
    const char* glslCtor = GetConstructorForTypeGLSL(context, SHADER_VARIABLE_TYPE_FLOAT, 4, true);
    
    // Metal
    const char* metalCtor = GetConstructorForTypeMetal(SHADER_VARIABLE_TYPE_FLOAT, 4);
  7. Translate HLSL declarations to GLSL

    master

    The Translate and TranslateDeclaration methods are responsible for converting HLSL declarations (like variables, structs, and buffers) into their GLSL equivalents. This includes handling system values, input/output prefixes, and constant buffer declarations.

    // Main entry point for translation
    virtual bool Translate();
    
    // Translates a specific declaration
    virtual void TranslateDeclaration(const Declaration* psDecl);
  8. Use the ToGLSL translator class

    master

    The ToGLSL class is a specialized implementation of the Translator interface used to convert HLSL shader code into GLSL. It manages language detection, declaration translation, and instruction mapping specifically for GLSL targets.

    To use it, you typically instantiate it with an HLSLCrossCompilerContext and call SetLanguage to specify the target GLSL version or allow autodetect via LANG_DEFAULT.

    // Example instantiation (requires HLSLCrossCompilerContext)
    ToGLSL translator(ctx);
    
    // Set the target language (e.g., using LANG_DEFAULT for autodetect)
    GLLang lang = translator.SetLanguage(LANG_DEFAULT);
    
    // Execute the translation
    bool success = translator.Translate();
  9. Use ShaderPhase to manipulate shader code and control flow

    master

    The ShaderPhase class provides methods to transform and analyze the shader code within a specific phase:

    • ResolveUAVProperties(const ShaderInfo& sInfo): Configures Unordered Access View properties based on the provided ShaderInfo.
    • UnvectorizeImmMoves(): Transforms vectorized immediate moves (e.g., MOV tX.xyz, (0, 1, 2)) into individual component moves (e.g., MOV tX.x, 0; MOV tX.y, 1; MOV tX.z, 2) to simplify datatype analysis.
    • PruneConstArrays(): Optimizes constant array usage by identifying and removing unused parts of the array.
    • ExpandSWAPCs(): Expands SWAPC opcodes into multiple MOVC opcodes. This must be called before other transformations.
    • GetCFG(): Returns a reference to the HLSLcc::ControlFlow::ControlFlowGraph for the current phase, building it if it hasn't been initialized.
  10. Convert between SVT and Resource types

    master

    The toolkit provides mapping functions to translate between SHADER_VARIABLE_TYPE (SVT) flags and other shader resource representations:

    • ResourceReturnTypeToFlag: Converts a RESOURCE_RETURN_TYPE to an SVT flag.
    • SVTTypeToResourceReturnType: Converts an SVT type back to a RESOURCE_RETURN_TYPE.
    • SVTTypeToPrecision: Extracts the REFLECT_RESOURCE_PRECISION from an SVT type.
    • ResourceReturnTypeToSVTType: Converts a resource return type and precision into an SVT flag.
  11. Iterate over instruction operands using ForEachOperand

    master

    The ForEachOperand template allows you to iterate over operands within a range of instructions using a callback function. You can control which operands are visited using bitwise flags:

    • FEO_FLAG_SUBOPERAND: Process sub-operands.
    • FEO_FLAG_SRC_OPERAND: Process source operands.
    • FEO_FLAG_DEST_OPERAND: Process destination operands.
    • FEO_FLAG_ALL: A convenience flag to process all of the above.
    // Example: Iterate over all source and destination operands in a range
    HLSLcc::ForEachOperand(it_begin, it_end, HLSLcc::FEO_FLAG_ALL, [](const Instruction* inst, const Operand* op, int flag) {
        // Handle operand based on flag
    });
  12. Generate shader code snippets with HLSLccToolkit

    master

    The HLSLccToolkit provides utility functions to retrieve boilerplate or structural shader code segments. You can use GetEarlyMain to retrieve code that should appear before the main function and GetPostShaderCode for code that should appear after the main function in the generated output.

    bstring earlyMain = GetEarlyMain(psContext);
    bstring postShader = GetPostShaderCode(psContext);