38 Commits
Author SHA1 Message Date
PzmuV1517 623aa1d3ea Shader packs: a texel offset given as a parameter moves the coordinate
GLSL asks a texel offset to be a constant expression; GL drivers also take one a
function receives as a `const in ivec2` parameter (Arc's FXAA samples its neighbours
that way, so its final pass was skipped in every dimension). Such an offset now moves
the coordinate by that many texels of the base level; a constant one stays an offset.
2026-11-06 01:52:10 +02:00
PzmuV1517 1da773a501 Shader packs: a matrix built from one vector takes its components column by column
`mat2(v)` with a vec4 `v` is the matrix with columns v.xy and v.zw in GLSL, as when the
components come as several arguments. naga's GLSL front end made each column the whole
vector, which failed validation ("Composing 0's component type is not expected"):
Bloop's water normals (prepare1) were skipped.
2026-11-06 01:52:10 +02:00
PzmuV1517 19f87e8059 Shader packs: an integer vertex output travels flat without saying so
GLSL asks a fragment input of integer type to be `flat`, but since 4.30 only the
fragment stage's interpolation qualifier counts: a vertex stage may write
`out uint frag_worldTN;` and GL drivers link it to `flat in uint`. naga left such an
output without interpolation and refused it (Alpha Piscium: 111 programs). An integer
vertex output or fragment input with no qualifier is now flat.
2026-11-06 01:52:10 +02:00
PzmuV1517 ee50f8ae1a Shader packs: empty declarations, precision on parameters, infinities in functions
Three things GL drivers take that naga's GLSL front end refused:
- an empty declaration (`;`) at global scope in any version, as a macro that writes a
  function leaves after it (DrDestens: 56 programs skipped);
- a precision qualifier on a function parameter (`float rand(highp vec2 p)`,
  TrailerShaders: final and composite);
- a constant expression in a function whose value is infinite or NaN
  (`1.0 / toeX` with toeX 0, Epoch's tonemap at its defaults): it is computed at run
  time, as GL does, instead of folded into a literal that can't hold it. WGSL keeps
  refusing it.
2026-11-06 01:52:10 +02:00
PzmuV1517 cb65d4f80d Shader packs: a global constant reads an element of a constant array
`const vec4 s = vec4(scale[0].x, ...)` over a constant array read the index and the
array from the function's arena while folding in the module's: naga panicked (index out
of bounds) and Ebin Resurrected failed to load. Both now come from the global arena.
2026-11-06 01:52:10 +02:00
Ludovic Marie 220717bd56 Merge pull request #9104 from arslanarm/shaderpacks/atomic-member-reads
🤖 Agent: This changes the vendored naga GLSL frontend: every non-constant struct member read now goes through a pointer, not a loaded copy of the struct. Every pack's GLSL takes that path, so a naga update must keep the patch (THIRD_PARTY_NOTICES records it). Constant expressions keep the old form. Nothing else found.
2026-11-04 16:25:32 +01:00
Ludovic Marie e221e9d786 Merge remote-tracking branch 'origin/main' into cg28/9005
# Conflicts:
#	crates/horizon-client-render/src/gfx/pack_post.rs
2026-11-04 13:00:50 +01:00
Arslan ArmanulyandClaude Opus 5.5 b873ea2dd6 Shader packs: read struct and buffer-block members that atomic functions use
naga's GLSL frontend read `s.member` on the right-hand side out of a loaded
copy of the whole struct. Once an atomic function marks that member atomic,
the copy's member is an atomic value no operator, conversion, call or store
takes ("Incompatible operands: Atomic(...) as Uint"). The member is now
loaded through a pointer to it, as array elements already were; constant
expressions keep the value form.

Tests: every read the #9081 probe makes (assignment, operators,
constructors, min, subgroupBroadcastFirst) on a shared struct member, a
buffer-block member, a member of a shared struct array and a nested member;
constants' members in constant expressions; a member beside a runtime-sized
array; and, on a host GPU, the values those reads return.

