shaderc-rs

repository·main·Indexed 43 days ago

https://github.com/google/shaderc-rs

Rust bindings for the Google shaderc library, enabling the compilation of GLSL and HLSL shader source code into SPIR-V binary modules or assembly text. Version 0.10.1 provides a high-level interface via the Compiler, CompileOptions, and CompilationArtifact components to manage target environments, optimization levels, and resource bindings.

Tokens
6K
Snippets
15
Records
23
Agent score
55%

What's inside shaderc-rs

  1. How the shaderc-rs compilation workflow works

    main

    The shaderc crate provides a high-level interface for shader compilation through three main components:

    1. Compiler: The primary interface used to compile GLSL/HLSL source code into SPIR-V binary modules or assembly code, or to assemble SPIR-V assembly.
    2. CompileOptions: Used to adjust default compilation behavior, such as adding macro definitions.
    3. CompilationArtifact: Holds the successful results of a compilation (e.g., the binary SPIR-V or the text assembly).

    Common tasks include compiling source code into SPIR-V binaries using compile_into_spirv or into assembly text using compile_into_spirv_assembly.

  2. Install shaderc via Cargo

    main

    To use shaderc-rs in your Rust project, add the shaderc crate to your Cargo.toml dependencies and declare the extern crate in your root file.

    Note: shaderc-rs requires the native C++ shaderc library to be present on your system. The build script (shaderc-sys) will attempt to find or build it automatically based on your environment configuration.

    [dependencies]
    shaderc = "0.10"
    extern crate shaderc;
  3. Setup for Windows (MSVC and GNU cross-compilation)

    main

    windows-msvc Setup

    1. Set rustup to stable MSVC: rustup default stable-x86_64-pc-windows-msvc
    2. Install Build Tools for Visual Studio 2022.
    3. Ensure toolchain paths are in your PATH.

    windows-gnu Cross-compilation (from MSVC)

    Note: The windows-gnu toolchain is not directly supported, but you can cross-compile to it from windows-msvc:

    1. rustup default stable-x86_64-pc-windows-msvc
    2. rustup target install x86_64-pc-windows-gnu
    3. Install Build Tools for Visual Studio 2022.
    4. Install msys2.
    5. In the msys2 terminal, run:
      pacman --noconfirm -Syu mingw-w64-x86_64-cmake mingw-w64-x86_64-make mingw-w64-x86_64-python3 mingw-w64-x86_64-ninja
    6. Add the msys2 mingw64 binary path to your PATH.
    7. Build using the target flag:
      cargo run --target x86_64-pc-windows-gnu
  4. Setup requirements for building from source

    main

    If you use --features build-from-source, the following tools must be available in your PATH:

    • CMake
    • Git
    • Python 3 (On Windows, the executable must be named python.exe)
    • C++11 compiler
    • Ninja (Required on windows-msvc, optional on other platforms)

    Linux Setup (Ubuntu)

    sudo apt-get install build-essential cmake git ninja-build python3

    macOS Setup (Homebrew)

    brew install git cmake ninja python@3.12
  5. Configure native shaderc library discovery

    main

    The shaderc-sys build script searches for the native C++ shaderc library in a specific order. You can influence this behavior using environment variables or Cargo features:

    1. --features build-from-source: Forces the build script to compile a copy of the native C++ shaderc from source (requires git, cmake, and python).
    2. SHADERC_LIB_DIR: If set to /path/to/shaderc/libs/, the build script searches this path for native dynamic or static libraries.
    3. VULKAN_SDK: If set, the build script searches $VULKAN_SDK/lib.
    4. pkg-config: If available, it is used to find the library path.
    5. System Paths: On Linux, /usr/lib/ is searched if SHADERC_LIB_DIR is not set.

    Linking Preferences:

    • By default, the script tries to find the dynamic library (shaderc/shaderc_shared) first, then the static library (shaderc_combined).
    • Use the --features prefer-static-linking feature to reverse this order (static first, then dynamic).
  6. Configure shaderc-sys build behavior

    main

    The shaderc-sys crate handles system library detection and building from source via build.rs. You can control the search and build order using the following methods:

    1. Force build from source: Use the --features build-from-source Cargo feature to prevent automatic library detection.
    2. Specify library directory: Set the SHADERC_LIB_DIR environment variable to a path (e.g., /path/to/shaderc/libs/). The build script will search this directory for libshaderc_combined.a (and glslang/SPIRV libraries on Linux).
    3. System Search: On Linux, /usr/lib/ is automatically searched if no other options are provided.
    4. Fallback: If no library is found, the crate will attempt to check out and compile a copy of Shaderc (this is slow).

    Note on Ninja: The build script checks for ninja on PATH. While optional on most platforms, Ninja is required when building with Visual Studio because MSBuild does not support paths longer than MAX_PATH.

  7. Configure the build environment for shaderc-rs

    main

    When building shaderc-rs, the build script attempts to find the shaderc library using the following precedence:

    1. Feature Flag: Use --features build-from-source to force building from source instead of detecting a system library.
    2. Environment Variable: Set SHADERC_LIB_DIR to a path (e.g., /path/to/shaderc/libs/). The build script will search this directory for libshaderc_combined.a (and glslang/SPIRV libraries on Linux).
    3. System Search (Linux): On Linux, /usr/lib/ is automatically searched if no other options are provided.
    4. Fallback: If all else fails, shaderc-sys will attempt to check out and compile a copy of Shaderc (this is slow).
  8. Compile a shader into SPIR-V binary and assembly

    main

    This example demonstrates how to initialize a Compiler, configure CompileOptions with a macro, and compile a GLSL source string into both a SPIR-V binary module and a text assembly module.

    use shaderc;
    
    let source = "#version 310 es\n void EP() {}";
    
    let mut compiler = shaderc::Compiler::new().unwrap();
    let mut options = shaderc::CompileOptions::new().unwrap();
    options.add_macro_definition("EP", Some("main"));
    
    // Compile to SPIR-V binary
    let binary_result = compiler.compile_into_spirv(
        source, shaderc::ShaderKind::Vertex,
        "shader.glsl", "main", Some(&options)).unwrap();
    
    assert_eq!(Some(&0x07230203), binary_result.as_binary().first());
    
    // Compile to SPIR-V assembly text
    let text_result = compiler.compile_into_spirv_assembly(
        source, shaderc::ShaderKind::Vertex,
        "shader.glsl", "main", Some(&options)).unwrap();
    
    assert!(text_result.as_text().starts_with("; SPIR-V\n"));
  9. Get the SPIR-V version and revision

    main

    The get_spirv_version() function returns the version and revision of the SPIR-V generated by the library as a tuple (u32, u32).

    The version number is a 32-bit word where the high-order bytes represent 0 | Major Number | Minor Number | 0.

    let (version, revision) = get_spirv_version();
  10. Access compilation results with CompilationArtifact

    main

    The CompilationArtifact object contains the output of a successful compilation. It can represent either text (assembly) or binary (SPIR-V) data.

    • len() -> usize: Returns the number of bytes in the output.
    • is_empty() -> bool: Returns true if the output length is 0.
    • as_binary() -> &[u32]: Returns the SPIR-V output as a slice of 32-bit words. Panics if the result is not binary.
    • as_binary_u8() -> &[u8]: Returns the SPIR-V output as a byte slice. Panics if the result is not binary.
    • as_text() -> String: Returns the compilation output as a text string (e.g., SPIR-V assembly). Panics if the result is binary.
    • get_num_warnings() -> u32: Returns the number of warnings generated.
    • get_warning_messages() -> String: Returns the detailed warning messages as a string.
    let artifact = compiler.compile_into_spirv(source, kind, filename, entry, Some(&options)).unwrap();
    let spirv_bytes = artifact.as_binary_u8();
    println!("Compiled {} bytes", artifact.len());
  11. Configure compilation with CompileOptions

    main

    The CompileOptions object allows you to fine-tune the compilation process. You can pass these options to Compiler methods to control target environments, language versions, and resource limits.

    Key configuration capabilities include:

    • Target Environment: Set the target API (Vulkan, OpenGL, or OpenGLCompat) and its version using set_target_env.
    • SPIR-V Version: Specify the target SPIR-V version with set_target_spirv.
    • Source Language: Set the source language (GLSL or HLSL) with set_source_language.
    • GLSL Version/Profile: Override #version directives using set_forced_version_profile.
    • Resource Limits: Set specific hardware/driver limits using set_limit.
    • Automatic Binding: Enable/disable automatic uniform binding with set_auto_bind_uniforms or automatic image-sampler combining with set_auto_combined_image_sampler.
    • HLSL IO Mapping: Enable HLSL IO mapping rules with set_hlsl_io_mapping.
    let mut options = shaderc::CompileOptions::new().unwrap();
    options.set_target_env(shaderc::TargetEnv::Vulkan, shaderc::EnvVersion::Vulkan1_1 as u32);
    options.set_target_spirv(shaderc::SpirvVersion::V1_3);
    options.set_source_language(shaderc::SourceLanguage::GLSL);
    options.set_limit(shaderc::Limit::MaxTextureUnits, 16);
  12. Inspect compilation results

    main

    After a compilation call, use the ShadercCompilationResult pointer to check for success or retrieve errors and the resulting SPIR-V bytes.

    // Check status (e.g., check if it succeeded)
    let status = shaderc_result_get_compilation_status(result);
    
    if status != SUCCESS_CODE {
        // Get error message
        let err_msg = shaderc_result_get_error_message(result);
        println!("Error: {}", CStr::from_ptr(err_msg).to_str().unwrap());
    }
    
    // Get the compiled SPIR-V bytes
    let len = shaderc_result_get_length(result);
    let bytes = shaderc_result_get_bytes(result);
    
    // Release the result
    shaderc_result_release(result);