Commit graph

2 commits

Author SHA1 Message Date
Erik
a85743f70d feat(render): Campaign V slice V2c - particle texture-index migration
Completes Campaign V slice V2 by moving both particle render paths -
billboard (particle.vert/.frag) and mesh-emitter (particle_mesh.vert/.frag) -
from raw 64-bit ARB_bindless_texture handles to binding=9 handle-table
indices, matching V2a's mesh path and V2b's terrain path.

Particles differ from both prior sub-slices in HOW the handle reaches the
shader:

- Billboard particles carry it as a per-INSTANCE vertex attribute (not an
  SSBO batch or a per-draw uniform), because each particle can use a
  different texture within one instanced draw. aTextureHandle (location 6,
  uvec2) became aTextureIndex (uint); particle.vert looks it up via
  ACDREAM_TEXTURE_HANDLE and reconstructs the SAME uvec2 into vTextureHandle
  exactly as before, so particle.frag - including its zero-handle check for
  the procedural circle fallback - needed no change at all. The per-instance
  ABI struct BillboardGpuInstance shrank by 4 bytes (one uint slot instead of
  two uint handle halves); ParticleBindlessInstanceTests updated for the new
  68-byte layout and the vertex attribute declaration text.

- Mesh-emitter particles carry it as a per-draw uniform (uTextureHandle,
  uvec2) exactly like terrain's pattern from V2b: one texture per draw call,
  set right before it. Became uTextureIndex (uint) + the same
  ACDREAM_TEXTURE_HANDLE lookup.

ParticleRenderer owns its own GlBindlessHandleTable and binding=9 SSBO,
independent of the other three renderers' tables, created eagerly in the
constructor alongside the other GL resources it already creates there. Unlike
the other three renderers, flushing/binding the table happens immediately
before EVERY individual draw call (four call sites: immediate billboard,
immediate mesh, and both halves of the deferred/prepared RetailAlphaQueue
path) rather than once per pipeline-state switch - a run of consecutive
mesh-particle sub-batches can register a new handle partway through (each
sub-batch has its own texture), and the table must be current for each one,
not just the first.

TextureCache's particle-texture cache (AcquireParticleTexture,
StandaloneBindlessTextureCache) needed no change: it only ever hands back a
raw ulong handle, and both ParticleGfxInfo.TextureHandle and
ParticleInstance.TextureHandle keep carrying that raw value - the table
lookup is added exactly where each path already converts its handle into
GPU-visible state (WriteBillboardGpuInstance and the two ProgramUniform
call sites).

Coverage caveat (flagged per the campaign doc's slice table): the offline
pixel gate's fixed outdoor view has no particles in frame, so it does not
exercise this slice - it only confirms nothing else regressed. This change
is correspondingly kept strictly mechanical (indirection only, no logic
change), but it still needs a user visual check with live particle emitters
before being trusted as pixel-identical.

Gate: dotnet build -c Release green, dotnet test tests/AcDream.App.Tests
-c Release green (3843 passed / 3 skipped on a clean run - one unrelated
pre-existing flaky allocation test, UiDatFontTests, failed once and passed
on immediate re-run in isolation and in the full suite, confirmed unrelated
to this change), and tools/run-offline-pixel-gate.ps1 passed against the
V2b commit's build with a 4.62e-05 differing-pixel fraction (a tripwire
only, per the coverage caveat above). No divergence-register row: this
introduces no retail behavior deviation.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-27 16:02:59 +02:00
Erik
2cbf34a668 perf(rendering): preserve alpha order with bindless particles
Carry each billboard particle's resident texture handle in the instance ABI so stable retail alpha ordering can batch mixed textures without rebinding and redrawing each short texture run. Keep DAT blend transitions as the only billboard draw boundary.

The connected 0xC95B stress scene improved from 6 FPS / 171 ms to about 153 FPS / 6.6 ms at the same roughly 21,000 visible entities. Release build succeeds and all 5,916 tests pass with five intentional skips.

Co-authored-by: OpenAI Codex <codex@openai.com>
2026-07-18 08:32:21 +02:00