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
51 changes: 20 additions & 31 deletions .github/workflows/shaders.yml
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# 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
# Each `X.hlsl` beside compiled forms of it (`compiled/{spv,dxil,msl,glsl}/X.*`) is compiled by
# `scripts/compile-shaders.sh` 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).
Expand All @@ -20,12 +20,14 @@ on:
paths:
- "**/*.hlsl"
- "**/shaders/compiled/**"
- "scripts/compile-shaders.sh"
- ".github/workflows/shaders.yml"
push:
branches: [main]
paths:
- "**/*.hlsl"
- "**/shaders/compiled/**"
- "scripts/compile-shaders.sh"
- ".github/workflows/shaders.yml"
workflow_dispatch:

Expand Down Expand Up @@ -99,36 +101,23 @@ jobs:
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
command -v spirv-cross
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')
bash scripts/compile-shaders.sh out || failed=1
stale=0
while IFS= read -r fresh; do
committed=${fresh#out/}
if cmp -s "$committed" "$fresh"; then
echo "ok $committed"
else
echo "::error file=$committed::$committed is not what its HLSL compiles to: download the compiled-shaders artifact and commit it"
echo " committed $(stat -c %s "$committed") bytes, compiled $(stat -c %s "$fresh")"
stale=1
fi
case $fresh in
*.spv) spirv-val "$fresh" || { echo "::error file=$committed::$fresh does not validate"; failed=1; } ;;
esac
done < <(find out -type f | sort)
echo "stale=$stale" >> "$GITHUB_ENV"
echo "failed=$failed" >> "$GITHUB_ENV"

Expand Down
28 changes: 12 additions & 16 deletions backend/src/shaders/compiled/msl/program_example.fragment.msl
Original file line number Diff line number Diff line change
Expand Up @@ -3,30 +3,26 @@

using namespace metal;

// `program_example.fragment.hlsl` in Metal, to the interface shadercross gives it: the entry
// point is `main0`, the inputs are the default vertex shader's `[[user(locnN)]]` outputs,
// textures and samplers are numbered from the draw's own (0), and the uniforms are buffer 0.

struct main0_in
struct type_Uniforms
{
float4 color [[user(locn0)]];
float2 uv [[user(locn1)]];
float4 data[2];
};

struct main0_out
{
float4 color [[color(0)]];
float4 out_var_SV_Target0 [[color(0)]];
};

fragment main0_out main0(
main0_in in [[stage_in]],
texture2d<float> Texture0 [[texture(0)]], sampler Sampler0 [[sampler(0)]],
texture2d<float> Texture1 [[texture(1)]], sampler Sampler1 [[sampler(1)]],
constant float4 *data [[buffer(0)]])
struct main0_in
{
float4 in_var_COLOR [[user(locn0)]];
float2 in_var_TEXCOORD0 [[user(locn1)]];
};

fragment main0_out main0(main0_in in [[stage_in]], constant type_Uniforms& Uniforms [[buffer(0)]], texture2d<float> Texture0 [[texture(0)]], texture2d<float> Texture1 [[texture(1)]], sampler Sampler0 [[sampler(0)]], sampler Sampler1 [[sampler(1)]])
{
main0_out out = {};
float4 base = Texture0.sample(Sampler0, in.uv) * in.color * data[0];
float4 extra = Texture1.sample(Sampler1, in.uv * data[1].x);
out.color = mix(base, extra, data[1].y);
out.out_var_SV_Target0 = mix((Texture0.sample(Sampler0, in.in_var_TEXCOORD0) * in.in_var_COLOR) * Uniforms.data[0], Texture1.sample(Sampler1, (in.in_var_TEXCOORD0 * Uniforms.data[1].x)), float4(Uniforms.data[1].y));
return out;
}

