- Macrostep 00: Model contract and retained historical options. - Macrostep 01: Project foundation and configuration, including schema, presets, CLI, and CI setup. - Macrostep 02: Mathematical core implementation and deterministic oracles. - Macrostep 03: CPU-based planar simulation engine, FFT backend, and headless runner. Provides exhaustive objectives, phase breakdowns, validation policies, and deliverables for each macrostep.
7.0 KiB
Macrostep 05 — GPU resources and 2-D FFT proof
Objective
Build a narrow, safe GPU layer under the raylib context and prove both selectable FFT algorithms against CPU oracles: the standard default and retained LegacyPackedUnitary. Do not implement the complete simulator until resource safety, stage fidelity, and numerical parity are demonstrated.
Dependencies
Macrosteps 00–04 complete.
Phase 5.1 — Capability and backend policy
Substep 5.1.1 — Capability report
Probe and record:
- OpenGL and GLSL versions;
- maximum 2-D texture size, texture units, and draw buffers;
R32FandRG32Ftexture/render-target support;- framebuffer completeness;
- timer-query availability;
- estimated resource budget.
Backend::Auto with FftAlgorithm::Standard falls back to CPU with a visible explanation. LegacyPackedUnitary is an explicit GPU-only request; unsupported capabilities or shapes fail clearly and never silently substitute the standard algorithm.
Substep 5.1.2 — Portability target
Primary path: desktop OpenGL 3.3 core-compatible GLSL. Do not copy legacy compatibility constructs (gl_TexCoord, ftransform, gl_FragColor, implicit shader versions). GLES/Web is a later secondary target.
Phase 5.2 — Isolated resource layer
Substep 5.2.1 — Unsafe boundary
Place all raw OpenGL/rlgl operations in src/gpu/resource/. Public wrappers are RAII-managed, non-cloneable owners of:
- texture IDs and format/extent metadata;
- framebuffers and attachment ownership;
- shader programs and uniform locations;
- fullscreen pass geometry;
- optional timer queries.
Every creation checks errors/completeness. Every custom pass flushes/restores raylib state according to a documented protocol.
Substep 5.2.2 — Ping-pong discipline
Make sampling a current render target impossible through the safe pass API. A pass declares read textures and a distinct write target; debug mode asserts no alias. This prevents legacy feedback-loop defects across all later variants.
Substep 5.2.3 — Shader assets
Embed release shaders and fail shader compilation/link with source name plus complete log. Support explicit development overrides only. Add shader preprocessing for shared curve/palette code if it produces deterministic, debuggable output.
Phase 5.3 — GPU data layouts
Use 2-D textures as the portable baseline:
- scalar fields:
R32F; - standard full-complex fields:
RG32Fat logical extent; - legacy packed fields:
RG32Fat the historical half-x packed extent; - 1-D later: one row;
- 2-D: direct layout;
- 3-D later: tiled z-slice atlas.
Define exact texel-center and logical-to-physical mappings. Compute shaders and native 3-D render targets may be optional optimizations, never the only correctness path.
Phase 5.4 — Standard 2-D FFT implementation
Substep 5.4.1 — Standard algorithm spike
Select Stockham autosort or a conventional staged radix-2 butterfly after comparing pass count, addressing clarity, and driver portability. Use integer texelFetch; never rely on filtering or normalized-coordinate rounding.
Substep 5.4.2 — Transform passes
Implement:
- real-to-complex copy;
- x and y forward stages;
- x and y inverse stages;
- exactly one
1/(Nx·Ny)inverse scale; - round-trip readback harness.
Cache twiddles/plans by shape. Require power-of-two GPU extents initially and report this distinction from the more flexible CPU backend.
Substep 5.4.3 — Spectral convolution
Upload or generate kernel spectra, multiply state spectrum by disk/ring spectra, inverse transform, and normalize by sampled sums consistent with CPU convention. Reuse the state forward FFT for both kernels.
Phase 5.5 — Legacy packed-unitary 2-D FFT
Substep 5.5.1 — CPU stage oracle
Implement a small, clear CPU oracle for adjacent-real packing, packed-spectrum conversion, historical plan entries, unitary butterflies, and inverse unpacking. It exists for fixtures and diagnosis, not as a production CPU backend.
Substep 5.5.2 — Modern GPU port of the historical algorithm
Retain the algorithmic behavior while rewriting its infrastructure:
- adjacent x samples packed into real/imaginary components;
- historical half-x complex layout and conjugate/Nyquist handling;
- bit-reversal and twiddle plan stages for x/y;
- forward/inverse unitary scaling at each stage;
- ping-pong FFT scratch textures;
- convolution multiplier
sqrt(Nx·Ny)/kernel_sum.
Use GLSL 330, integer texel addressing, embedded shaders, checked resources, and the common safe pass API. Do not copy compatibility-profile GLSL or resource hazards.
Substep 5.5.3 — Algorithm-specific constraints
Require power-of-two extents and enough texture/FBO resources. Keep standard and legacy plans, spectra, and scratch caches separate. Changing FftAlgorithm is a cold transactional rebuild and invalidates integration history.
Phase 5.6 — Numerical proof harness
For each algorithm and test field, read back explicit stages:
- input complex copy;
- forward spectrum;
- inverse round trip;
- disk/ring spectral products;
MandN.
Compare to CPU with max absolute, mean absolute, and L2 error. Include zero, constant, impulse, checkerboard, random seeded, and asymmetric patterns at multiple small powers of two.
Initial targets:
- standard round-trip max error
≤1e-4; - standard convolution max error
≤2e-4on agreed fixtures; - legacy packed stage outputs match the CPU stage fixtures within documented
f32tolerances; - legacy final convolution agrees with direct/standard CPU convolution within
2e-4on agreed fixtures; - constant-field density error is small enough to preserve the documented tolerance.
Tolerances must be measured and recorded per GPU/driver class before broadening; never hide systematic normalization errors behind a loose bound.
Phase 5.7 — Lifecycle and failure testing
- Recreate FFT resources repeatedly across sizes.
- Simulate allocation/shader failures and preserve the CPU backend.
- Check for GL errors and incomplete FBOs in debug validation mode.
- Verify no resource growth after repeated switches.
- Confirm no synchronous readback in the normal frame path.
- Preflight memory estimates before allocation and retain the prior valid backend on failure.
Deliverables
- GPU capability report and automatic fallback policy.
- Auditable unsafe/RAII resource layer.
- Validated standard and legacy packed-unitary 2-D GPU FFT/convolution proofs.
- Legacy packing/plan/unitary CPU stage oracle.
- Per-algorithm stage-comparison harness and initial GPU benchmark metadata.
Exit gate
- Standard CPU/GPU round trip and convolution tolerances pass on the reference environment.
- Legacy packing, plans, scaling, round trip, spectra, and convolution pass their stage/final tolerances.
- Resource alias checks prevent read/write feedback.
- Shader and allocation failures are actionable and non-destructive.
- Repeated recreation shows no incomplete framebuffer or resource leak.
- GPU simulation work does not begin until the 2-D proof is stable.