diff --git a/.github/workflows/shaders.yml b/.github/workflows/shaders.yml new file mode 100644 index 000000000..b6e740a3e --- /dev/null +++ b/.github/workflows/shaders.yml @@ -0,0 +1,144 @@ +# 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: 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. 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 + +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 build-scripts/download-prebuilt-DirectXShaderCompiler.cmake pins DXC v1.9.2602. + 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 }}-prebuilt-dxc-1 + + - name: Toolchain + run: | + sudo apt-get update -y + 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 + id: sdl + uses: libsdl-org/setup-sdl@main + with: + version: 3-latest + cmake-generator: Ninja + install-linux-dependencies: true + + - name: Build shadercross (prebuilt DXC) + if: steps.cache.outputs.cache-hit != 'true' + run: | + git clone --filter=blob:none "https://github.com/$SHADERCROSS_REPO" sc + git -C sc checkout "$SHADERCROSS_SHA" + 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=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: | + 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 + failed=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")" + # 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' || env.failed == '1' + uses: actions/upload-artifact@v4 + with: + name: compiled-shaders + path: out/ + + - name: Fail on stale or broken compiled shaders + if: env.stale == '1' || env.failed == '1' + run: exit 1 diff --git a/core/gfx/shaders/compiled/dxil/liquid_glass.fragment.dxil b/core/gfx/shaders/compiled/dxil/liquid_glass.fragment.dxil index e8a82f9af..24e69dce5 100644 Binary files a/core/gfx/shaders/compiled/dxil/liquid_glass.fragment.dxil and b/core/gfx/shaders/compiled/dxil/liquid_glass.fragment.dxil differ diff --git a/core/gfx/shaders/compiled/spv/liquid_glass.fragment.spv b/core/gfx/shaders/compiled/spv/liquid_glass.fragment.spv index f9d667b32..4a36459a9 100644 Binary files a/core/gfx/shaders/compiled/spv/liquid_glass.fragment.spv and b/core/gfx/shaders/compiled/spv/liquid_glass.fragment.spv differ 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