17 changes: 9 additions & 8 deletions core/gfx/LiquidField.zig
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
//! One layer of liquid glass: rounded boxes that run together where they come close, drawn as
//! frosted, refracting glass in one pass at every pixel (`shaders/liquid_glass.glsl`).
//! frosted, refracting glass in one pass at every pixel (`shaders/liquid_glass.fragment.hlsl`).
//!
//! **What it is.** Each shape is a rounded box — a circle is a square rounded all the way, a
//! capsule a rect rounded across its short side. Their outlines are joined by a smooth minimum
Expand Down Expand Up @@ -148,7 +148,7 @@ pub fn clusters(self: *const LiquidField) Clusters {

pub const uniform_vec4s = header_vec4s + shape_vec4s * max_shapes;

/// What the program reads, `uData` in `shaders/liquid_glass.glsl`.
/// What the program reads, `uData` in `shaders/liquid_glass.fragment.hlsl`.
pub const Uniforms = extern struct {
/// Where the frost starts, and 1 / its size: physical pixels to its uv.
frost_map: [4]f32,
Expand Down Expand Up @@ -231,17 +231,18 @@ const rim_line_width: f32 = 0.8;

// ── Drawing ─────────────────────────────────────────────────────────────────────────────────────

/// The program for the native backend on Vulkan and D3D12, compiled by shadercross from
/// `shaders/liquid_glass.fragment.hlsl` (the commands are at its top). Copied out of the embed to
/// be aligned: Vulkan takes SPIR-V as 32-bit words.
/// The program, written once in `shaders/liquid_glass.fragment.hlsl` and compiled from it for every
/// backend (`scripts/compile-shaders.sh`, checked in CI): SPIR-V and DXIL for the native backend on
/// Vulkan and D3D12, Metal for it on macOS, GLSL ES 3.00 for the web. The binary forms are copied
/// out of the embed to be aligned: Vulkan takes SPIR-V as 32-bit words.
const compiled = struct {
const spirv align(8) = @embedFile("shaders/compiled/spv/liquid_glass.fragment.spv").*;
const dxil align(8) = @embedFile("shaders/compiled/dxil/liquid_glass.fragment.dxil").*;
};

var program: programs.Program = .from(.{
.glsl = @embedFile("shaders/liquid_glass.glsl"),
.msl = @embedFile("shaders/liquid_glass.metal"),
.glsl = @embedFile("shaders/compiled/glsl/liquid_glass.fragment.glsl"),
.msl = @embedFile("shaders/compiled/msl/liquid_glass.fragment.msl"),
.spirv = &compiled.spirv,
.dxil = &compiled.dxil,
}, .{ .textures = 1, .uniform_vec4s = uniform_vec4s });
Expand Down Expand Up @@ -431,7 +432,7 @@ pub const Sample = struct {
light: f32,
};

/// `shaders/liquid_glass.glsl`'s field, line for line, over every shape: for hit tests and tests.
/// `shaders/liquid_glass.fragment.hlsl`'s field, line for line, over every shape: for hit tests and tests.
pub fn sample(self: *const LiquidField, p: dvui.Point.Physical) Sample {
const big = std.math.floatMax(f32);
var d1: f32 = big;
Expand Down
2 changes: 1 addition & 1 deletion core/gfx/glass_look.zig
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,7 @@ pub fn window(opacity: f32) Window {

// ── The app's glass ─────────────────────────────────────────────────────────────────────────────

/// The app's glass at the opacity, as the glass program (`shaders/liquid_glass.glsl`) reads it. Shaped
/// The app's glass at the opacity, as the glass program (`shaders/liquid_glass.fragment.hlsl`) reads it. Shaped
/// after Apple's lens, measured over a striped pattern: the backdrop pulled *inward* from a band
/// near the rim — `bevel` of the shape's shorter half, at most `bevel_cap` points — by up to `bend`
/// times that band, which magnifies it and, past 1, folds it into a mirrored, darker band just
Expand Down
5 changes: 5 additions & 0 deletions core/gfx/programs.zig
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,11 @@
//! `MAX_VEC4` for the length of its uniforms, `uniform vec4 uData[MAX_VEC4]`. Inputs are
//! `vColor` and `vTextureCoord`; samplers are `uSampler` (unit 0, the draw's own texture),
//! `uTex1` and `uTex2` (the extra textures `begin` binds).
//!
//! **Or generated.** A complete GLSL ES 3.00 source — one starting with its `#version` line — is
//! compiled as it is, on WebGL2 only. That is what `scripts/compile-shaders.sh` makes from a
//! program written once in HLSL, with the names above; fizzy's glass is one
//! (`shaders/liquid_glass.fragment.hlsl`).
const std = @import("std");
const dvui = @import("dvui");

Expand Down
Loading
Loading