Praxis is a 3D engine in Rust for large open-world games. Each guide teaches the ideas behind one part of it through the tickets and pull requests that built that part, with live demos. Each is dated: it names the commit it describes and says what has changed on main since.
Start with Instance Buffer Explained. The path it follows, from the ECS World through prepare and upload to the GPU, still holds, but its map of the frame is from 2026-10-19, when one render pass issued a draw call per Primitive of each Renderable; the frame on main, after the guides, lists its passes now. Each card names its milestone, tickets and pull requests.
Follows one frame from the ECS World to the draw call as PR #220 left it, then shows how each Renderable keeps a slot in an Instance buffer fixed at 524,288 slots. It covers what happens past that capacity, per-frame staging, GPU memory accounting, measuring performance, and tests that can fail.
ReadStarts from texels and mips, explains anisotropic filtering, and shows why distant normal maps need Toksvig. It walks through the minification and texture-wall Reference scenes and what it costs to keep 10,000 textures bound.
ReadCovers light units, exposure, fp16 render targets and tonemapping operators, which you can try in live demos. It also explains the MSAA resolve, dither, colour spaces and HDR displays, and sets out the seven decisions PR #219 put to the owner, all taken as recommended and carried out by #200.
ReadShows how rays find triangles through a tree of boxes, then the two levels Praxis builds: a compacted BLAS per Mesh and a static TLAS anchored at load. It covers the memory the engine estimates, when the static TLAS rebuilds and why it holds 2^18 entries, and the shadow rays, dynamic TLAS, town and crowd that have built on the two structures since, with live demos.
ReadShows how Praxis reduces its 4x MSAA depth into a chain of ever coarser levels every frame, each texel the farthest depth over its share of the screen. It covers why reverse-Z keeps the minimum, how odd sizes share the screen, what reading every sample costs on the M4 Pro, and how phase two of the culling pass bounds any box with four reads, with live demos.
ReadFollows the culling pass as it builds the frame's one indirect draw in slot order, tests each Instance against the view frustum, and culls in two phases against the depth pyramid. It covers the Render switch that turns culling off, the checks that culling never changes the image, and what each step costs, with live demos.
ReadBuilds the lit pass's shading up one term at a time: what a BRDF is, metals and dielectrics in glTF 2.0's Appendix B, Fresnel, D and V, and why Praxis mixes them by lobe. It covers point lights that fall off by inverse square inside KHR_lights_punctual's range window, each scene's ambient and background, the oracles behind every term and what it all costs, with live demos.
ReadFollows a pixel from measured light to the 8-bit value on screen: photometric lights and an EV100 exposure, the weighted MSAA resolve, Gran Turismo 7's tonemapping curve and Peters's blue-noise dither. It covers why the tonemapping pass alone encodes sRGB, into an 8-bit UNORM target, with live demos.
ReadExplains how #184, measuring minification's floor, split Praxis's anisotropic filtering: 16x for colour textures, 4x for data textures such as normal maps. It covers the probe rule, what 16x cost on the M4 Pro, and why fewer probes removed the sparkle, with live demos.
ReadShows how the Froxel pass lists each Froxel's point lights from the light buffer, so a pixel shades only the point lights that may reach it. It covers the oracle that checks the lists against brute force, and what the lists cost on each scene and where they pay, with live demos.
ReadTraces one hard shadow ray per pixel toward each light: where it starts, where it ends, and why it moved out of the lit pass into the shadow mask's compute pass, with live demos.
ReadShows how Praxis traces shadow rays against Moving Instances: a second TLAS, rebuilt every frame from at most 10,000 of them, which a ray the static TLAS lets through is traced against. It covers the cap and which Instances it leaves out, timing the build on the CPU and the GPU, and what it costs on the orrery and on town, with live demos.
ReadFollows a flaky test's missed ray to a race on Metal between the writes to a BLAS and the TLAS build that reads them. It shows how #244 narrowed the race down and why the fix waits for BLAS compaction on every backend, with live demos.
ReadFollows the crowd scene's 100 figures, which the GPU bends every frame from a rest Mesh, and why their shadows come from rigid per-bone shadow proxies, the form the owner picked for M7: a BLAS built when its Mesh loads keeps the rest shape, and rebuilding 100 figures' BLASes every frame would take 7.8 to 9.3 ms of GPU against a Budget of 1.0 ms. It covers the bend pass, Bends blended between Ticks, the 1,600 proxies in the dynamic TLAS and what they cost, with live demos.
ReadBuilds M3's dense Reference scene: a 1 × 1 km town placed by hash, 10,000 pigeons as Moving Instances, and a camera path from street level to a circle over the rooftops. It covers culling at target scale, counting lights per lit pixel without the side-effect trap, why the wires need 4x MSAA, and what town costs in its first M4 Pro Baseline, with live demos.
ReadFollows the crowd's figures to form (a): each one's BLAS refitted every frame through wgpu-hal on Metal, which wgpu's safe API cannot do. It covers rebuild against refit, wgpu's three layers and the snatch lock, the unsafe block's invariants one by one, the Metal shared event that orders the dynamic TLAS's build after the refits, the test that compares refit with rebuild, and timing the refits with a marker pass, with live demos. The refit form's update took 1.37 to 1.38 ms of GPU against a Budget of 1.0 ms, and the owner picked the per-bone proxies for M7; the refit stays behind its Render switch as the reference for the shapes.
ReadFollows #207's first reading of ADR 0019's gate on town, and what a frame's GPU time counts when Metal runs passes and consecutive frames side by side: a span, not a sum. It covers what the gate times and why it leaves shadow rays out, its breach in town's Baseline at 9.8 ms against 6, which sends options B and D to prototypes, shadow-ray time as a difference of two runs, the dynamic TLAS build that hid behind the frame before and was counted twice, and checking frames far along a camera path without drawing them all, with live demos.
ReadFollows #296's prototype of ADR 0019's option B on town: a visibility ID pass that records which triangle owns each sample through primitive_index, and a compute pass that compacts the lit pass's indices to those triangles by prefix sum, in the frame's order. It covers why, on the M4 Pro, a triangle drawn without its coplanar neighbour covers fewer samples, so the compaction has to keep whole Meshlets to draw A's image, the measurement that reads 13.2 ms against A's 9.7 and the 6 ms gate, and why no free visibility pass would reach it, with live demos. The prototype stays on its branch.
ReadFollows #351's fix for the sparkle on helmet's smoothest metals: highlights narrower than the turn the normal makes across a pixel, which one shading point per pixel catches or misses and 4x MSAA leaves alone. It covers filtering the distribution rather than the image, Tokuyoshi and Kaplanyan's widening of α² by the shading normal's screen-space derivatives, κ and the roughness floor, a white furnace that sums over half vectors so it works from the floor up, and the f32 error in D that furnace found, 1.1% of a white metal's light, fixed with |N × H|², with live demos. Together they cost town's lit pass 2.0%.
ReadFollows #350's fix for the terminator that stepped along a coarse smooth-shaded mesh's triangles, where the shadow mask hid each light from whole triangles that turn from it while their interpolated normals face it. It covers why skipping the ray there leaks light into cast shadows, the Surface normals the depth passes now write, each sample's triangle normal from screen-space derivatives and its interpolated normal, packed as 8-bit octahedral coordinates within 0.95°, and the ray that starts inside the surface and passes only the back face where it leaves its own mesh, with live demos. It costs town's GPU frame about 2.8 ms.
ReadFollows #347's two parts. The culling pass now writes the frame's packed indices with a workgroup per Meshlet record, dispatched indirectly with a count the GPU sums. It binds a stand-in buffer, since wgpu puts the indirect buffer and every bound group in one usage scope. The second part, a test of each Meshlet against the frustum, the depth pyramid and its normal cone, was prototyped and kept out of main. The guide covers the apex-free cone test and why any bounding sphere serves, why each Meshlet is tested once under Metal's fast math, and why town gained nothing: most of its Instances draw one Meshlet. With live demos.
ReadFollows #348's finding. Under 2x or 4x MSAA, when a fragment stage reads no flat input, the M4 Pro merges the fragments of index-sharing triangles, so a sample can show its neighbour's inputs while coverage stays exact. It covers how that cost option B's ID pass samples, and how it gave the shadow mask one triangle's normals while the lit pass shaded the other, which culling exposed. The fix, #369's, has the normals pass read a flat input, and the guide says why invariance could not do it and what stays open with one GPU measured. With live demos.
ReadFollows #297's prototype of option D, kept off main. The depth passes write each sample's triangle ID, and a compute material pass rebuilds each pixel's triangles and shades them in place of the lit pass. It covers the 16-bit halves and the phase bit, perspective-correct weights from the corners' clip x, y and w with no divide by w, gradients from neighbouring pixel centres and why the per-quad form matched the lit pass, and MSAA kept by shading each triangle once per pixel. On town the gate stayed within about 2% of A's, so A stays. With live demos.
ReadFollows #357's fix of the one-pixel outline that shadowed silhouettes wore. The shadow mask pass finds the pixels whose samples show a second surface, by each sample's distance from the nearest sample's triangle plane, traces that surface too from a packed list in an indirect dispatch, and keeps its bits in a second layer; each fragment of the lit pass reads the layer of the surface its coverage shows. It covers surfaces against triangles, why tracing a minority of pixels in place costs a whole pass, and how to read Metal's overlapping pass times. With live demos.
ReadFollows the arcs on minification's far floor, which #359 found in a reference that averages the checker exactly over each pixel. It covers sampling and Nyquist, the pixel's footprint as a box whose sinc response passes detail past Nyquist as a residue that flips sign and falls only as p/(πw), how one sample per pixel of that residue beats into arcs, why hardware anisotropic filtering is a box along the major axis and LOD bias barely helps, what a Gaussian does instead and what it blurs, and what #388 measured, with live demos. No filter has been chosen.
Follows #358's fix for sun shadow edges that stepped by whole pixels beside 4x MSAA geometry, from #364's survey: one ray per pixel is a point sample, and only more samples recover an edge inside one surface. The shadow mask pass now lists each pixel whose verdict for one of the first seven directional lights differs from a 4-neighbour's, traces that light again from its other samples, and keeps four verdict bits per light in the samples texel; the lit pass shades each fragment by the share of its covered samples the light reaches. It covers Whitted's edge-adaptive sampling and what it misses, the two new dispatches, the bit layout, and the cost: 2.86 ms of town's GPU frame, under a 4 ms ceiling. With live demos.
Follows #381's search for why every CPU stage of town ran 20 to 40% slower once the GPU became the longer half of the frame. With two frames in flight the main thread waits for the oldest frame every frame; timed through a fixed chain of 2,000 dependent multiply-adds, its M4 Pro core ran about 30% slower for the whole frame in most runs that blocked, and not when it spin-waited, on the same performance cluster and at priority 45 too. It covers frames in flight and the two ways to wait, Apple silicon's clusters and clocks, reading a clock without counters, the nine runs, how the kernel can cap the QoS class a process started from a shell asks for, and why praxis bench now spin-waits while the window blocks. With live demos.
Follows how Praxis keeps every bench run as a Bench record on the bench-history branch and finds where a frame time stepped. A record is keyed by a hash of the five paths that can change a measurement, so a docs-only commit inherits its record; a Harness version names what the metrics mean, and its ten versions so far break the charts' lines; only quiet, clean runs of main form a Series. Each publish marks a Suspect where a CPU or GPU frame median leaves the median of the 8 records before it, ± 4 median absolute deviations or 3%, once per step. It covers why the median and its absolute deviation resist one bad run, what the detector cannot see, and what #485 and #486 add. With a live demo.
One frame as the main branch runs it at commit 270c5e4 (2026-10-26), with the guide that explains each step. On the CPU, prepare reads the ECS World into Frame data, and upload writes the frame's uniforms, lights and Instance writes into per-frame staging (Instance Buffer Explained). Upload records their copies into GPU buffers at the head of the frame's commands, with the static TLAS's rebuild in a frame that writes or frees a static Instance (Acceleration Structures Explained). The GPU receives the steps below in this order, though on Metal a step can run beside the steps around it, or even start before an earlier one, where neither writes what the other binds:
crowd scene does: each figure bent from its rest Mesh (Crowd Shadows Explained). With the crowd's refit form, chosen by a Render switch and available on Metal only, the frame's commands up to the bend pass are submitted first, with the refit of each figure's BLAS, and the dynamic TLAS's build follows them (BLAS Refit Explained).count_point_lights Render switch on, which ships off: the light count pass, which counts each lit pixel's point lights for the GPU stats (Town Explained).Four guides explain what these passes rest on: Distant Textures Explained and Anisotropy Explained, how Praxis cooks and samples textures; HDR Pipeline Explained, the survey behind the light units, the HDR target and the tonemapping; and GPU Ordering Explained, why a frame that loads Meshes first waits for their BLASes to be compacted.