Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
144 changes: 144 additions & 0 deletions .github/workflows/shaders.yml
Original file line number Diff line number Diff line change
@@ -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
Binary file modified core/gfx/shaders/compiled/dxil/liquid_glass.fragment.dxil
Binary file not shown.
Binary file modified core/gfx/shaders/compiled/spv/liquid_glass.fragment.spv
Binary file not shown.
2 changes: 1 addition & 1 deletion core/gfx/shaders/liquid_glass.fragment.hlsl
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Loading