Readonlycapabilities§62's headless tier: no textures at all, hence maxTextureSize: 0.
Frozen — a fixture that could be reconfigured mid-test would make the
capability negotiation it is meant to exercise meaningless.
Every §62 member is answered rather than omitted (WP-R1.1), and every answer is the floor: this renderer has no device, no shader stage and no surface, so "no" and "zero" are not conservative guesses here but the literal truth. That is what makes it the conformance fixture — a backend author reading it sees the complete record with nothing left to infer.
ReadonlyeventsThe §6b channel required by Renderer. Never emitted to by this class.
Number of completed NullRenderer.initialize calls.
The most recent EffectRenderPass (not copied); null before the
first call.
Retained rather than cleared per call, unlike
NullRenderer.lastRenderTarget, because there is no "an effect pass
without an effect" call to clear it: every renderEffect has one, so a
stale value is impossible.
Options of the most recent initialize; null before the first call.
The §43 interpolation record of the most recent render (not copied;
callers reuse one per frame), or null when that call passed none — so a
later non-interpolated frame clears the record rather than leaving the
previous frame's standing, which would let a test pass for the wrong
reason. null before the first call.
Root of the most recent render; null before the first call.
The RenderTarget of the most recent
render, or null when that call passed none (R-4) — cleared per call for
the same reason NullRenderer.lastInterpolation is: a later
on-screen frame must not leave the previous off-screen pass's target
standing, which would let a test pass for the wrong reason. null before
the first call.
Nothing is drawn into it. A null renderer has no surfaces at all, so an off-screen pass is recorded and skipped exactly as an on-screen one is — which is what lets an application's render-to-texture wiring be asserted headlessly (§92's cheap half).
Arguments of the most recent resize; null before the first call.
Views of the most recent render (not copied); null before the first call.
Number of NullRenderer.render calls.
Number of NullRenderer.renderEffect calls (R-6).
Number of NullRenderer.resize calls.
§84's render counters (A-1). Declared, and never written to — this
renderer submits no draw calls at all, so every frame's honest contribution
to drawCalls, triangles, and instances is zero, and adding zero is
writing nothing.
Declaring it rather than omitting it is deliberate: it makes a headless
application's statistics wiring assertable end to end
(supportsRenderStatistics answers true, app.stats.drawCalls
reads 0 rather than NaN), which is the same job every other recording
field on this class does. A backend that genuinely cannot count is the
case the optional member exists for; this one can, and the answer is zero.
Whether NullRenderer.dispose has run. Disposal is terminal.
Drops every listener and marks the renderer disposed. Idempotent: a second call returns without doing anything. Recorded call data is left intact so a test can still assert on it after teardown.
Resolves immediately — there is no context to acquire. Records the call and its options.
Returns an already-resolved promise rather than being async, so awaiting
it costs one microtask and nothing else; the method still satisfies §61's
Promise<void> signature and an application's await app.initialize().
Optionaloptions: RendererOptionsRecords root, views, the §43 interpolation record, and the R-4
target, and draws nothing. No pose is computed: the null backend has
nothing to draw them into, and a test that wants the interpolated matrices
asks buildInterpolatedRenderList for them directly.
Optionalinterpolation: RenderInterpolationOptionaltarget: RenderTarget | nullRecords the §70 effect pass, and draws nothing (R-6).
Declared rather than omitted, exactly as
NullRenderer.statistics is and for the same reason: this class is
the interface's conformance fixture, and an application's post-processing
wiring — which passes ran, in which order, over which surfaces — is
assertable headlessly only if the null backend accepts them. A backend
that genuinely cannot run an effect is the case the optional member
exists for; this one can record, and recording is the whole of what it
does for render too.
Records the requested size. resolution defaults to 1.
Optionalresolution: number
A renderer that draws nothing and records what it was asked to draw — §62's headless tier, reduced to its floor.
Two jobs:
NullRendererruns its whole frame loop — fixed steps, systems,renderevents, render-list construction — in Node, with no canvas and no GPU. That is what theApplicationintegration tests (WP-3.6) and the §33 determinism suites need: proof that the simulation is unchanged by the presence or absence of a renderer.Rendererrequires, minus every line of GL.It is not the software rasterizer of §62's "software" tier — nothing is rasterized. Naming it
"null"rather than"headless"keeps that distinction available for a backend that really does produce pixels off screen (decision, WP-3.4).Recording
Every call is counted and its arguments retained, so a test can assert that the application drove the renderer correctly without a display. The counters are plain public fields: a test double's whole purpose is inspection, and hiding them behind getters would add ceremony without adding a guarantee.
lastViewsretains the array the caller passed, not a copy — callers are expected to reuse a per-frame array, so read it before the next frame or copy what you need (the same rule the pooled render list follows).Events
A null renderer has no context, so it never loses one and emits nothing on its own. NullRenderer.events is a fully functional emitter regardless — a test can emit
contextlostthrough it to exercise application-side loss handling without a GPU, which is exactly the §92 integration test's cheap half.Lifecycle
dispose()is idempotent and terminal, and every other method throws a FourError withINVALID_APPLICATION_STATEafterwards — the same contractApplicationuses (§45, §83). A silent no-op after disposal would let a test record zero calls and pass for the wrong reason (decision, WP-3.4). Note the deliberate asymmetry with a lost context, which is recoverable and must never throw: disposal is the application's own doing and cannot be undone.