← The development journal

Temporal history is a contract

An eight-position sample sequence is only the start. The harder question is deciding when yesterday’s pixels still belong in today’s frame.

When I started adding temporal reconstruction to OmiLith, the sampling pattern looked like the obvious centre of the feature. The path uses eight jitter positions and reconstructs at native resolution. It is a small, understandable sequence. The difficult part was everything that decides whether an earlier sample is still useful.

Temporal history is a promise about continuity. A stored pixel says that some information from a previous view can contribute to the current image. The renderer has to check that promise before it reuses the information. Otherwise, accumulation becomes a way to preserve the wrong answer.

History belongs to a view

Each view owns its history. That ownership matters when the renderer handles more than one camera or eye. History is not a general pool of recently rendered pixels: it has a particular projection, extent, and view identity behind it.

Reprojection can tell us where a previous sample would land. It does not, by itself, establish that the sample represents the surface now visible there. OmiLith checks depth and normals as part of that decision. A newly revealed surface and the foreground object that previously covered it should not be treated as interchangeable simply because a coordinate can be calculated.

Thinking about ownership first helps keep these checks connected. The reconstruction needs to know whose history it is reading, how that history was produced, and what could have changed since then.

Reset is part of the feature

The engine invalidates accumulated samples when continuity changes across the scene, projection, eye, extent, exposure, or time. These are not secondary cleanup cases. They define the situations in which reusing history stops being a valid operation.

A resize is a straightforward example of a changed extent. A changed projection or eye creates a different view contract. A discontinuity in time means the sequence is no longer the continuous progression that the accumulation expected. Exposure also belongs in the conversation because stored image information has a relationship to the conditions under which it was produced.

It is tempting to see a reset as a loss of quality because it temporarily gives up accumulated information. But preserving invalid information is not a quality improvement. Recovery after a reset is a normal state of the feature and needs to be inspected alongside the steady result.

Verify that jitter reaches the scene

One implementation issue made this especially concrete. On Metal, the intended Halton base-2/base-3 projection jitter was missing. The temporal path could have an eight-position sequence in its configuration without that sequence actually reaching the scene pass.

That is a useful distinction between a feature existing in code and its input making the complete journey through the renderer. If the scene is not sampled at the intended positions, a later reconstruction pass cannot manufacture the missing samples.

The fix required checking the handoff into the projection, not choosing a more complicated sequence. It reinforced a pattern I keep encountering in engine work: inspect the boundary where one subsystem’s assumption becomes another subsystem’s input.

Look at failure and recovery

The journal captures cover valid history, reset, and recovery, rather than showing only the most stable final frame. Each state answers a different question. A settled image shows accumulation. A reset shows that an invalid history can be rejected. Recovery shows how the path returns to its normal operation.

The sampling sequence remains important, but it is only one part of the system. Temporal reconstruction becomes easier to reason about when history has explicit ownership, validity checks, and a well-defined end. The contract is what lets old samples improve a new frame without silently carrying an old mistake forward.

Have a thought or a question?
I'd love to hear it. ↗