@woosh/meep-engine 3.14.6 → 3.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/engine/network/orchestrator/ServerAuthoritativeClient.d.ts +48 -0
- package/src/engine/network/orchestrator/ServerAuthoritativeClient.d.ts.map +1 -1
- package/src/engine/network/orchestrator/ServerAuthoritativeClient.js +246 -3
- package/src/shade/RENDERER_CONTRACT.md +584 -580
- package/src/shade/playground/avboit/README.md +117 -0
- package/src/shade/playground/avboit/index.html +215 -0
- package/src/shade/playground/avboit/main.d.ts +2 -0
- package/src/shade/playground/avboit/main.d.ts.map +1 -0
- package/src/shade/playground/avboit/main.js +875 -0
- package/src/shade/renderer/Renderer.d.ts +16 -0
- package/src/shade/renderer/Renderer.d.ts.map +1 -1
- package/src/shade/renderer/Renderer.js +29 -28
- package/src/shade/renderer/ShadeTransparencyMode.d.ts +13 -0
- package/src/shade/renderer/ShadeTransparencyMode.d.ts.map +1 -0
- package/src/shade/renderer/ShadeTransparencyMode.js +30 -0
- package/src/shade/renderer/path_tracer/accumulating/AccumulatingPathTracer.d.ts +9 -0
- package/src/shade/renderer/path_tracer/accumulating/AccumulatingPathTracer.d.ts.map +1 -1
- package/src/shade/renderer/path_tracer/accumulating/AccumulatingPathTracer.js +26 -0
- package/src/shade/renderer/rasterize/bucket/prepare_meshlet_draw_commands_by_material.d.ts +7 -2
- package/src/shade/renderer/rasterize/bucket/prepare_meshlet_draw_commands_by_material.d.ts.map +1 -1
- package/src/shade/renderer/rasterize/bucket/prepare_meshlet_draw_commands_by_material.js +12 -1
- package/src/shade/renderer/rasterize/expand/shader_filter_triangle_by_material_side.js +1 -1
- package/src/shade/renderer/rasterize/forward/FORWARD_VERTEX_SHADER_STRUCT.d.ts +3 -0
- package/src/shade/renderer/rasterize/forward/FORWARD_VERTEX_SHADER_STRUCT.d.ts.map +1 -0
- package/src/shade/renderer/rasterize/{native/oit → forward}/FORWARD_VERTEX_SHADER_STRUCT.js +4 -4
- package/src/shade/renderer/rasterize/forward/SHADING_SCENE_RESOURCE_GROUP.d.ts +3 -0
- package/src/shade/renderer/rasterize/forward/SHADING_SCENE_RESOURCE_GROUP.d.ts.map +1 -0
- package/src/shade/renderer/rasterize/forward/SHADING_SCENE_RESOURCE_GROUP.js +17 -0
- package/src/shade/renderer/rasterize/{native/oit → forward}/chunk_forward_shade_standard_fragment_brick4.d.ts +1 -1
- package/src/shade/renderer/rasterize/forward/chunk_forward_shade_standard_fragment_brick4.d.ts.map +1 -0
- package/src/shade/renderer/rasterize/{native/oit → forward}/chunk_forward_shade_standard_fragment_brick4.js +16 -16
- package/src/shade/renderer/rasterize/{native/oit → forward}/chunk_forward_shade_standard_fragment_ibl.d.ts +1 -1
- package/src/shade/renderer/rasterize/forward/chunk_forward_shade_standard_fragment_ibl.d.ts.map +1 -0
- package/src/shade/renderer/rasterize/{native/oit → forward}/chunk_forward_shade_standard_fragment_ibl.js +17 -17
- package/src/shade/renderer/rasterize/{native/oit → forward}/chunk_material_side_should_draw.d.ts +1 -1
- package/src/shade/renderer/rasterize/forward/chunk_material_side_should_draw.d.ts.map +1 -0
- package/src/shade/renderer/rasterize/{native/oit → forward}/chunk_material_side_should_draw.js +2 -2
- package/src/shade/renderer/rasterize/forward/vertex_shader_chunk.d.ts +3 -0
- package/src/shade/renderer/rasterize/forward/vertex_shader_chunk.d.ts.map +1 -0
- package/src/shade/renderer/rasterize/{native/oit → forward}/vertex_shader_chunk.js +4 -4
- package/src/shade/renderer/rasterize/native/avboit/AVBOITSettings.d.ts +27 -0
- package/src/shade/renderer/rasterize/native/avboit/AVBOITSettings.d.ts.map +1 -0
- package/src/shade/renderer/rasterize/native/avboit/AVBOITSettings.js +27 -0
- package/src/shade/renderer/rasterize/native/avboit/AVBOIT_CONSTANTS.d.ts +107 -0
- package/src/shade/renderer/rasterize/native/avboit/AVBOIT_CONSTANTS.d.ts.map +1 -0
- package/src/shade/renderer/rasterize/native/avboit/AVBOIT_CONSTANTS.js +120 -0
- package/src/shade/renderer/rasterize/native/avboit/AVBOIT_LEDGER.md +463 -0
- package/src/shade/renderer/rasterize/native/avboit/AVBOIT_PLAN_2026_09_03.md +509 -0
- package/src/shade/renderer/rasterize/native/avboit/AVBOIT_WARP_STRUCT.d.ts +15 -0
- package/src/shade/renderer/rasterize/native/avboit/AVBOIT_WARP_STRUCT.d.ts.map +1 -0
- package/src/shade/renderer/rasterize/native/avboit/AVBOIT_WARP_STRUCT.js +33 -0
- package/src/shade/renderer/rasterize/native/avboit/avboit_build_depth_curve.d.ts +20 -0
- package/src/shade/renderer/rasterize/native/avboit/avboit_build_depth_curve.d.ts.map +1 -0
- package/src/shade/renderer/rasterize/native/avboit/avboit_build_depth_curve.js +38 -0
- package/src/shade/renderer/rasterize/native/avboit/avboit_draw_fragment_body.d.ts +38 -0
- package/src/shade/renderer/rasterize/native/avboit/avboit_draw_fragment_body.d.ts.map +1 -0
- package/src/shade/renderer/rasterize/native/avboit/avboit_draw_fragment_body.js +130 -0
- package/src/shade/renderer/rasterize/native/avboit/avboit_spec_support.d.ts +37 -0
- package/src/shade/renderer/rasterize/native/avboit/avboit_spec_support.d.ts.map +1 -0
- package/src/shade/renderer/rasterize/native/avboit/avboit_spec_support.js +73 -0
- package/src/shade/renderer/rasterize/native/avboit/chunk_avboit_counts_to_extinction.d.ts +9 -0
- package/src/shade/renderer/rasterize/native/avboit/chunk_avboit_counts_to_extinction.d.ts.map +1 -0
- package/src/shade/renderer/rasterize/native/avboit/chunk_avboit_counts_to_extinction.js +17 -0
- package/src/shade/renderer/rasterize/native/avboit/chunk_avboit_dilate_word.d.ts +11 -0
- package/src/shade/renderer/rasterize/native/avboit/chunk_avboit_dilate_word.d.ts.map +1 -0
- package/src/shade/renderer/rasterize/native/avboit/chunk_avboit_dilate_word.js +18 -0
- package/src/shade/renderer/rasterize/native/avboit/chunk_avboit_extinction_to_counts.d.ts +9 -0
- package/src/shade/renderer/rasterize/native/avboit/chunk_avboit_extinction_to_counts.d.ts.map +1 -0
- package/src/shade/renderer/rasterize/native/avboit/chunk_avboit_extinction_to_counts.js +17 -0
- package/src/shade/renderer/rasterize/native/avboit/chunk_avboit_extinction_to_transmittance.d.ts +9 -0
- package/src/shade/renderer/rasterize/native/avboit/chunk_avboit_extinction_to_transmittance.d.ts.map +1 -0
- package/src/shade/renderer/rasterize/native/avboit/chunk_avboit_extinction_to_transmittance.js +17 -0
- package/src/shade/renderer/rasterize/native/avboit/chunk_avboit_fold_pairs.d.ts +15 -0
- package/src/shade/renderer/rasterize/native/avboit/chunk_avboit_fold_pairs.d.ts.map +1 -0
- package/src/shade/renderer/rasterize/native/avboit/chunk_avboit_fold_pairs.js +25 -0
- package/src/shade/renderer/rasterize/native/avboit/chunk_avboit_integral_depth_coordinate.d.ts +17 -0
- package/src/shade/renderer/rasterize/native/avboit/chunk_avboit_integral_depth_coordinate.d.ts.map +1 -0
- package/src/shade/renderer/rasterize/native/avboit/chunk_avboit_integral_depth_coordinate.js +35 -0
- package/src/shade/renderer/rasterize/native/avboit/chunk_avboit_occupancy_range.d.ts +11 -0
- package/src/shade/renderer/rasterize/native/avboit/chunk_avboit_occupancy_range.d.ts.map +1 -0
- package/src/shade/renderer/rasterize/native/avboit/chunk_avboit_occupancy_range.js +21 -0
- package/src/shade/renderer/rasterize/native/avboit/chunk_avboit_occupancy_word_mask.d.ts +9 -0
- package/src/shade/renderer/rasterize/native/avboit/chunk_avboit_occupancy_word_mask.d.ts.map +1 -0
- package/src/shade/renderer/rasterize/native/avboit/chunk_avboit_occupancy_word_mask.js +26 -0
- package/src/shade/renderer/rasterize/native/avboit/chunk_avboit_overflows.d.ts +11 -0
- package/src/shade/renderer/rasterize/native/avboit/chunk_avboit_overflows.d.ts.map +1 -0
- package/src/shade/renderer/rasterize/native/avboit/chunk_avboit_overflows.js +22 -0
- package/src/shade/renderer/rasterize/native/avboit/chunk_avboit_pack_counts.d.ts +10 -0
- package/src/shade/renderer/rasterize/native/avboit/chunk_avboit_pack_counts.d.ts.map +1 -0
- package/src/shade/renderer/rasterize/native/avboit/chunk_avboit_pack_counts.js +21 -0
- package/src/shade/renderer/rasterize/native/avboit/chunk_avboit_sample_transmittance.d.ts +10 -0
- package/src/shade/renderer/rasterize/native/avboit/chunk_avboit_sample_transmittance.d.ts.map +1 -0
- package/src/shade/renderer/rasterize/native/avboit/chunk_avboit_sample_transmittance.js +32 -0
- package/src/shade/renderer/rasterize/native/avboit/chunk_avboit_transmission_tint.d.ts +20 -0
- package/src/shade/renderer/rasterize/native/avboit/chunk_avboit_transmission_tint.d.ts.map +1 -0
- package/src/shade/renderer/rasterize/native/avboit/chunk_avboit_transmission_tint.js +27 -0
- package/src/shade/renderer/rasterize/native/avboit/chunk_avboit_transmittance_to_extinction.d.ts +10 -0
- package/src/shade/renderer/rasterize/native/avboit/chunk_avboit_transmittance_to_extinction.d.ts.map +1 -0
- package/src/shade/renderer/rasterize/native/avboit/chunk_avboit_transmittance_to_extinction.js +18 -0
- package/src/shade/renderer/rasterize/native/avboit/chunk_avboit_unpack_counts.d.ts +9 -0
- package/src/shade/renderer/rasterize/native/avboit/chunk_avboit_unpack_counts.d.ts.map +1 -0
- package/src/shade/renderer/rasterize/native/avboit/chunk_avboit_unpack_counts.js +21 -0
- package/src/shade/renderer/rasterize/native/avboit/chunk_avboit_view_depth_to_physical.d.ts +9 -0
- package/src/shade/renderer/rasterize/native/avboit/chunk_avboit_view_depth_to_physical.d.ts.map +1 -0
- package/src/shade/renderer/rasterize/native/avboit/chunk_avboit_view_depth_to_physical.js +23 -0
- package/src/shade/renderer/rasterize/native/avboit/chunk_avboit_view_depth_to_virtual.d.ts +13 -0
- package/src/shade/renderer/rasterize/native/avboit/chunk_avboit_view_depth_to_virtual.d.ts.map +1 -0
- package/src/shade/renderer/rasterize/native/avboit/chunk_avboit_view_depth_to_virtual.js +27 -0
- package/src/shade/renderer/rasterize/native/avboit/chunk_avboit_warp.d.ts +22 -0
- package/src/shade/renderer/rasterize/native/avboit/chunk_avboit_warp.d.ts.map +1 -0
- package/src/shade/renderer/rasterize/native/avboit/chunk_avboit_warp.js +38 -0
- package/src/shade/renderer/rasterize/native/avboit/graph_rasterize_meshes_transparent_avboit.d.ts +117 -0
- package/src/shade/renderer/rasterize/native/avboit/graph_rasterize_meshes_transparent_avboit.d.ts.map +1 -0
- package/src/shade/renderer/rasterize/native/avboit/graph_rasterize_meshes_transparent_avboit.js +935 -0
- package/src/shade/renderer/rasterize/native/avboit/shader_avboit_build_warp.d.ts +8 -0
- package/src/shade/renderer/rasterize/native/avboit/shader_avboit_build_warp.d.ts.map +1 -0
- package/src/shade/renderer/rasterize/native/avboit/shader_avboit_build_warp.js +135 -0
- package/src/shade/renderer/rasterize/native/avboit/shader_avboit_draw_brick4.d.ts +3 -0
- package/src/shade/renderer/rasterize/native/avboit/shader_avboit_draw_brick4.d.ts.map +1 -0
- package/src/shade/renderer/rasterize/native/avboit/shader_avboit_draw_brick4.js +101 -0
- package/src/shade/renderer/rasterize/native/avboit/shader_avboit_draw_ibl.d.ts +3 -0
- package/src/shade/renderer/rasterize/native/avboit/shader_avboit_draw_ibl.d.ts.map +1 -0
- package/src/shade/renderer/rasterize/native/avboit/shader_avboit_draw_ibl.js +96 -0
- package/src/shade/renderer/rasterize/native/avboit/shader_avboit_integrate.d.ts +3 -0
- package/src/shade/renderer/rasterize/native/avboit/shader_avboit_integrate.d.ts.map +1 -0
- package/src/shade/renderer/rasterize/native/avboit/shader_avboit_integrate.js +83 -0
- package/src/shade/renderer/rasterize/native/avboit/shader_avboit_occupancy.d.ts +9 -0
- package/src/shade/renderer/rasterize/native/avboit/shader_avboit_occupancy.d.ts.map +1 -0
- package/src/shade/renderer/rasterize/native/avboit/shader_avboit_occupancy.js +119 -0
- package/src/shade/renderer/rasterize/native/avboit/shader_avboit_resolve.d.ts +3 -0
- package/src/shade/renderer/rasterize/native/avboit/shader_avboit_resolve.d.ts.map +1 -0
- package/src/shade/renderer/rasterize/native/avboit/shader_avboit_resolve.js +81 -0
- package/src/shade/renderer/rasterize/native/avboit/shader_avboit_splat.d.ts +15 -0
- package/src/shade/renderer/rasterize/native/avboit/shader_avboit_splat.d.ts.map +1 -0
- package/src/shade/renderer/rasterize/native/avboit/shader_avboit_splat.js +237 -0
- package/src/shade/renderer/rasterize/native/material_draw_indirect.d.ts +22 -0
- package/src/shade/renderer/rasterize/native/material_draw_indirect.d.ts.map +1 -0
- package/src/shade/renderer/rasterize/native/material_draw_indirect.js +69 -0
- package/src/shade/renderer/rasterize/native/oit/graph_rasterize_meshes_transparent_oit.d.ts.map +1 -1
- package/src/shade/renderer/rasterize/native/oit/graph_rasterize_meshes_transparent_oit.js +665 -722
- package/src/shade/renderer/rasterize/native/oit/shader_oit_generate_moments.js +3 -3
- package/src/shade/renderer/rasterize/native/oit/shader_oit_resolve_moments.js +4 -4
- package/src/shade/renderer/rasterize/native/oit/shader_oit_resolve_moments_ibl.js +4 -4
- package/src/shade/renderer/rasterize/native/oit/FORWARD_VERTEX_SHADER_STRUCT.d.ts +0 -3
- package/src/shade/renderer/rasterize/native/oit/FORWARD_VERTEX_SHADER_STRUCT.d.ts.map +0 -1
- package/src/shade/renderer/rasterize/native/oit/SHADING_SCENE_RESOURCE_GROUP.d.ts +0 -3
- package/src/shade/renderer/rasterize/native/oit/SHADING_SCENE_RESOURCE_GROUP.d.ts.map +0 -1
- package/src/shade/renderer/rasterize/native/oit/SHADING_SCENE_RESOURCE_GROUP.js +0 -17
- package/src/shade/renderer/rasterize/native/oit/chunk_forward_shade_standard_fragment_brick4.d.ts.map +0 -1
- package/src/shade/renderer/rasterize/native/oit/chunk_forward_shade_standard_fragment_ibl.d.ts.map +0 -1
- package/src/shade/renderer/rasterize/native/oit/chunk_material_side_should_draw.d.ts.map +0 -1
- package/src/shade/renderer/rasterize/native/oit/vertex_shader_chunk.d.ts +0 -3
- package/src/shade/renderer/rasterize/native/oit/vertex_shader_chunk.d.ts.map +0 -1
|
@@ -0,0 +1,463 @@
|
|
|
1
|
+
# AVBOIT — ledger
|
|
2
|
+
|
|
3
|
+
Companion to [`AVBOIT_PLAN_2026_09_03.md`](./AVBOIT_PLAN_2026_09_03.md). One entry per step of §8, in the
|
|
4
|
+
order they landed, each recording what was built, how it was verified, and what the step found that the plan
|
|
5
|
+
did not predict. Findings that change the plan are noted here and the plan's revision header is bumped, never
|
|
6
|
+
silently edited.
|
|
7
|
+
|
|
8
|
+
Branch: `claude/avboit-transparency-pipeline-192cff`, worktree
|
|
9
|
+
`.claude/worktrees/avboit-transparency-pipeline-192cff`, from `9ab84a154`.
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## 0. Survey — 2026-09-03
|
|
14
|
+
|
|
15
|
+
**Done.** Read the deck (text extraction via `pdftotext`; the page renderer is not installed here) and the
|
|
16
|
+
MBOIT pipeline end to end: orchestrator, three shaders, frame-graph API, resource descriptors, the bucket →
|
|
17
|
+
draw-command prep, the emulator's capabilities, the software device's recording API, and the `Renderer` call
|
|
18
|
+
site and mode-switch precedents.
|
|
19
|
+
|
|
20
|
+
**Findings that shaped the plan:**
|
|
21
|
+
|
|
22
|
+
- The sorted meshlet buffer the draws consume has no count header
|
|
23
|
+
(`shader_sort_meshlets_by_material.js:20` writes a bare `array<MeshletDefinition>`); the collection *before*
|
|
24
|
+
sorting does, and the prep helper already builds an indirect dispatch command over it. The occupancy pass
|
|
25
|
+
needs both, so the helper returns two more handles (plan §3.1).
|
|
26
|
+
- No render pipeline in the tree writes a storage buffer from the fragment stage, and the pipeline-layout
|
|
27
|
+
helper applies one visibility to all groups. The splat pass builds its layout per group (plan §3.11).
|
|
28
|
+
- The emulator models `atomic<T>` as `{ value }` objects and implements `atomicAdd/Min/Max/Or`, `textureStore`
|
|
29
|
+
on 3D `CPUBitmapData`, `extractBits/insertBits`, `countOneBits`; barriers are no-ops and there is no
|
|
30
|
+
workgroup-cooperative dispatch. The warp build is therefore a single invocation so it stays
|
|
31
|
+
emulator-testable (plan §3.4).
|
|
32
|
+
- View-space depth sign: the volumetrics take `-(view_matrix × p).z`, so the camera looks down −Z; the
|
|
33
|
+
occupancy pass uses the same (plan §3.3).
|
|
34
|
+
- `float32-blendable` is on the device floor purely for MBOIT's `r32float`/`rgba32float` targets; AVBOIT
|
|
35
|
+
blends `rgba16float` only (plan §3.11).
|
|
36
|
+
- The in-app browser pane cannot run the full renderer (immediate data unsupported there, per memory), so the
|
|
37
|
+
playground look (step 9) needs a real Chrome tab; module compilation (step 8) works in either.
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
## 1. Skeleton and switch — 2026-09-03
|
|
42
|
+
|
|
43
|
+
**Done.** `ShadeTransparencyMode` (`MBOIT = 0`, `AVBOIT = 1`), `Renderer.transparency_mode` defaulting to
|
|
44
|
+
MBOIT, a ternary at the single transparency call site selecting the recorder, and
|
|
45
|
+
`graph_prepare_meshlet_draw_commands_by_material` returning `meshlet_collection` and
|
|
46
|
+
`meshlet_iterator_command` beside what it returned before. `Renderer.js` and the prep helper are CRLF files;
|
|
47
|
+
edited byte-exactly.
|
|
48
|
+
|
|
49
|
+
**Verified.** The rasterize spec tree (14 files, 81 tests) passes with the helper change; the source-level spec
|
|
50
|
+
in `graph_rasterize_meshes_transparent_avboit.spec.js` pins the import, the default and the ternary.
|
|
51
|
+
|
|
52
|
+
**Found.** Nothing the plan did not predict.
|
|
53
|
+
|
|
54
|
+
---
|
|
55
|
+
|
|
56
|
+
## 2. Depth curve, packing, warp — 2026-09-03
|
|
57
|
+
|
|
58
|
+
**Done.** `chunk_avboit_depth_curve` (t(z), z(t)), `chunk_avboit_pack` (10|10|10, counts ↔ extinction ↔
|
|
59
|
+
transmittance, the overflow predicate), `AVBOIT_WARP_STRUCT`, `chunk_avboit_warp` (φ, and the view-depth →
|
|
60
|
+
virtual → physical composition), each with an emulator spec.
|
|
61
|
+
|
|
62
|
+
**Found.**
|
|
63
|
+
|
|
64
|
+
- The emulator hands a `u32` with its top bit set back as a negative JS number (`0xFFFFFF00` reads as
|
|
65
|
+
`-256`). Specs compare through `>>> 0`. `countOneBits` and the shift operators are unsigned internally, so
|
|
66
|
+
the shaders themselves are unaffected.
|
|
67
|
+
- `vec3<f32>` values travel as `Float32Array` through the emulator, so round-trip expectations are f32-exact
|
|
68
|
+
(six digits), not f64.
|
|
69
|
+
|
|
70
|
+
---
|
|
71
|
+
|
|
72
|
+
## 3. Occupancy and warp build — 2026-09-03
|
|
73
|
+
|
|
74
|
+
**Done.** `chunk_avboit_occupancy` (range, word mask, dilate, fold), `shader_avboit_occupancy` (per meshlet,
|
|
75
|
+
indirect over the filtered collection), `shader_avboit_build_warp` (one invocation: dilate → count → halve
|
|
76
|
+
until it fits, then the exclusive-prefix LUT), with specs for the chunk and for the whole build shader
|
|
77
|
+
dispatched through the emulator.
|
|
78
|
+
|
|
79
|
+
**Found.** The implementation dilates at *every* level it evaluates rather than once at the finest and once at
|
|
80
|
+
the settled level, which is the same thing where it matters (the settled level) and simpler to reason about.
|
|
81
|
+
`AVBOIT_OCCUPANCY_DILATION` at the prepass is therefore only a belt-and-braces one slice. Plan §3.4 describes
|
|
82
|
+
the per-level version.
|
|
83
|
+
|
|
84
|
+
Specs pin: empty → `V_max` and an all-zero LUT; a run that dilates to exactly `P` does not halve; one over
|
|
85
|
+
halves once and is re-dilated at the coarser level; everywhere-occupied halves down to `V = P`; the prefix is
|
|
86
|
+
exclusive, monotone, and never exceeds `P`.
|
|
87
|
+
|
|
88
|
+
---
|
|
89
|
+
|
|
90
|
+
## 4. Splat — 2026-09-03
|
|
91
|
+
|
|
92
|
+
**Done.** `shader_avboit_splat`: low-resolution render pass, no depth attachment, a `r8unorm` dummy target
|
|
93
|
+
with `writeMask: 0`, per-group pipeline layout with the atomics group fragment-only, linear splat with
|
|
94
|
+
per-channel overflow marks stored inverted under `atomicMax`. `chunk_avboit_tint` factored out so the splat
|
|
95
|
+
and the draw compute the transmission tint from one function.
|
|
96
|
+
|
|
97
|
+
**Found.** No un-jitter in the splat: `unjitter_uv` is derivative-based and the jitter is in
|
|
98
|
+
internal-resolution pixels, so at 1/8 resolution the correction would be eight times too large. Recorded in
|
|
99
|
+
the shader's docblock; the plan did not mention it.
|
|
100
|
+
|
|
101
|
+
**Verified.** Validity tier (vertex + fragment) and the tier-3 layout spec: group 3 entries are `FRAGMENT`
|
|
102
|
+
with `buffer.type === "storage"`, groups 0–2 are `VERTEX | FRAGMENT`, the target's `writeMask` is 0, no
|
|
103
|
+
`depthStencil`.
|
|
104
|
+
|
|
105
|
+
---
|
|
106
|
+
|
|
107
|
+
## 5. Integrate — 2026-09-03
|
|
108
|
+
|
|
109
|
+
**Done.** `shader_avboit_integrate`, 8×8 workgroups, exclusive prefix into a `P + 1`-deep `rgba8unorm` 3D
|
|
110
|
+
storage texture, per-channel saturation and overflow pinning. Emulator spec over one column: exclusive
|
|
111
|
+
prefix, independent channels, saturation, a summed field past 255 saturating in one step, an overflow mark
|
|
112
|
+
pinning only its channel, a mark at slice 0.
|
|
113
|
+
|
|
114
|
+
**Found.** Nothing unpredicted.
|
|
115
|
+
|
|
116
|
+
---
|
|
117
|
+
|
|
118
|
+
## 6. Draw and sample — 2026-09-03
|
|
119
|
+
|
|
120
|
+
**Done.** `chunk_avboit_sample_transmittance` (bias in virtual slices, warp, `(p + 0.5) / (P + 1)` depth
|
|
121
|
+
coordinate, `exp`), `avboit_draw_fragment_body` (one WGSL text shared by the IBL and Brick4 pipelines, where
|
|
122
|
+
the two MBOIT resolve shaders share theirs by duplication), `shader_avboit_draw_ibl`,
|
|
123
|
+
`shader_avboit_draw_brick4`, `AVBOIT_INTEGRAL_SAMPLER_DESCRIPTOR`. Emulator spec of the sample chunk against a
|
|
124
|
+
hand-built integral: run boundaries land on texel centres, an event in front attenuates by `exp(-E × ln 256)`,
|
|
125
|
+
the bias clears a surface's own linear splat, a gap sample reads the previous run's total and none of the
|
|
126
|
+
next run's, beyond the far plane the sample reads the total.
|
|
127
|
+
|
|
128
|
+
**Found.** The emulator's `null` sampler is the linear clamp-to-edge fallback the spec wants, so no sampler
|
|
129
|
+
object is built there.
|
|
130
|
+
|
|
131
|
+
---
|
|
132
|
+
|
|
133
|
+
## 7. Resolve, orchestrator, tier 3 — 2026-09-03
|
|
134
|
+
|
|
135
|
+
**Done.** `shader_avboit_resolve` (reads the scene, writes a fresh `rgba16float`; RGB total transmittance
|
|
136
|
+
cannot blend in place), the full `graph_rasterize_meshes_transparent_avboit` with its sub-recorders exported
|
|
137
|
+
(`graph_avboit_occupancy`, `_build_warp`, `_splat`, `_integrate`, `_draw`),
|
|
138
|
+
`avboit_shaders_wgsl_validity.spec.js` (10 modules, zero diagnostics),
|
|
139
|
+
`graph_rasterize_meshes_transparent_avboit.spec.js` on the software device.
|
|
140
|
+
|
|
141
|
+
**Found.**
|
|
142
|
+
|
|
143
|
+
- `graph_compute_pass` cannot create a `read_write` storage buffer from a descriptor: the helper puts every
|
|
144
|
+
readable binding through the read path first, so the descriptor arrives at `pass.write` as an object. The
|
|
145
|
+
warp build is recorded by hand instead, and its `settings` uniform is serialized with
|
|
146
|
+
`encoder.allocateTransientValueBuffer` — the plain-object convenience only exists inside the graph
|
|
147
|
+
helpers, not in `ComputeShader.dispatch`. Plan §3.1's table is unchanged; the pass is the same, just not
|
|
148
|
+
via the helper.
|
|
149
|
+
- No spec builds a `GPUViewContext` over a scene on the mock, so the tier-3 spec pins the exported
|
|
150
|
+
sub-recorders (warp build: one workgroup into a buffer of the struct's size; integration: the dispatch
|
|
151
|
+
covers the voxel grid and the texture is `3d`, `P + 1` deep, storage + sampled; resolve: one 3-vertex draw
|
|
152
|
+
into a fresh target) and the `Renderer` switch at source level. The whole orchestrator's first encode is on
|
|
153
|
+
a real device (steps 8–9).
|
|
154
|
+
|
|
155
|
+
**Verified.** 62 tests across the avboit directory; 81 across `rasterize/`; the full `src/shade` suite
|
|
156
|
+
(489 files, 3360 tests) passes; `scripts/check-types.mjs` reports the declaration emit clean and zero
|
|
157
|
+
consumer-visible errors.
|
|
158
|
+
|
|
159
|
+
---
|
|
160
|
+
|
|
161
|
+
## 8–9. Real compile and the look — 2026-09-03, handed over
|
|
162
|
+
|
|
163
|
+
**Done.** `src/shade/playground/avboit/`: the deck's harness in miniature (a stack of cards at a controlled
|
|
164
|
+
spacing, a tinted glass pane, an opaque backdrop), the MBOIT/AVBOIT switch, and a **Compile check** that
|
|
165
|
+
pushes all ten AVBOIT modules through `createShaderModule` on the live device and prints
|
|
166
|
+
`getCompilationInfo`. The README carries the one scripted session (six steps) with what "pass" looks like
|
|
167
|
+
for each.
|
|
168
|
+
|
|
169
|
+
**Not done here, and why.** The in-app browser pane returned `null` from `requestAdapter()` this session
|
|
170
|
+
(its `wgslLanguageFeatures` are present, the adapter is not — the same state
|
|
171
|
+
`playground-needs-immediate-data` recorded on 2026-09-02), and no Claude-in-Chrome browser was connected.
|
|
172
|
+
So neither the real compile nor the picture happened on this side. What the pane did prove: the page
|
|
173
|
+
loads under the dev server, every import resolves (the ten shader modules assemble at import time, so a
|
|
174
|
+
chunk-dependency or struct error would have thrown there), and `start()` runs up to
|
|
175
|
+
`Renderer.initialize`, which reports `requestAdapter returned null` cleanly. The sources were dumped
|
|
176
|
+
(`scratchpad/wgsl/*.wgsl`, 217 KB) for a pane compile should an adapter come back. What is known: every
|
|
177
|
+
module passes the validator, which has no type checker; the type-level risks are listed below so the first
|
|
178
|
+
compile failure, if any, is quick to place.
|
|
179
|
+
|
|
180
|
+
**Type-level things the validator could not see** (in the order a compiler would meet them):
|
|
181
|
+
|
|
182
|
+
- `avboit_warp` takes `ptr<storage, AVBOITWarp, read>`; the splat and draw pass `&warp` where `warp` is
|
|
183
|
+
declared through `addStorageBuffer(..., false)`. `var<storage>` without an explicit mode is `read`, which
|
|
184
|
+
matches.
|
|
185
|
+
- `atomicAdd(&extinction_volume[column + u32(s)], …)` indexes an `array<atomic<u32>>` binding from the
|
|
186
|
+
fragment stage — core WGSL, but the first such pipeline in the tree.
|
|
187
|
+
- `textureStore(integral, vec3<i32>, vec4<f32>)` on `texture_storage_3d<rgba8unorm, write>`.
|
|
188
|
+
- `select(vec3<u32>, vec3<u32>, vec3<bool>)` and `select(vec3<f32>, vec3<f32>, vec3<bool>)` in the
|
|
189
|
+
integration; both are the component-wise overload.
|
|
190
|
+
- The splat fragment entry has no return type against a pipeline with one colour target at `writeMask: 0`.
|
|
191
|
+
|
|
192
|
+
---
|
|
193
|
+
|
|
194
|
+
## 10. Docs — 2026-09-03
|
|
195
|
+
|
|
196
|
+
**Done.** `RENDERER_CONTRACT.md` §2 says which mode needs `float32-blendable`; §9 lists
|
|
197
|
+
`transparency_mode`. `CHANGELOG.md` carries the entry under the unreleased 4.0.0 "Added". The plan is at
|
|
198
|
+
revision 1; nothing in steps 1–9 required a revision of its design sections.
|
|
199
|
+
|
|
200
|
+
**Open, for the session in the playground README:** the ten real compiles; the first frame under AVBOIT;
|
|
201
|
+
the stack at the default spacing (does the bar behind it show through?); the spacing at which the cards
|
|
202
|
+
fall into one slice; the red pane's tint on the backdrop. Results go here as entry 11.
|
|
203
|
+
|
|
204
|
+
---
|
|
205
|
+
|
|
206
|
+
## 11. Path-traced reference on the playground — 2026-09-03
|
|
207
|
+
|
|
208
|
+
**Done.** A third mode on the page, "path traced (reference)": `feature_path_tracing_enabled` on, TAA
|
|
209
|
+
forced off while it is, the tracer's running-mean floor lowered from 0.01 to 0.002 so the image keeps
|
|
210
|
+
settling past 100 samples, a **Reset accumulation** button, and a once-a-second readout of how converged
|
|
211
|
+
the image is. That readout needed one small engine addition: `AccumulatingPathTracer.min_sample_count`,
|
|
212
|
+
the sample count of the tile that has had the fewest (the per-tile counters were private, and frames
|
|
213
|
+
elapsed is not a sample count — a frame advances one tile).
|
|
214
|
+
|
|
215
|
+
**What the reference is good for, and not** (README "The reference"):
|
|
216
|
+
|
|
217
|
+
- The tracer rolls each transparent hit against alpha and continues the ray, so it converges to the
|
|
218
|
+
exact order-independent composite of the card stack — the thing both OIT modes approximate. Good.
|
|
219
|
+
- It has no per-channel transmission tint (thin-walled transmission carries only `(1 - F)`), so it
|
|
220
|
+
cannot confirm AVBOIT's glass tint; the pane is compared with the tint off.
|
|
221
|
+
- Its shadow rays treat geometry as opaque (the tracer's own `TODO`), so the cards cast full shadows
|
|
222
|
+
there and none under either OIT mode. The stack, not its shadow, is the comparison.
|
|
223
|
+
|
|
224
|
+
**Verified.** `path_tracer/` has no specs of its own; the getter is a read of a private array. The
|
|
225
|
+
page's module graph was re-loaded in the pane (see entry 8–9 for what the pane can and cannot prove).
|
|
226
|
+
|
|
227
|
+
---
|
|
228
|
+
|
|
229
|
+
## 12. First renders on a real device — 2026-09-03
|
|
230
|
+
|
|
231
|
+
Alex ran the playground and pasted three canvas captures at the default settings (four cards, alpha
|
|
232
|
+
0.85, spacing 1.5, red glass pane): path traced, AVBOIT, MBOIT. Read against the code, not measured.
|
|
233
|
+
|
|
234
|
+
**The stack — the deck's depth-complexity claim reproduces.** Under MBOIT the green card, two layers
|
|
235
|
+
back, shows through both the red and the blue card as a lighter rectangle inside the red one. Under
|
|
236
|
+
the path tracer and under AVBOIT it does not; the overlap is red with the blue card directly behind
|
|
237
|
+
faintly visible. AVBOIT is the closer of the two to the reference on the stack. Both rasterized modes
|
|
238
|
+
render the cards darker than the tracer, and they agree with each other, so that is a forward-shading
|
|
239
|
+
versus tracer difference and not this pipeline's.
|
|
240
|
+
|
|
241
|
+
**Also as predicted:** the tracer casts full shadows from the cards (its occlusion query treats
|
|
242
|
+
geometry as opaque), neither OIT mode does.
|
|
243
|
+
|
|
244
|
+
**The glass pane — the tint is working as written, and is the wrong default.** Under AVBOIT the pane
|
|
245
|
+
is deep red; under the tracer and MBOIT it is clear glass. The material is transmission 1 with a red
|
|
246
|
+
base colour, and `chunk_avboit_tint` multiplies transmitted light by that base colour, so green and
|
|
247
|
+
blue drop to ~14 %. It is not opaque — the horizon shows through as the dark-to-bright step at floor
|
|
248
|
+
level — but it is a material semantic the other paths do not have: the forward shader states there is
|
|
249
|
+
no per-channel transmission tint in the material model, and the tracer's thin-walled transmission
|
|
250
|
+
carries only the Fresnel split. The contract's promise that the two modes take the same inputs and
|
|
251
|
+
are safe to flip per frame does not survive a flip that recolours glass.
|
|
252
|
+
|
|
253
|
+
Two corrections to what the code says about itself: the chunk docblock calls base-colour-as-tint
|
|
254
|
+
"the OpenPBR default", which it is not (OpenPBR's transmission colour defaults to white; glTF's
|
|
255
|
+
transmission extension is the one that uses the base colour); and plan §4's first row rests on the
|
|
256
|
+
same reading.
|
|
257
|
+
|
|
258
|
+
**Proposed:** keep the RGB plumbing, default the tint off, expose it as a runtime switch
|
|
259
|
+
(`renderer.avboit.transmission_tint_from_base_color`, carried in the warp header so the splat and
|
|
260
|
+
draw agree per frame), a checkbox on the page, plan to revision 2. The alternative — keep it on and
|
|
261
|
+
document it as an AVBOIT-only semantic — makes one asset mean two things depending on a renderer
|
|
262
|
+
switch. Alex chose the first; entry 14.
|
|
263
|
+
|
|
264
|
+
**Also closed by these renders:** step 8's real compile, for the IBL variant. An AVBOIT frame on a real
|
|
265
|
+
device means the occupancy, warp, splat, integrate, IBL draw and resolve modules all compiled and the
|
|
266
|
+
pipeline ran end to end. The Brick4 draw variant is not exercised by the page (`indirect_lighting_mode`
|
|
267
|
+
is IBL there) and remains compiled only by the validator.
|
|
268
|
+
|
|
269
|
+
---
|
|
270
|
+
|
|
271
|
+
## 13. The deck's opaque-card case on the page — 2026-09-03
|
|
272
|
+
|
|
273
|
+
**Done.** At Alex's request: a small pale quad, alpha 1 on a `Transparent` material, one unit behind
|
|
274
|
+
the last card and offset so part of it is seen through the stack and part stands against the backdrop
|
|
275
|
+
and the dark bar. Both pipelines clamp its coverage at `OIT_COVERAGE_MAX` (0.997), so it is the "0 %
|
|
276
|
+
transmittance card" of deck slides 35–38: the case where a moment reconstruction cannot hold the
|
|
277
|
+
step, leaks the background through the surface, and tears along the depth discontinuity at its edge.
|
|
278
|
+
A checkbox (`opaque_quad`, default on) and step 8 of the README's session.
|
|
279
|
+
|
|
280
|
+
**What to expect from AVBOIT here, from the design:** the quad's extinction saturates its slice
|
|
281
|
+
(`-ln(1 - 0.997) / ln 256 ≈ 1.05`, clamped to 1.0), so the integral behind it reads 1.0 and every
|
|
282
|
+
fragment further back is weighted by a transmittance of 1/256. Its edge is a step in the extinction
|
|
283
|
+
volume at 1/8 resolution, softened by the draw's trilinear sample over about one voxel — so a slight
|
|
284
|
+
halo of that width is the expected artefact, not a leak. Anything behind the quad showing through its
|
|
285
|
+
interior would be a finding.
|
|
286
|
+
|
|
287
|
+
---
|
|
288
|
+
|
|
289
|
+
## 14. The tint, kept and off by default — 2026-09-03
|
|
290
|
+
|
|
291
|
+
**Done.** `AVBOITSettings` on `Renderer.avboit` with one switch, `transmission_tint_from_base_color`
|
|
292
|
+
(default false). The orchestrator takes `avboit` beside its other inputs (the MBOIT one ignores it), the
|
|
293
|
+
warp build copies it into a new `tint_from_base_color` header word, and both the splat and the draw
|
|
294
|
+
weight `chunk_avboit_tint` by `transmission_factor × f32(warp.tint_from_base_color)`. The chunk's
|
|
295
|
+
docblock now says whose semantics the mapping is (glTF's, not OpenPBR's). Plan to revision 2 (§3.5,
|
|
296
|
+
§4, §7). Contract §9 lists `avboit`. The page has a checkbox for it, off by default, and step 6 of the
|
|
297
|
+
session turns it on before comparing the pane.
|
|
298
|
+
|
|
299
|
+
**Why a header word and not a shader constant:** the splat and the draw must agree within a frame, and
|
|
300
|
+
the header is already the one place both read per-frame facts from; a runtime flag lets the page do
|
|
301
|
+
the A/B without a rebuild.
|
|
302
|
+
|
|
303
|
+
**Verified.** Build-warp emulator spec asserts the header copies the flag; validity tier over the
|
|
304
|
+
edited modules; rasterize spec tree.
|
|
305
|
+
|
|
306
|
+
---
|
|
307
|
+
|
|
308
|
+
## 15. Decisions on the open items — 2026-09-03
|
|
309
|
+
|
|
310
|
+
Alex's answers to the open list, and what each became.
|
|
311
|
+
|
|
312
|
+
**Slice floor — mine to call; 5 cm is a spacing worth resolving.** Virtual slice thickness is
|
|
313
|
+
`(z0 + z - near) · ln(1 + (far - near) / z0) / V`. At the page's camera (`near 0.1`, `far 200`,
|
|
314
|
+
`z0 = 0.8`) and `V = 1024` that was 5.8 cm at 10 m: one slice, under the two-slice bias, so 5 cm
|
|
315
|
+
blended. `AVBOIT_VIRTUAL_SLICES_MAX` is now 4096 — 1.5 cm at 10 m, 2.8 cm at 20 m, 7 cm at 50 m — so
|
|
316
|
+
5 cm resolves out to about 17 m before the bias eats it, and the adaptive halving still pulls it back
|
|
317
|
+
when more than 128 slices are occupied. The bias stays at 2.0 rather than dropping to the analytic
|
|
318
|
+
1.5: the half-slice margin is what covers the depth difference between a splat fragment (a
|
|
319
|
+
1/8-resolution pixel centre) and a draw fragment on a sloped surface. Cost: 128 occupancy words, a
|
|
320
|
+
16 KB LUT, a longer single-lane loop in the warp build. Specs unchanged in intent (they read the
|
|
321
|
+
constant) and green.
|
|
322
|
+
|
|
323
|
+
**The opaque quad — fine as is.** Stays on the page.
|
|
324
|
+
|
|
325
|
+
**Camera.** `OrbitalCameraController` on the page (drag, wheel, right-drag), **Copy camera** to the
|
|
326
|
+
clipboard and a box, **Apply camera** from the box, `camera_state()` / `apply_camera()` /
|
|
327
|
+
`configure({ camera })` from the console. Plan §9 gains the slope risk in its own words: a splat
|
|
328
|
+
fragment carries its low-resolution pixel centre's depth, and on a grazing surface that can exceed
|
|
329
|
+
the bias.
|
|
330
|
+
|
|
331
|
+
**Brick4 — should land.** The variant exists and is validator-clean; the page now has an
|
|
332
|
+
indirect-lighting select (IBL / Brick4) so the Brick4 draw pipeline compiles and draws on a device.
|
|
333
|
+
The scene has no probes, so the indirect term is black either way; the step is in the session.
|
|
334
|
+
|
|
335
|
+
**Timings — hook up the profiler.** Done as on `volumetrics_froxel`: **Profile** starts a
|
|
336
|
+
`GPUProfileSession` at `WORKLOAD`, pressing it again drains and downloads
|
|
337
|
+
`avboit-<mode>-<timestamp>.sgpt` for the gpu-inspector-tool, with the mode and the page's settings in
|
|
338
|
+
the note and the adapter in the meta. Same settings, two recordings, one per mode, is the comparison.
|
|
339
|
+
|
|
340
|
+
**Software device — wrong tool for an end-to-end spec, and that is fine.** Plan §6 tier 3 now says
|
|
341
|
+
so; the full-orchestrator spec is off the list.
|
|
342
|
+
|
|
343
|
+
**Phase 2 — only clean, unambiguous, non-situational.** Decided item by item in plan §7 revision 3.
|
|
344
|
+
One landed: the draw discards a fragment the volume already calls extinct (`T ≤ 1/256` in every
|
|
345
|
+
channel) *before* forward shading. No new resource or pass; the error is bounded by `1/256` of the
|
|
346
|
+
background (`AVBOIT_EXTINCT_TRANSMITTANCE`), below the 8-bit volume's own resolution. Tile skipping
|
|
347
|
+
(no: sub-millisecond passes), the zero-transmittance depth cull (not now: situational, touches the
|
|
348
|
+
depth other passes read, wants its own plan and a scene that needs it), the monochrome buffer (no:
|
|
349
|
+
two representations for content we do not have), the sparse texture (no: speculative).
|
|
350
|
+
|
|
351
|
+
**Back-sided materials — ignored for now.** Plan §7 keeps the note.
|
|
352
|
+
|
|
353
|
+
**AVBOIT is the default.** `Renderer.transparency_mode = ShadeTransparencyMode.AVBOIT`; the enum,
|
|
354
|
+
the contract (§2 keeps `float32-blendable` on the floor while MBOIT is selectable; §9 row), the
|
|
355
|
+
changelog and the page's default select follow. The source-level spec pins the new default.
|
|
356
|
+
|
|
357
|
+
**Committed** on the branch after this entry.
|
|
358
|
+
|
|
359
|
+
---
|
|
360
|
+
|
|
361
|
+
## 16. The page's camera and profile buttons, reported not working — 2026-09-03
|
|
362
|
+
|
|
363
|
+
Alex: the camera could not be moved, and **Profile** appeared to do nothing with no way to stop it.
|
|
364
|
+
|
|
365
|
+
**What was verified here.** The page was restructured so the camera, its controller and every control
|
|
366
|
+
are wired *before* the device is requested. That made the input path testable in the in-app pane,
|
|
367
|
+
which has no adapter: a synthetic pointer drag dispatched on the canvas orbited the camera from
|
|
368
|
+
`(0, 2.5, 12)` to `(-7.3, 3.7, 9.4)` and a wheel event zoomed it, read back through `camera_state()`.
|
|
369
|
+
So `OrbitalCameraController` on `document.body`, the pointer device and the transform write are
|
|
370
|
+
correct as wired. What could not be reproduced is the failure in a real session.
|
|
371
|
+
|
|
372
|
+
**What the restructure found.** Building geometry before `Renderer.initialize` threw
|
|
373
|
+
`Cannot read properties of undefined (reading 'exports')`: the meshlet builder runs on a WASM
|
|
374
|
+
instance (`MeshoptClusterizer`) that instantiates asynchronously at import, and the renderer's own
|
|
375
|
+
initialization had been what gave it time. The page now awaits `MeshoptClusterizer.ready` itself. Not
|
|
376
|
+
the reported fault (the committed order built geometry after the device), but the same class of
|
|
377
|
+
thing: a step that silently depended on another step's timing.
|
|
378
|
+
|
|
379
|
+
**Made robust, since the fault could not be seen.** Every control handler runs through a guard that
|
|
380
|
+
puts a failure into the report panel (`<what> failed: <message>`) instead of only the console; the
|
|
381
|
+
final settings application is guarded so a settings error cannot stop the frame loop from starting;
|
|
382
|
+
the Profile button carries a `recording` state (label and red border) so a running capture is
|
|
383
|
+
unmistakable and the same button stops it; the controls are spelled out under the canvas. If a real
|
|
384
|
+
session still does not orbit, the report panel now says why.
|
|
385
|
+
|
|
386
|
+
**Suspicion, unverified.** With HMR off, a reload taken while the page's files were being rewritten
|
|
387
|
+
on disk can pair one version of `index.html` with another of `main.js` or `Renderer.js`; a
|
|
388
|
+
`TypeError` in the settings step then killed `start()` before the frame loop, which is the one
|
|
389
|
+
sequence that leaves the buttons registered, the camera frozen and no picture of the cause. A hard
|
|
390
|
+
reload after this commit is the first thing to try.
|
|
391
|
+
|
|
392
|
+
---
|
|
393
|
+
|
|
394
|
+
## 17. First timings — 2026-09-03
|
|
395
|
+
|
|
396
|
+
Alex ran a short profiling session on the page (the deck's harness in miniature: four cards, the
|
|
397
|
+
glass pane, the opaque quad, 1280x720): **AVBOIT 289.9 us, MBOIT 282.4 us**, averages of the
|
|
398
|
+
transparency work, which he reads as within the noise. Device and capture files not recorded here.
|
|
399
|
+
|
|
400
|
+
**What that says.** On this scene the two pipelines cost the same. That is the expected shape, not a
|
|
401
|
+
surprise: the deck's cost argument (AVBOIT approaching no-OIT, slides 106-110) comes from VFX content
|
|
402
|
+
with tens of millions of overdrawn transparent pixels, where the 1/8-resolution splat, the saturated
|
|
403
|
+
integral and the early-out remove most of the shading work. Six quads at 720p have almost nothing
|
|
404
|
+
for those to remove, and AVBOIT's fixed costs — the occupancy pass, the warp build, the integration
|
|
405
|
+
over a 160x90x129 volume, the resolve into a fresh target — are of the same order as MBOIT's two
|
|
406
|
+
full-resolution passes over the same few quads. So the number closes the question "does the new path
|
|
407
|
+
cost more on light content" (no), and leaves open the one the deck actually answers, which needs a
|
|
408
|
+
scene with real transparent overdraw. Plan §9's contention risk is unmeasured until then.
|
|
409
|
+
|
|
410
|
+
**Committed** with this entry.
|
|
411
|
+
|
|
412
|
+
---
|
|
413
|
+
|
|
414
|
+
## 18. Review against the engine — 2026-09-03
|
|
415
|
+
|
|
416
|
+
Alex asked three questions of the code: is anything reinvented, does it follow one function per
|
|
417
|
+
chunk, does it fight the engine. Surveyed and fixed; plan to revision 4.
|
|
418
|
+
|
|
419
|
+
**Reinvented, now reused.**
|
|
420
|
+
|
|
421
|
+
- *The depth curve.* `chunk_avboit_depth_curve` (t(z), z(t), a `z0` knob) was a second copy of what the
|
|
422
|
+
light clusters and the froxels already use: `cluster_depth_to_z_slice` with parameters from
|
|
423
|
+
`build_light_cluster_parameters`. That builder's `scale` argument is the linearization the deck
|
|
424
|
+
describes; `avboit_build_depth_curve` chooses it so the curve is pure log from the near offset,
|
|
425
|
+
because the warp makes near precision free. Gone: the chunk, its inverse, its spec, the
|
|
426
|
+
`AVBOIT_DEPTH_CURVE_LINEARIZATION` constant, and the camera binding of the warp build (the curve now
|
|
427
|
+
arrives as three floats in the settings and in the occupancy pass's own settings uniform).
|
|
428
|
+
- *The bit fold.* `avboit_fold_pairs` is the OR of the even bits compacted and the odd bits compacted,
|
|
429
|
+
which is `decode32_morton2`'s job. Found on the way: for a 32-bit code that function's `.y` is *not*
|
|
430
|
+
the odd bits — the `<< 31` that separates them in the 16-bit variant (`<< 15`) cannot work in 32
|
|
431
|
+
bits — and the first reuse trusted it; the warp-build spec caught it as an occupied count of 70
|
|
432
|
+
against 66. The fold now goes through the correct `.x` half twice, once on `x >> 1`, and has a spec
|
|
433
|
+
against a bit-by-bit reference. `decode32_morton2` is used nowhere else in the tree; its `.y` is a
|
|
434
|
+
latent engine defect, noted for whoever reaches for it next.
|
|
435
|
+
- *The sampler.* `AVBOIT_INTEGRAL_SAMPLER_DESCRIPTOR` duplicated `SAMPLER_LINEAR_UPSCALE` (linear,
|
|
436
|
+
clamped on every axis), which the draw already binds as `sLinear` for the split-sum LUT. The integral
|
|
437
|
+
samples through that; one binding fewer in the view group.
|
|
438
|
+
- *The draw loop.* `material_draw` existed privately in both orchestrators. It is
|
|
439
|
+
`rasterize/native/material_draw_indirect.js` now, with the material bind-group layout it needs,
|
|
440
|
+
imported by both.
|
|
441
|
+
|
|
442
|
+
**Convention.** 650 of the engine's ~700 chunk files hold exactly one function named after the file;
|
|
443
|
+
mine held two to six. Five chunks became seventeen (`chunk_avboit_pack_counts`, `_unpack_counts`,
|
|
444
|
+
`_transmittance_to_extinction`, `_extinction_to_transmittance`, `_extinction_to_counts`,
|
|
445
|
+
`_counts_to_extinction`, `_overflows`, `_occupancy_range`, `_occupancy_word_mask`, `_dilate_word`,
|
|
446
|
+
`_fold_pairs`, `_warp`, `_view_depth_to_virtual`, `_view_depth_to_physical`,
|
|
447
|
+
`_integral_depth_coordinate`, `_sample_transmittance`, `_transmission_tint`), each with its spec; the
|
|
448
|
+
hand-built warp and the curve's inverse the specs share live in `avboit_spec_support.js`, test-only.
|
|
449
|
+
|
|
450
|
+
**Kept, on purpose.** `avboit_draw_fragment_body.js` shares one WGSL text between the IBL and Brick4
|
|
451
|
+
draw pipelines where MBOIT duplicates its resolve body. No precedent either way in the tree; two
|
|
452
|
+
45-line bodies drifting apart is the worse failure, and the two files stay concrete modules with no
|
|
453
|
+
conditionals in them.
|
|
454
|
+
|
|
455
|
+
**Also checked and found conforming:** the manual frame-graph passes match the MBOIT orchestrator's
|
|
456
|
+
shape; `AVBOITSettings` on `Renderer.avboit` matches the other sub-object settings; the settings
|
|
457
|
+
structs defined beside their shaders match `shader_read_out_bucket`; the bit packing uses WGSL's
|
|
458
|
+
`insertBits`/`extractBits` directly, which is what the engine's `bitfield_*` chunks are thin proxies
|
|
459
|
+
for; no `safe_*` math helper was reinvented — the one floor (`max(T, 1e-6)` before a log) has no
|
|
460
|
+
counterpart in `shader/chunk/math/`.
|
|
461
|
+
|
|
462
|
+
**Verified.** Every spec in `rasterize/` (the avboit directory's now-larger set, MBOIT's, the shared
|
|
463
|
+
bucket code) and the type check.
|