Fixes #9081

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-04 15:44:19 +05:00
Arslan ArmanulyandClaude Opus 5.5 ab7164bba1 Shader packs: a const local may be a texel offset
A `const` local whose initializer is a constant expression is a
constant expression itself (GLSL 4.60, 4.3.3, as #8462 quotes it).
naga's GLSL frontend kept it as a local variable, so a texel offset
given by one failed with "Sample offset must be a const-expression". It
now becomes a module constant, as a global `const` does, also inside a
loop.

Fixes #8462

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-04 10:57:52 +05:00
Arslan ArmanulyandClaude Opus 5.5 72ca9db476 Shader packs: .length() of a sized buffer member as an array size
`.length()` of an explicitly sized array is a constant expression
whether or not the array is (GLSL 4.60, 4.3.3, as #8463 quotes it). In
a constant context (an array size) naga's GLSL frontend lowered the
array itself there and failed with "Unexpected runtime-expression"; the
array is now lowered outside it, for its type only.

Fixes #8463

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-04 10:52:03 +05:00
gigamans442andCodex GPT-6.1 Sol 0f983e34d1 fix(shaders): reset GLSL type for each declarator
Keep one array declarator from changing the base type of following
comma-separated variables.

Co-Authored-By: Codex GPT-6.1 Sol <[email protected]>
2026-11-02 19:17:48 -05:00
Ludovic MarieandClaude Opus 5.5 3380705af9 Game: start on CPUs without POPCNT again (Core 2), and check every build for it
Since #4160 (10-29) the game's allocator is mimalloc v3, from libmimalloc-sys
0.1.49. Built by MSVC, its mi_popcount is `__popcnt64`, which MSVC turns into
the POPCNT instruction whatever the target. mimalloc runs before main, so on a
Core 2 (no POPCNT) the Windows game died at once with 0xc000001d and no log
(#7264). v0.2.97 had no POPCNT at all; nightlies 2026-10-31 and 11-01 have 9.

third_party/libmimalloc-sys is the published crate with one change: on MSVC
x86/x64, mi_popcount takes mimalloc's portable count, as the GCC builds without
-mpopcnt already do. Only arena bitmaps count bits, never malloc's fast path.
[patch.crates-io] points at it until a release carries microsoft/mimalloc#1291.

tools/check_cpu_baseline.py disassembles the Windows and Linux programs in the
game build and fails on any POPCNT or LZCNT: it flags 2026-11-01's
horizon-client.exe and passes v0.2.97's and the Linux builds.

Fixes #7264

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-02 15:43:19 +01:00
Ludovic MarieandClaude Opus 5.5 e478b7e210 third_party: libmimalloc-sys 0.1.49 as published, mimalloc v3 sources only
Verbatim copy of the crates.io package (checksum 6a45a52f…b8e9 in Cargo.lock),
without c_src/mimalloc/v2 and v3's CMake, contrib and CI files, which the build
never reads. The next commit applies the fix and wires it in.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-02 15:34:56 +01:00
Ludovic MarieandClaude Opus 5.5 8bba5bd368 DX12: keep compiled shaders on disk between runs
FXC compiles Horizon's pipelines in about 2.6 s on every start (DXC
about 1 s). wgpu-hal's shader cache only lasts one device, so the
vendored dx12 backend now also appends what it compiled to
<game home>/cache/dx12-shaders.bin and reads it back as precompiled
bytecode next time. Keys hash the compiler, shader model, stage, entry
point and naga's HLSL; records carry a checksum and a torn tail is
written over.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-02 13:45:23 +01:00
Ludovic Marie 0d22dea027 Merge the stack below (main merged) into #4453 2026-10-31 20:11:07 +01:00
Ludovic Marie 2d6e8182a1 Merge the stack below (main merged) into #4452 2026-10-31 20:11:06 +01:00
Ludovic Marie 008ea17e50 Merge pull request #4446 from PzmuV1517/shader-packs/06
🤖 Agent: merged #4445's new head (86e0d83db, main merged) up the stack; #4790 also merges current main (lib.rs: shader_options beside main's split_profiles); top of stack builds clean (client lib+bin).
2026-10-31 19:45:07 +01:00
Ludovic Marie 723ecbaa7f Merge branch 'a3-4443' into a3-4444 2026-10-31 18:56:26 +01:00
Ludovic Marie 6173f174b1 Merge remote-tracking branch 'origin/main' into a3-4442 2026-10-31 18:56:01 +01:00
Ludovic Marie 5e0dba5db4 Merge pull request #4441 from PzmuV1517/shader-packs/01
🤖 Agent: shader packs 1/12: multi-line macro calls, struct varyings, property continuations; naga 30.0.1 vendored unchanged (MIT/Apache, notices added) with fixes on top; review points fixed. CI green.
2026-10-31 18:49:35 +01:00
Ludovic Marie 703a0a75f4 Merge pull request #4220 from elythepreciouscat/perf/opengl-indirect-multidraw
🤖 Agent: GL backend: indirect execution without compute shaders, batched indirect draws with the same strides/offsets; rdbtCVS checked fmt/clippy on third_party/wgpu-hal by hand and approved. CI green.
2026-10-31 18:49:31 +01:00
PzmuV1517 af942d8ae1 Shader packs: read storage buffers and custom images
- bufferObject.<n>: a storage buffer of n bytes (or bytes per pixel of the render size). Buffer blocks are bound in set 4 and lose their access qualifiers so that every stage binds them alike.
- image.<name>: 2D and 3D images of the pack's, with their format, size (or the render size scaled), sampler name and whether they clear before each frame.
- iris.features: SSBO and CUSTOM_IMAGES are supported.
- A 3D image (iimage3D) is declared as 3D.
- naga: atomicAdd and the other atomic functions on buffers and shared memory, imageAtomic*.

Nothing binds them yet.
2026-10-31 15:54:01 +02:00
PzmuV1517 e944268c3e Shader packs: translate compute programs, read size.buffer, options in shaders.properties
- Compute programs (<name>.csh, then _a to _z) are translated and carried before their composite-style program: images (colorimgN in its own format), workGroups and workGroupsRender, shared memory, barriers and subgroups. Compute-only programs are noted as not supported yet. Nothing dispatches them yet.
- size.buffer.<buffer>: a size of its own, relative to the render size (floats) or in pixels (integers).
- shaders.properties and the program switches see the pack's options at their defaults (switches on, value options): Photon sizes its cloud buffers only under #if CLOUDS_TEMPORAL_UPSCALING == 4.
- naga: subgroup operations, memory barriers, and barrier() as a work group barrier.
2026-10-31 15:54:01 +02:00
PzmuV1517 85382019cf Shader packs: struct fields and array elements passed as out arguments no longer crash Vulkan
naga's SPIR-V writer panicked on Photon's deferred4 ("Expression is not cached"): the GLSL frontend passed a pointer into the middle of a variable for f(s.field), which only Metal's writer takes. The naga pin moves to a fix that spills such arguments through a local and writes them back after the call, as GLSL's copy-out says.

real_packs with BACKENDS=1 hands every translated module to the SPIR-V, HLSL, GLSL and MSL writers with wgpu's settings, so this is caught on any machine.
2026-10-31 15:54:01 +02:00
PzmuV1517 3a0de65fa4 Shader packs: IS_IRIS and Iris feature flags, sized array types in global initializers
- IS_IRIS is defined: packs take Iris's extensions (compute programs, images, custom textures) only where it is, and Rethinking Voxels draws an error screen without it. iris.features.optional defines IRIS_FEATURE_<name> for the features Horizon has; a required one it lacks is noted.
- A global initialized with the size on its type (ivec3[6] offsets = ivec3[](...)) kept only the size as its type.
- naga: constant shifts drop the bits shifted out, as GLSL says (1 << 31).
2026-10-31 15:54:01 +02:00
PzmuV1517 0bfcf5f954 Shader packs: shadow.culling, voxelDistance and geometry stage writes once per triangle
- shadow.culling=false draws every section within the shadow distance and every face of it; reversed does so within voxelDistance (default 160) and culls beyond as before. Horizon drew only the faces turned to the light, and a pack voxelizing the world in its shadow pass lost the others.
- A geometry stage's image and buffer writes run in one of the invocations that stand for its triangle; in an expression the others read the value instead. The instances drawn follow from the strips EndPrimitive ends.
- at_midBlock for flat terrain: the offset to the centre of the face's block, in 64ths.
- firstPersonCamera.
- naga: imageAtomic* give the texel's value before the operation.
2026-10-31 15:54:01 +02:00
PzmuV1517 a59d378bf4 Shader packs: struct varyings, Vulkan type names as identifiers, naga's GLSL gaps
Photon translated 45 of its 88 composite-style programs and none of its overworld terrain, sky set-up or final pass; now all 88 translate and every dimension loads its full set.

- A struct passed between stages (flat out CloudsParameters p) keeps the pack's declaration as a private global and travels as its members packed into vectors, copied by our main.
- sampler, texture2D and Vulkan GLSL's other texture and sampler type names are free names in desktop GLSL (sampler2D sampler); they are renamed unless called.
- naga's GLSL frontend lacked textureGather, sampled implicit levels in vertex shaders, broke struct constructors with array members, vecN(bool) and vector % scalar: naga is patched with the fixes until a release has them.
- real_packs prints every loader note with SHOW_ERRORS.
2026-10-31 15:53:50 +02:00
PzmuV1517 914e5d0103 Vendor naga 30.0.1 unchanged
Shader packs need fixes to naga's GLSL frontend that no release has yet. They go on top of this
copy in the commits that need them, as wgpu-hal's fix did, instead of a git dependency.
2026-10-31 15:53:50 +02:00
PzmuV1517 d085cabe41 Shader packs: outputs narrower than vec4, inverse(), alphaTestRef and the cutout test
- A fragment output declared float, vec2 or vec3 (int and uint too) is written as the vec4 of its kind, alpha 1: wgpu's targets take four components.
- inverse() of a mat2, mat3 or mat4 is a function of ours (the adjugate over the determinant): naga writes it neither for Metal nor for WGSL.
- alphaTestRef is a constant of the program: its alpha test's reference.
- Solid and cutout terrain draw together, so terrain gets the cutout test (alpha > 0.1) unless alphaTest.* says otherwise.
- The naga pin moves to the fixes for pointers into vectors passed as out arguments and field selection on a matrix column.
2026-10-31 15:53:50 +02:00
rdbt b2f0b58fc9 Merge pull request #4047 from elythepreciouscat/fix/fps-cap-vsync-off
Linux: OpenGL presents uncapped with VSync off
2026-10-31 05:15:01 +00:00
ElyTheCatandAntigravity 41da822ff8 fix(gl): restore clippy allow on UniformLocation and clarify multi-draw requirements
- Re-add clippy::clone_on_copy allow for UniformLocation in command.rs.
- Format indirect_execution expression in adapter.rs to satisfy rustfmt.
- Note in gfx.rs comment that multi_draw requires indirect first instance
  support (OpenGL 4.2 + GL_ARB_shader_draw_parameters) in addition to 4.0 indirect draws.

Co-Authored-By: Antigravity <[email protected]>
2026-10-31 02:00:38 +03:30
ElyTheCatandAntigravity 09ecc06126 fix(gl): drop premature Immediate startup default and clarify EGL swap intervals
- Revert startup present_mode default in gfx.rs to preserve Mailbox -> Fifo
  ordering for Vulkan and DX12 before settings load.
- Add comments in egl.rs explaining the configure-time vs present-time swap
  interval fallback handling.
- Note regression guard purpose for the GLES [Fifo, Immediate] present mode test.

Co-Authored-By: Antigravity <[email protected]>
2026-10-31 01:41:18 +03:30
k3yhandClaude Opus 5.5 da4ded1d7c Windows Vulkan: wait for the acquire fence only with VSync (gfx-rs/wgpu#10714)
wgpu-hal 30 waits on Windows for a fence that vkAcquireNextImageKHR
signals, in every present mode. It was added for FIFO, where AMD and
NVIDIA drivers present through DXGI and stuttered without it
(gfx-rs/wgpu#8310). With VSync off (Immediate, Mailbox) the driver
signals that fence only once the GPU has finished the previous frame, so
get_current_texture held the CPU for the whole GPU frame and the GPU sat
idle while the next one was recorded: 72% busy with a shader pack on an
RX 7900 XT. Apply the upstream fix (gfx-rs/wgpu#10714, not in a release
yet), which creates the fence only for Fifo and FifoRelaxed.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-30 14:19:27 -05:00
ElyTheCatandAntigravity 5a2bfc3d24 OpenGL 4.2: Enable indirect execution and simulated multi-draw batching
Basic indirect drawing (glDrawArraysIndirect, glDrawElementsIndirect) is
standard in Desktop OpenGL 4.0+ and GLES 3.1+.

1. In wgpu-hal GLES adapter: decouple DownlevelFlags::INDIRECT_EXECUTION
   from compute shaders, enabling indirect execution on Desktop GL 4.0+
   (and GLES 3.1+), which unlocks multi_draw in horizon-client-render on
   OpenGL 4.2 hardware (e.g. Intel HD Graphics 4000).
2. In wgpu-hal GLES command and queue: avoid pushing draw_count separate
   Command variants into the command buffer. Push a single DrawIndirect /
   DrawIndexedIndirect command with draw_count. In queue execution, bind
   DRAW_INDIRECT_BUFFER once and loop glDraw*Indirect across offsets on the
   CPU, eliminating command buffer allocation churn and repeated buffer
   re-bindings.

Co-Authored-By: Antigravity <[email protected]>
2026-10-30 10:21:03 +03:30
ElyTheCatandAntigravity 05f68f3fdc Linux: OpenGL presents uncapped with VSync off
On Linux with OpenGL (the GLES wgpu backend), wgpu-hal hard-coded its
surface capabilities present_modes to [Fifo] only (//TODO), never
offering Immediate. When VSync was turned off and Max Framerate was set
to Unlimited:
1. chosen_present_mode filtered supported modes and could only select
   Fifo since Immediate was missing from the adapter's capabilities.
2. In gfx.rs, pre_present_notify is called whenever present_mode is Fifo,
   which on Wayland requests a wl_surface.frame callback from the compositor,
   pacing RedrawRequested to the display refresh rate (60 FPS).
3. In egl.rs, eglSwapInterval(0) was never called, leaving EGL to default
   to swap interval 1 (60 FPS on X11 and Wayland).

- In wgpu-hal: report [Fifo, Immediate] present modes for GLES on all
  platforms, and call eglSwapInterval with 0 for Immediate and 1 for Fifo
  in configure and present.
- In gfx.rs: consider Immediate on startup surface configuration if
  supported, matching vsync_present_mode.
- In window.rs: add unit test for present mode choice with [Fifo, Immediate].

Co-Authored-By: Antigravity <[email protected]>
2026-10-30 02:30:12 +03:30
Kacpherek b8f0cde900 Fix the Metal HDR colour space lookup that crashed macOS at start
wgpu-hal's Metal backend read the CoreGraphics colour space names (kCGColorSpaceITUR_2100_PQ and others) as if dlsym returned the string, but it returns the address of the global that holds it. Configuring a PQ swapchain passed that address to CGColorSpaceCreateWithName, which faulted with no Rust panic. Read through the pointer.
2026-10-27 21:59:16 +01:00
Ludovic MarieandClaude Opus 5.5 4992d877c7 Fix the Intel HD 520/530 crash at device creation (#746)
wgpu 30 creates a Vulkan descriptor pool with no pool sizes while it creates
the device. Vulkan allows that only since 1.3.215; Intel's Windows driver for
6th-10th gen integrated GPUs (igvk64.dll, HD 520/530, UHD 620/630) reads
past the empty array and crashes with 0xc0000005 inside request_device, right
after the game logs its renderer (gfx-rs/wgpu#9845). Apply the upstream fix
(gfx-rs/wgpu#10124: one sampler entry when the pool would be empty), which no
wgpu release has yet, through [patch.crates-io].

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-19 07:22:12 +02:00
Ludovic MarieandClaude Opus 5.5 79f78f904b Vendor wgpu-hal 30.0.1 unchanged (examples left out)
The crates.io sources of wgpu-hal 30.0.1, minus its examples, lock file and
VCS metadata, so the next commit can apply one upstream fix that has no
release yet. Excluded from the workspace: it keeps its own lints.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-19 07:22:12 +02:00