CLEAR_SHADER_SOURCE:"struct DrawUniforms {\n viewProjection : mat4x4<f32>,\n model : mat4x4<f32>,\n color : vec4<f32>,\n normalMatrix : mat3x3<f32>,\n};\n\n@group(0) @binding(0) var<uniform> draw : DrawUniforms;\n\n@vertex\nfn vertexMain(@builtin(vertex_index) index : u32) -> @builtin(position) vec4<f32> {\n // (-1,-1), (-1, 3), (3, -1): one triangle covering the clip-space square.\n let corner = i32(index);\n let x = f32(corner / 2) * 4.0 - 1.0;\n let y = f32(corner & 1) * 4.0 - 1.0;\n return vec4<f32>(x, y, 1.0, 1.0);\n}\n\n@fragment\nfn fragmentMain() -> @location(0) vec4<f32> {\n return draw.color;\n}\n"
The clear pipeline's WGSL — a full-surface triangle at the far plane.
Why a draw clears, when WebGPU has loadOp: "clear"
Because §61's clear contract is not "clear the surface", it is "clear the
viewport rectangle": "clears are confined to the viewport rectangle, never
the whole surface", which is what makes a minimap composite over the main
view rather than erase it. WebGPU's loadOp clears the whole attachment and
has no scissor, so a loadOp: "clear" implementation of a two-view frame
would erase the first view when the second began. setScissorRect plus a
full-surface triangle clears exactly the rectangle — which is what the WebGL
backend's scissored clear does, expressed in the primitives WebGPU has.
The colour attachment therefore always loads (loadOp: "load"), and the
per-view rules fall out unchanged: a view carrying no clearColor draws no
clear triangle at all, and a view that carries one clears just its rectangle.
Depth is cleared for every view by the same draw — depthCompare: "always"
with depthWriteEnabled: true and z = 1, the far plane §61 names — so a
later view cannot be occluded by an earlier one's geometry.
The colour-less variant writes no colour channels (writeMask: 0), so the
clear-depth-only case is the same draw with a different pipeline key rather
than a second code path.
The clear pipeline's WGSL — a full-surface triangle at the far plane.
Why a draw clears, when WebGPU has
loadOp: "clear"Because §61's clear contract is not "clear the surface", it is "clear the viewport rectangle": "clears are confined to the viewport rectangle, never the whole surface", which is what makes a minimap composite over the main view rather than erase it. WebGPU's
loadOpclears the whole attachment and has no scissor, so aloadOp: "clear"implementation of a two-view frame would erase the first view when the second began.setScissorRectplus a full-surface triangle clears exactly the rectangle — which is what the WebGL backend's scissoredcleardoes, expressed in the primitives WebGPU has.The colour attachment therefore always loads (
loadOp: "load"), and the per-view rules fall out unchanged: a view carrying noclearColordraws no clear triangle at all, and a view that carries one clears just its rectangle. Depth is cleared for every view by the same draw —depthCompare: "always"withdepthWriteEnabled: trueandz = 1, the far plane §61 names — so a later view cannot be occluded by an earlier one's geometry.The colour-less variant writes no colour channels (
writeMask: 0), so the clear-depth-only case is the same draw with a different pipeline key rather than a second code path.