From bfc4a3b5fe8522f1cf625c0194f8592c47dad67f Mon Sep 17 00:00:00 2001 From: foxnne Date: Wed, 14 Oct 2026 08:14:19 -0500 Subject: [PATCH 1/5] ci: SPIR-V and DXIL compiled from their HLSL in CI, checked against what is committed --- .github/workflows/shaders.yml | 123 ++++++++++++++++++++++++++++++++++ 1 file changed, 123 insertions(+) create mode 100644 .github/workflows/shaders.yml diff --git a/.github/workflows/shaders.yml b/.github/workflows/shaders.yml new file mode 100644 index 000000000..90ea80fc8 --- /dev/null +++ b/.github/workflows/shaders.yml @@ -0,0 +1,123 @@ +# The SPIR-V and DXIL fizzy's native backend embeds (Vulkan, D3D12), compiled from their HLSL here +# rather than on someone's machine, and checked against what is committed. +# +# Each `X.hlsl` beside a `compiled/spv/X.spv` and `compiled/dxil/X.dxil` is compiled with +# SDL_shadercross and compared byte for byte. A difference fails the job and uploads the fresh +# files as the `compiled-shaders` artifact, laid out as the repository has them: download it and +# commit them. Before this, nothing noticed when the HLSL changed and the compiled files did not, +# and Windows and Linux quietly drew the previous glass (#242, #373). +# +# The toolchain is the one the committed files were made with: SDL_shadercross with the +# DirectXShaderCompiler fork it vendors (libsdl-org/DirectXShaderCompiler at 2c84a1c5), built +# with clang; built with GCC 13 that DXC corrupts its heap compiling DXIL. The shaders that have +# not changed since they were compiled (the backend's defaults) reproduce byte for byte, which is +# how a change of toolchain would show. The build takes a while, so it is cached by its pin. + +name: Shaders + +on: + pull_request: + paths: + - "**/*.hlsl" + - "**/shaders/compiled/**" + - ".github/workflows/shaders.yml" + push: + branches: [main] + paths: + - "**/*.hlsl" + - "**/shaders/compiled/**" + - ".github/workflows/shaders.yml" + workflow_dispatch: + +env: + SHADERCROSS_REPO: libsdl-org/SDL_shadercross + # Its `external/DirectXShaderCompiler` is libsdl-org/DirectXShaderCompiler at 2c84a1c5. + SHADERCROSS_SHA: 1ff05bec573988a98ef9e0260b4da44f512b8367 + +jobs: + compile: + runs-on: ubuntu-24.04 + timeout-minutes: 150 + steps: + - uses: actions/checkout@v4 + + - name: Cached shadercross + id: cache + uses: actions/cache@v4 + with: + path: ~/shadercross + key: shadercross-${{ env.SHADERCROSS_SHA }}-vendored-clang-1 + + - name: Toolchain + run: | + sudo apt-get update -y + sudo apt-get install -y clang ninja-build cmake libjson-perl spirv-tools + + # Every run: shadercross links SDL, cached or not (setup-sdl caches its own build). + - name: SDL + id: sdl + uses: libsdl-org/setup-sdl@main + with: + version: 3-latest + cmake-generator: Ninja + install-linux-dependencies: true + + - name: Build shadercross (vendored DXC, clang) + if: steps.cache.outputs.cache-hit != 'true' + env: + CC: clang + CXX: clang++ + run: | + git clone --filter=blob:none "https://github.com/$SHADERCROSS_REPO" sc + git -C sc checkout "$SHADERCROSS_SHA" + git -C sc submodule update --init --recursive --depth 1 --jobs 4 + git -C sc/external/DirectXShaderCompiler rev-parse HEAD + cmake -S sc -B sc/build -GNinja \ + -DCMAKE_BUILD_TYPE=Release \ + -DSDLSHADERCROSS_VENDORED=ON \ + -DSDLSHADERCROSS_CLI=ON \ + -DSDLSHADERCROSS_SHARED=ON \ + -DSDLSHADERCROSS_STATIC=OFF \ + -DSDLSHADERCROSS_WERROR=OFF \ + -DSDLSHADERCROSS_INSTALL=ON \ + -DSDLSHADERCROSS_INSTALL_RUNTIME=ON \ + -DCMAKE_INSTALL_PREFIX="$HOME/shadercross" + cmake --build sc/build --parallel + cmake --install sc/build + + - name: Compile and compare + run: | + export PATH="$HOME/shadercross/bin:$PATH" + export LD_LIBRARY_PATH="$HOME/shadercross/lib:${{ steps.sdl.outputs.prefix }}/lib:$LD_LIBRARY_PATH" + shadercross --help > /dev/null 2>&1 || { echo "shadercross does not run"; ldd "$HOME/shadercross/bin/shadercross"; exit 1; } + stale=0 + mkdir -p out + while IFS= read -r hlsl; do + dir=$(dirname "$hlsl"); name=$(basename "$hlsl" .hlsl) + for form in spv dxil; do + committed="$dir/compiled/$form/$name.$form" + [ -f "$committed" ] || continue + fresh="out/$committed" + mkdir -p "$(dirname "$fresh")" + (cd "$dir" && shadercross "$name.hlsl" -o "$GITHUB_WORKSPACE/$fresh") + if [ "$form" = spv ]; then spirv-val "$fresh"; fi + if cmp -s "$committed" "$fresh"; then + echo "ok $committed" + else + echo "::error file=$hlsl::$committed is not what $hlsl compiles to: download the compiled-shaders artifact and commit it" + stale=1 + fi + done + done < <(git ls-files '*.hlsl') + echo "stale=$stale" >> "$GITHUB_ENV" + + - name: Upload the fresh compiled shaders + if: env.stale == '1' + uses: actions/upload-artifact@v4 + with: + name: compiled-shaders + path: out/ + + - name: Fail on stale compiled shaders + if: env.stale == '1' + run: exit 1 From d3259b389b0813e9d4fcdabf8f3cb566dbb118db Mon Sep 17 00:00:00 2001 From: foxnne Date: Wed, 14 Oct 2026 12:19:25 -0500 Subject: [PATCH 2/5] shaders: the HLSL glass compiles again; the check reports every shader `float3(x)` from one scalar is Metal and GLSL, not HLSL: `(float3)x`. The workflow goes on past a shader that does not compile, uploads what did, and says where a stale one differs. Co-Authored-By: Claude Opus 5.5 --- .github/workflows/shaders.yml | 22 ++++++++++++++++----- core/gfx/shaders/liquid_glass.fragment.hlsl | 2 +- 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/.github/workflows/shaders.yml b/.github/workflows/shaders.yml index 90ea80fc8..6547cb8f5 100644 --- a/.github/workflows/shaders.yml +++ b/.github/workflows/shaders.yml @@ -91,6 +91,7 @@ jobs: export LD_LIBRARY_PATH="$HOME/shadercross/lib:${{ steps.sdl.outputs.prefix }}/lib:$LD_LIBRARY_PATH" shadercross --help > /dev/null 2>&1 || { echo "shadercross does not run"; ldd "$HOME/shadercross/bin/shadercross"; exit 1; } stale=0 + failed=0 mkdir -p out while IFS= read -r hlsl; do dir=$(dirname "$hlsl"); name=$(basename "$hlsl" .hlsl) @@ -99,25 +100,36 @@ jobs: [ -f "$committed" ] || continue fresh="out/$committed" mkdir -p "$(dirname "$fresh")" - (cd "$dir" && shadercross "$name.hlsl" -o "$GITHUB_WORKSPACE/$fresh") - if [ "$form" = spv ]; then spirv-val "$fresh"; fi + # One that does not compile is reported, and the rest still go. + if ! (cd "$dir" && shadercross "$name.hlsl" -o "$GITHUB_WORKSPACE/$fresh"); then + echo "::error file=$hlsl::$hlsl does not compile to $form" + failed=1 + continue + fi + if [ "$form" = spv ] && ! spirv-val "$fresh"; then + echo "::error file=$hlsl::$fresh does not validate" + failed=1 + fi if cmp -s "$committed" "$fresh"; then echo "ok $committed" else echo "::error file=$hlsl::$committed is not what $hlsl compiles to: download the compiled-shaders artifact and commit it" + echo " committed $(stat -c %s "$committed") bytes, compiled $(stat -c %s "$fresh"); first differences (offset, committed, compiled):" + cmp -l "$committed" "$fresh" | head -8 || true stale=1 fi done done < <(git ls-files '*.hlsl') echo "stale=$stale" >> "$GITHUB_ENV" + echo "failed=$failed" >> "$GITHUB_ENV" - name: Upload the fresh compiled shaders - if: env.stale == '1' + if: env.stale == '1' || env.failed == '1' uses: actions/upload-artifact@v4 with: name: compiled-shaders path: out/ - - name: Fail on stale compiled shaders - if: env.stale == '1' + - name: Fail on stale or broken compiled shaders + if: env.stale == '1' || env.failed == '1' run: exit 1 diff --git a/core/gfx/shaders/liquid_glass.fragment.hlsl b/core/gfx/shaders/liquid_glass.fragment.hlsl index 38c81fffc..42bf83ad9 100644 --- a/core/gfx/shaders/liquid_glass.fragment.hlsl +++ b/core/gfx/shaders/liquid_glass.fragment.hlsl @@ -209,7 +209,7 @@ float4 main(PSInput input) : SV_Target0 // The glass's own light, added whole. Scaled with the picture, it went wherever the picture // was see-through — over the explorer, which is the window's OS glass — and the glass all but // vanished there, rim and all, where Liquid Glass keeps its bright rim over anything. - o.rgb += float3(clamp(lift + lens * g2.w * lit, 0.0, 1.0)); + o.rgb += (float3)clamp(lift + lens * g2.w * lit, 0.0, 1.0); o = clamp(o, 0.0, 1.0); // Light over a see-through picture carries its own alpha: brighter than its alpha, a pixel is // no colour premultiplied, and the frost captured from this frame next (`rgb / a`) blew it up From bf66f57461aab0818ef4e388bf0cc38a056c8c2a Mon Sep 17 00:00:00 2001 From: foxnne Date: Wed, 14 Oct 2026 12:52:05 -0500 Subject: [PATCH 3/5] ci: DXC built from its whole history, so its DXIL is the same compiler's Co-Authored-By: Claude Opus 5.5 --- .github/workflows/shaders.yml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/.github/workflows/shaders.yml b/.github/workflows/shaders.yml index 6547cb8f5..f03f60275 100644 --- a/.github/workflows/shaders.yml +++ b/.github/workflows/shaders.yml @@ -46,7 +46,7 @@ jobs: uses: actions/cache@v4 with: path: ~/shadercross - key: shadercross-${{ env.SHADERCROSS_SHA }}-vendored-clang-1 + key: shadercross-${{ env.SHADERCROSS_SHA }}-vendored-clang-2 - name: Toolchain run: | @@ -70,8 +70,12 @@ jobs: run: | git clone --filter=blob:none "https://github.com/$SHADERCROSS_REPO" sc git -C sc checkout "$SHADERCROSS_SHA" - git -C sc submodule update --init --recursive --depth 1 --jobs 4 + # Whole histories, not `--depth 1`: DXC writes its version into every shader it compiles, + # counted from its git history, and a shallow clone counts 1 — every DXIL four bytes short + # and its hash different from the same compiler's elsewhere. + git -C sc submodule update --init --recursive --filter=blob:none --jobs 4 git -C sc/external/DirectXShaderCompiler rev-parse HEAD + git -C sc/external/DirectXShaderCompiler rev-list --count HEAD cmake -S sc -B sc/build -GNinja \ -DCMAKE_BUILD_TYPE=Release \ -DSDLSHADERCROSS_VENDORED=ON \ From 116da75cb63dd7aa9f174b59d214fcd47e66144b Mon Sep 17 00:00:00 2001 From: foxnne Date: Wed, 14 Oct 2026 12:56:48 -0500 Subject: [PATCH 4/5] ci: shadercross with its pinned prebuilt DXC, not one built from source Co-Authored-By: Claude Opus 5.5 --- .github/workflows/shaders.yml | 43 +++++++++++++++++++---------------- 1 file changed, 24 insertions(+), 19 deletions(-) diff --git a/.github/workflows/shaders.yml b/.github/workflows/shaders.yml index f03f60275..98c1a97be 100644 --- a/.github/workflows/shaders.yml +++ b/.github/workflows/shaders.yml @@ -7,11 +7,11 @@ # commit them. Before this, nothing noticed when the HLSL changed and the compiled files did not, # and Windows and Linux quietly drew the previous glass (#242, #373). # -# The toolchain is the one the committed files were made with: SDL_shadercross with the -# DirectXShaderCompiler fork it vendors (libsdl-org/DirectXShaderCompiler at 2c84a1c5), built -# with clang; built with GCC 13 that DXC corrupts its heap compiling DXIL. The shaders that have -# not changed since they were compiled (the backend's defaults) reproduce byte for byte, which is -# how a change of toolchain would show. The build takes a while, so it is cached by its pin. +# The toolchain: SDL_shadercross, pinned, with the DirectXShaderCompiler release it pins itself +# (Microsoft's prebuilt v1.9.2602, by SHA-256), as shadercross's own CI builds it. Nothing heavy is +# built here: SPIRV-Cross and shadercross, a few minutes, cached by the pin. What was committed +# before this came from a DXC built from source (libsdl-org's fork at 2c84a1c5): it was compiled +# again once with this one, and is checked against it from then on. name: Shaders @@ -31,7 +31,7 @@ on: env: SHADERCROSS_REPO: libsdl-org/SDL_shadercross - # Its `external/DirectXShaderCompiler` is libsdl-org/DirectXShaderCompiler at 2c84a1c5. + # Its build-scripts/download-prebuilt-DirectXShaderCompiler.cmake pins DXC v1.9.2602. SHADERCROSS_SHA: 1ff05bec573988a98ef9e0260b4da44f512b8367 jobs: @@ -46,12 +46,12 @@ jobs: uses: actions/cache@v4 with: path: ~/shadercross - key: shadercross-${{ env.SHADERCROSS_SHA }}-vendored-clang-2 + key: shadercross-${{ env.SHADERCROSS_SHA }}-prebuilt-dxc-1 - name: Toolchain run: | sudo apt-get update -y - sudo apt-get install -y clang ninja-build cmake libjson-perl spirv-tools + sudo apt-get install -y ninja-build cmake spirv-tools # Every run: shadercross links SDL, cached or not (setup-sdl caches its own build). - name: SDL @@ -62,32 +62,37 @@ jobs: cmake-generator: Ninja install-linux-dependencies: true - - name: Build shadercross (vendored DXC, clang) + - name: Build shadercross (prebuilt DXC) if: steps.cache.outputs.cache-hit != 'true' - env: - CC: clang - CXX: clang++ run: | git clone --filter=blob:none "https://github.com/$SHADERCROSS_REPO" sc git -C sc checkout "$SHADERCROSS_SHA" - # Whole histories, not `--depth 1`: DXC writes its version into every shader it compiles, - # counted from its git history, and a shallow clone counts 1 — every DXIL four bytes short - # and its hash different from the same compiler's elsewhere. - git -C sc submodule update --init --recursive --filter=blob:none --jobs 4 - git -C sc/external/DirectXShaderCompiler rev-parse HEAD - git -C sc/external/DirectXShaderCompiler rev-list --count HEAD + git -C sc submodule update --init --depth 1 external/SPIRV-Cross + # Microsoft's release of DXC, as shadercross pins it (version and SHA-256). + cmake -P sc/build-scripts/download-prebuilt-DirectXShaderCompiler.cmake + dxc_root="$PWD/sc/external/DirectXShaderCompiler-binaries" + cmake -S sc/external/SPIRV-Cross -B spirv_cross_build -GNinja \ + -DCMAKE_BUILD_TYPE=Release -DSPIRV_CROSS_SHARED=ON -DSPIRV_CROSS_STATIC=ON \ + -DCMAKE_INSTALL_PREFIX="$HOME/shadercross" + cmake --build spirv_cross_build + cmake --install spirv_cross_build cmake -S sc -B sc/build -GNinja \ -DCMAKE_BUILD_TYPE=Release \ - -DSDLSHADERCROSS_VENDORED=ON \ + -DSDLSHADERCROSS_VENDORED=OFF \ -DSDLSHADERCROSS_CLI=ON \ -DSDLSHADERCROSS_SHARED=ON \ -DSDLSHADERCROSS_STATIC=OFF \ -DSDLSHADERCROSS_WERROR=OFF \ -DSDLSHADERCROSS_INSTALL=ON \ -DSDLSHADERCROSS_INSTALL_RUNTIME=ON \ + -DDirectXShaderCompiler_ROOT="$dxc_root" \ + -Dspirv_cross_c_shared_ROOT="$HOME/shadercross:$HOME/shadercross/share/spirv_cross_c_shared" \ -DCMAKE_INSTALL_PREFIX="$HOME/shadercross" cmake --build sc/build --parallel cmake --install sc/build + # The compiler shadercross loads at run time, beside it. + find "$dxc_root" -name 'lib*.so*' -exec cp -L {} "$HOME/shadercross/lib/" \; + ls "$HOME/shadercross/lib" - name: Compile and compare run: | From 83ebd1645bd2d450ac8b62063bbcd6e02b23e6e1 Mon Sep 17 00:00:00 2001 From: foxnne Date: Wed, 14 Oct 2026 13:02:23 -0500 Subject: [PATCH 5/5] shaders: the glass SPIR-V and DXIL compiled from #373's HLSL Compiled by this workflow (shadercross 1ff05bec, DXC v1.9.2602), the toolchain the backend's default shaders reproduce with byte for byte. The DXIL's interface (signatures, pipeline state, flags) is unchanged. Co-Authored-By: Claude Opus 5.5 --- .github/workflows/shaders.yml | 6 +++--- .../compiled/dxil/liquid_glass.fragment.dxil | Bin 8444 -> 9072 bytes .../compiled/spv/liquid_glass.fragment.spv | Bin 13412 -> 15488 bytes 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/shaders.yml b/.github/workflows/shaders.yml index 98c1a97be..b6e740a3e 100644 --- a/.github/workflows/shaders.yml +++ b/.github/workflows/shaders.yml @@ -9,9 +9,9 @@ # # The toolchain: SDL_shadercross, pinned, with the DirectXShaderCompiler release it pins itself # (Microsoft's prebuilt v1.9.2602, by SHA-256), as shadercross's own CI builds it. Nothing heavy is -# built here: SPIRV-Cross and shadercross, a few minutes, cached by the pin. What was committed -# before this came from a DXC built from source (libsdl-org's fork at 2c84a1c5): it was compiled -# again once with this one, and is checked against it from then on. +# built here: SPIRV-Cross and shadercross, a few minutes, cached by the pin. The shaders that had +# not changed since they were compiled (the backend's defaults) reproduce with it byte for byte, +# SPIR-V and DXIL: it is the toolchain they were made with. name: Shaders diff --git a/core/gfx/shaders/compiled/dxil/liquid_glass.fragment.dxil b/core/gfx/shaders/compiled/dxil/liquid_glass.fragment.dxil index e8a82f9af2fac2f23dcd403ae2a1e93bab673e91..24e69dce5d757cbc2759632ffedf993c985066f3 100644 GIT binary patch delta 5924 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 diff --git a/core/gfx/shaders/compiled/spv/liquid_glass.fragment.spv b/core/gfx/shaders/compiled/spv/liquid_glass.fragment.spv index f9d667b32a85e7185a2275b93ff279ba27d1b113..4a36459a94f4db2d1388a82807b16918a1127b58 100644 GIT binary patch literal 15488 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 F{U5)H+_V4y literal 13412 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