Part 1 of the Metal PathTracer Architecture series, based on the renderer documentation snapshot created on May 14, 2026. This expands the original GitHub devlog announcement.

When I started this project, the goal was straightforward: build a progressive path tracer that could render useful images on my Mac. That goal is still at the center of the renderer, but the surrounding questions have become much more interesting. Once a path tracer can produce an image, the next challenge is understanding it well enough to change it without losing correctness. I have been gradually turning the original renderer into a Metal-based research renderer: something that can render, expose its decisions, compare execution paths, and provide a safe place to investigate real-time techniques.

I use “research renderer” in a practical sense. It does not mean that every experiment is complete, and it certainly does not mean that every enabled mode is production-ready. It means that the renderer has enough structure and instrumentation for me to ask technical questions in a controlled way. A feature can be present, measured, and useful for learning while still being gated, limited, or unsuitable as a default.

A layered renderer

The architecture is easier to reason about when divided into a few responsibilities. Scene loading and material import prepare the scene. An acceleration-structure layer chooses between Metal hardware ray tracing and the software BVH path. The integrator turns those intersections into paths. Sampling and reuse provide several ways to estimate direct light. Finally, accumulation, denoising, tonemapping, bloom, and image output turn the result into something I can inspect.

That separation matters because a rendering experiment should not require me to rewrite the whole application. A change in light sampling should be comparable across the two integration paths. A hardware ray-tracing test should still have a software route for fallback and parity work. A debug view should reveal a problem rather than quietly altering the beauty render. The layers are not merely a diagram; they are boundaries that make experiments reversible.

The public v2 foundation already includes the core path tracer, glTF/PBR material work, hardware and software ray-tracing backends, denoising, MNEE caustics, headless rendering, and GUI controls. The current real-time-oriented work extends that foundation with scheduling and transport experiments. That distinction is important: the newer work is an investigation built on a functioning renderer, not a replacement for the basic path-tracing path.

Two ways to execute a path

The renderer keeps both a megakernel and a wavefront execution style. The megakernel is the original, comparatively direct path through the integrator. It is useful because it is simple to follow, acts as a parity reference, and remains a sensible fallback while the staged path is being validated.

The wavefront path breaks the work into explicit stages. Rays can be generated, intersected, shaded, traced toward a light, and accumulated through separate dispatches. That introduces queues and bookkeeping, but it also makes active work visible. I can inspect how many paths reach a stage, how much queue capacity is used, and where a dispatch is doing work for inactive lanes. For a renderer approaching an interactive workload, this visibility is as valuable as a potential speedup.

I am deliberately keeping the bridge conservative. Intersection supports active-count indirect dispatch, while shading still uses a direct grid in the documented bridge state. Shadow work also uses a direct grid because its storage is sparse by path index. Those choices leave scheduling efficiency on the table, but they make the current implementation easier to compare with the megakernel and easier to debug. A staged architecture is only useful if the staging itself can be trusted.

Sampling experiments with boundaries

Direct-light sampling has grown from legacy rectangle-light and emissive sampling to include RIS, spatial and temporal reuse, world-reuse experiments, and ReSTIR DI. There are also prototypes for indirect reuse, path guiding, and a radiance cache. I treat these as a family of experiments rather than a single finished feature.

The feature gates are part of the design. ReSTIR GI, ReSTIR PT, path guiding, radiance caching, and debug AOVs are disabled by default when they are not part of the baseline. That prevents an experimental transport path from silently changing a reference render. It also gives me a clear answer when a result changes: either I intentionally enabled a mode, or I did not.

This discipline is especially important for the ReSTIR-related work. Direct-light reuse has explicit reservoir and screen-space passes. The diffuse indirect path is a bounded prototype, and the path-reuse work is a research scaffold with conservative eligibility rules. Those are useful stepping stones, but they are not evidence of a complete production implementation. I would rather describe a narrow experiment accurately than use a broad name that suggests guarantees the renderer does not yet provide.

Interactive does not mean benchmarked

The current exploration targets interactive work at around 720p, including scenes such as Living Room, San Miguel, and Bistro-class large scenes. Bistro is particularly revealing because recording overhead changes the observed frame pacing. Native macOS screen recording can add enough overhead that a heavy scene no longer represents the experience of running the renderer by itself. OBS has behaved better in these tests, but I have not isolated the exact reason.

That observation changed how I talk about captures. A video is a qualitative record of the renderer’s current behavior, not a formal performance measurement. When frame time is already close to its limit, presentation and capture become part of the workload. To make a performance claim, I need a controlled run and instrumentation that separates path-tracing cost from compositor and capture overhead.

The same principle applies to the architecture. Queue metrics, counters, debug AOVs, and headless rendering are there to make behavior inspectable. They do not turn every frame into a benchmark automatically. They give me the tools to construct a measurement when a question is specific enough.

What this renderer is becoming

The most useful way to describe the project today is simple: it is a Metal renderer exploring real-time path-tracing techniques under Apple platform constraints. It has a functioning path-tracing foundation, two execution styles, hardware and software ray-tracing routes, and several advanced components behind explicit gates.

The open work is correspondingly concrete. I need to close the remaining indirect-dispatch issues in the wavefront bridge, make sparse shadow work more compact or explicitly indexed, and improve the metrics so that queue storage policies are not confused with active work. I also need better traces to separate renderer cost from presentation and capture cost. None of those tasks requires pretending that the current prototypes are finished.

For me, that is the value of building the renderer this way. The project can produce images today, but it can also show me why an image or a frame behaves as it does. Keeping a reliable baseline beside the experiments makes the ambitious parts more useful, because every new idea has a place to be measured, limited, and eventually either promoted or retired.

Continue the series

Next: From Megakernel to Wavefront on Apple GPUs.