@photivra/engine 2.9.0 → 2.11.1
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/CHANGELOG.md +223 -1
- package/CITATION.cff +1 -1
- package/NODE_SUPPORT.md +21 -3
- package/README.md +6 -6
- package/SECURITY.md +1 -1
- package/THIRD_PARTY.md +2 -0
- package/dist/capture/native-environment-lean.js +89 -5
- package/dist/capture/native-environment-lean.js.map +1 -1
- package/dist/capture/native-environment-tabulated.d.ts +37 -0
- package/dist/capture/native-environment-tabulated.js +42 -6
- package/dist/capture/native-environment-tabulated.js.map +1 -1
- package/dist/core/portable-math.d.ts +19 -0
- package/dist/core/portable-math.js +82 -0
- package/dist/core/portable-math.js.map +1 -1
- package/dist/core/version.d.ts +1 -1
- package/dist/core/version.js +1 -1
- package/dist/core/version.js.map +1 -1
- package/dist/facade/channel-response.d.ts +15 -0
- package/dist/facade/channel-response.js +33 -0
- package/dist/facade/channel-response.js.map +1 -0
- package/dist/facade/chart-material-layers.d.ts +115 -0
- package/dist/facade/chart-material-layers.js +224 -0
- package/dist/facade/chart-material-layers.js.map +1 -0
- package/dist/facade/chart-radiance-batch.d.ts +8 -2
- package/dist/facade/chart-radiance-batch.js +11 -4
- package/dist/facade/chart-radiance-batch.js.map +1 -1
- package/dist/facade/chart-scenes.d.ts +134 -0
- package/dist/facade/chart-scenes.js +32 -22
- package/dist/facade/chart-scenes.js.map +1 -1
- package/dist/facade/diffraction-provider.d.ts +8 -2
- package/dist/facade/diffraction-provider.js +120 -16
- package/dist/facade/diffraction-provider.js.map +1 -1
- package/dist/facade/display-view-gpu-session.d.ts +18 -0
- package/dist/facade/display-view-gpu-session.js +280 -0
- package/dist/facade/display-view-gpu-session.js.map +1 -0
- package/dist/facade/display-view-gpu.d.ts +146 -0
- package/dist/facade/display-view-gpu.js +59 -0
- package/dist/facade/display-view-gpu.js.map +1 -0
- package/dist/facade/display-view.d.ts +123 -1
- package/dist/facade/display-view.js +478 -116
- package/dist/facade/display-view.js.map +1 -1
- package/dist/facade/expected-signal-setup.d.ts +5 -0
- package/dist/facade/expected-signal-setup.js +18 -1
- package/dist/facade/expected-signal-setup.js.map +1 -1
- package/dist/facade/expected-signal.d.ts +80 -6
- package/dist/facade/expected-signal.js +200 -24
- package/dist/facade/expected-signal.js.map +1 -1
- package/dist/facade/scene-radiance.d.ts +4 -2
- package/dist/facade/scene-radiance.js +7 -4
- package/dist/facade/scene-radiance.js.map +1 -1
- package/dist/facade/simulate-capture.d.ts +81 -7
- package/dist/facade/simulate-capture.js +276 -53
- package/dist/facade/simulate-capture.js.map +1 -1
- package/dist/facade/simulate-display-view.d.ts +54 -3
- package/dist/facade/simulate-display-view.js +120 -27
- package/dist/facade/simulate-display-view.js.map +1 -1
- package/dist/gpu/chart-composite-gpu.d.ts +119 -0
- package/dist/gpu/chart-composite-gpu.js +572 -0
- package/dist/gpu/chart-composite-gpu.js.map +1 -0
- package/dist/gpu/chart-radiance-gpu.d.ts +6 -6
- package/dist/gpu/chart-radiance-gpu.js +9 -11
- package/dist/gpu/chart-radiance-gpu.js.map +1 -1
- package/dist/gpu/display-view-chart-material-module.d.ts +10 -0
- package/dist/gpu/display-view-chart-material-module.js +58 -0
- package/dist/gpu/display-view-chart-material-module.js.map +1 -0
- package/dist/gpu/display-view-composite-material-module.d.ts +68 -0
- package/dist/gpu/display-view-composite-material-module.js +248 -0
- package/dist/gpu/display-view-composite-material-module.js.map +1 -0
- package/dist/gpu/display-view-quality-gpu.d.ts +185 -0
- package/dist/gpu/display-view-quality-gpu.js +1000 -0
- package/dist/gpu/display-view-quality-gpu.js.map +1 -0
- package/dist/gpu/display-view-speed-gpu.d.ts +156 -0
- package/dist/gpu/display-view-speed-gpu.js +524 -0
- package/dist/gpu/display-view-speed-gpu.js.map +1 -0
- package/dist/gpu/image-kernel-accelerator.d.ts +23 -4
- package/dist/gpu/image-kernel-accelerator.js +58 -10
- package/dist/gpu/image-kernel-accelerator.js.map +1 -1
- package/dist/gpu/image-kernel-execution.d.ts +60 -3
- package/dist/gpu/image-kernel-execution.js +2 -2
- package/dist/gpu/image-kernel-execution.js.map +1 -1
- package/dist/gpu/sensor-stage-gpu.d.ts +11 -0
- package/dist/gpu/sensor-stage-gpu.js +42 -13
- package/dist/gpu/sensor-stage-gpu.js.map +1 -1
- package/dist/gpu/webgpu-runtime.d.ts +4 -2
- package/dist/gpu/webgpu-runtime.js +13 -4
- package/dist/gpu/webgpu-runtime.js.map +1 -1
- package/dist/gpu/webgpu-switch.d.ts +4 -0
- package/dist/gpu/webgpu-switch.js +29 -0
- package/dist/gpu/webgpu-switch.js.map +1 -0
- package/dist/gpu/wgsl-chart-composite-kernel.d.ts +52 -0
- package/dist/gpu/wgsl-chart-composite-kernel.js +515 -0
- package/dist/gpu/wgsl-chart-composite-kernel.js.map +1 -0
- package/dist/gpu/wgsl-chart-kernel.d.ts +11 -9
- package/dist/gpu/wgsl-chart-kernel.js +33 -27
- package/dist/gpu/wgsl-chart-kernel.js.map +1 -1
- package/dist/gpu/wgsl-display-view-quality-kernel.d.ts +95 -0
- package/dist/gpu/wgsl-display-view-quality-kernel.js +769 -0
- package/dist/gpu/wgsl-display-view-quality-kernel.js.map +1 -0
- package/dist/gpu/wgsl-display-view-speed-kernel.d.ts +75 -0
- package/dist/gpu/wgsl-display-view-speed-kernel.js +386 -0
- package/dist/gpu/wgsl-display-view-speed-kernel.js.map +1 -0
- package/dist/gpu/wgsl-sensor-kernel.d.ts +28 -9
- package/dist/gpu/wgsl-sensor-kernel.js +94 -50
- package/dist/gpu/wgsl-sensor-kernel.js.map +1 -1
- package/dist/index.d.ts +6 -3
- package/dist/index.js +4 -2
- package/dist/index.js.map +1 -1
- package/dist/optics/defocus-disc-kernel.d.ts +61 -0
- package/dist/optics/defocus-disc-kernel.js +191 -0
- package/dist/optics/defocus-disc-kernel.js.map +1 -0
- package/dist/optics/irradiance-convolution.d.ts +11 -0
- package/dist/optics/irradiance-convolution.js +64 -24
- package/dist/optics/irradiance-convolution.js.map +1 -1
- package/dist/optics/scene-to-sensor-irradiance.d.ts +13 -0
- package/dist/optics/scene-to-sensor-irradiance.js +58 -24
- package/dist/optics/scene-to-sensor-irradiance.js.map +1 -1
- package/dist/optics/sensor-aperture-rays.js +2 -1
- package/dist/optics/sensor-aperture-rays.js.map +1 -1
- package/dist/optics/sensor-environment-query.js +4 -3
- package/dist/optics/sensor-environment-query.js.map +1 -1
- package/dist/optics/site-diffraction-kernel.d.ts +16 -0
- package/dist/optics/site-diffraction-kernel.js +60 -8
- package/dist/optics/site-diffraction-kernel.js.map +1 -1
- package/dist/processing/demosaic.js +114 -37
- package/dist/processing/demosaic.js.map +1 -1
- package/dist/schema/scene-radiance.d.ts +8 -0
- package/dist/schema/scene-radiance.js +28 -17
- package/dist/schema/scene-radiance.js.map +1 -1
- package/dist/sensor/environment-photo-signal.d.ts +21 -0
- package/dist/sensor/environment-photo-signal.js +68 -2
- package/dist/sensor/environment-photo-signal.js.map +1 -1
- package/docs/API_REFERENCE.md +60 -1
- package/docs/QUICK_START.md +120 -11
- package/docs/USAGE.md +82 -2
- package/docs/api/core-version.md +1 -1
- package/docs/api/exports.json +157 -2
- package/docs/api/facade-chart-material-layers.md +137 -0
- package/docs/api/facade-chart-radiance-batch.md +7 -2
- package/docs/api/facade-display-view-gpu.md +94 -0
- package/docs/api/facade-display-view.md +50 -0
- package/docs/api/facade-scene-radiance.md +3 -2
- package/docs/api/facade-simulate-capture.md +81 -12
- package/docs/api/facade-simulate-display-view.md +39 -2
- package/docs/api/gpu-image-kernel-accelerator.md +10 -3
- package/docs/api/gpu-image-kernel-execution.md +76 -4
- package/docs/api/gpu-webgpu-switch.md +23 -0
- package/package.json +3 -1
|
@@ -5,15 +5,17 @@
|
|
|
5
5
|
*
|
|
6
6
|
* Every classification decision (half-plane, disk, sector, cell and gap boundaries) is evaluated with an
|
|
7
7
|
* a-priori bound on how far the float32 decision value can be from the float64 reference value: the
|
|
8
|
-
* point's float32 rounding, each float32 operation (
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
* once to float32.
|
|
8
|
+
* point's float32 rounding, each float32 operation (E = 2^-23: one WGSL rounding of + − ×, in either
|
|
9
|
+
* direction, W3C WGSL "Floating Point Accuracy"), the float32 rounding of the constants and the
|
|
10
|
+
* reference's own float64 rounding. A point whose decision value lies within four times its bound is
|
|
11
|
+
* flagged, and the host recomputes it with the reference. So every unflagged point has exactly the
|
|
12
|
+
* reference's material, and its value is the reference value rounded once to float32.
|
|
13
13
|
*
|
|
14
|
-
* Siemens-star sectors are
|
|
15
|
-
*
|
|
16
|
-
*
|
|
14
|
+
* Siemens-star sectors are decided only by the signs of cross products with the sector boundary
|
|
15
|
+
* directions (host float64 cos/sin, rounded once to float32): the sign of v chooses the half-plane, a
|
|
16
|
+
* binary search over that half-plane's boundaries finds the sector, and the two enclosing cross products
|
|
17
|
+
* carry the bound. The kernel calls no transcendental function, so neither the WGSL `atan2` bound nor the
|
|
18
|
+
* realm's float64 `Math.atan2` enters. See docs/WEBGPU.md, "Scene radiance on the GPU".
|
|
17
19
|
*/
|
|
18
20
|
export declare const CHART_WORKGROUP_SIZE = 256;
|
|
19
21
|
/** Preset codes of `Params.preset`, in `CHART_SCENE_PRESETS` order. */
|
|
@@ -27,4 +29,4 @@ export declare const CHART_PRESET_CODE: Readonly<{
|
|
|
27
29
|
readonly "point-grid": 6;
|
|
28
30
|
readonly patches: 7;
|
|
29
31
|
}>;
|
|
30
|
-
export declare const CHART_RADIANCE_WGSL = "\nconst
|
|
32
|
+
export declare const CHART_RADIANCE_WGSL = "\nconst E: f32 = 1.1920928955078125e-7;\nconst SAFETY: f32 = 4.0;\nconst SLACK: f32 = 1e-14;\nstruct Params {\n count: u32, columns: u32, preset: u32, na: u32,\n nb: u32, nc: u32, wavelength_mode: u32, pad0: u32,\n f0: f32, f1: f32, f2: f32, f3: f32,\n f4: f32, f5: f32, f6: f32, f7: f32,\n};\n@group(0) @binding(0) var<uniform> params: Params;\n@group(0) @binding(1) var<storage, read> points: array<vec2<f32>>;\n@group(0) @binding(2) var<storage, read> table: array<f32>;\n@group(0) @binding(3) var<storage, read> aux: array<f32>;\n@group(0) @binding(4) var<storage, read> selectors: array<u32>;\n@group(0) @binding(5) var<storage, read_write> values: array<f32>;\n@group(0) @binding(6) var<storage, read_write> flags: array<u32>;\n\nvar<private> flagged: bool;\nfn check(distance: f32, bound: f32) { if (!(abs(distance) > SAFETY * bound)) { flagged = true; } }\n// Flags t when it lies within the bound of an integer n with lo <= n <= hi.\nfn check_integer(t: f32, bound: f32, lo: f32, hi: f32) {\n let n = round(t);\n if (SAFETY * bound >= 0.5) { flagged = true; return; }\n if (n >= lo && n <= hi) { check(t - n, bound); }\n}\n// x = (a + b) * scale, with the bound of its distance from the float64 reference value.\nfn scaled_sum_bound(a: f32, b: f32, s: f32, scale: f32, x: f32, constant_rounding: f32) -> f32 {\n return E * ((abs(a) + abs(b) + abs(s)) * abs(scale) + (1.0 + constant_rounding) * abs(x)) + SLACK * (abs(x) + 1.0);\n}\nfn cross_d(u: f32, v: f32, k: u32) -> f32 { return v * aux[2u * k] - u * aux[2u * k + 1u]; }\nfn cross_bound(u: f32, v: f32, k: u32, d: f32) -> f32 {\n return E * (3.0 * abs(v * aux[2u * k]) + 3.0 * abs(u * aux[2u * k + 1u]) + abs(d)) + SLACK * (abs(u) + abs(v));\n}\nfn material(p: vec2<f32>) -> u32 {\n let u = p.x;\n let v = p.y;\n switch (params.preset) {\n case 0u: { return 0u; }\n case 1u: {\n // Light where u\u00B7cos t \u2212 v\u00B7sin t \u2265 0.\n let a = u * params.f0;\n let b = v * params.f1;\n let d = a - b;\n check(d, E * (3.0 * abs(a) + 3.0 * abs(b) + abs(d)) + SLACK * (abs(u) + abs(v)));\n return select(0u, 1u, d >= 0.0);\n }\n case 2u: {\n let uu = u * u;\n let vv = v * v;\n let q = uu + vv;\n check(q - params.f0, E * (4.0 * q + 2.0 * params.f0) + SLACK * (q + params.f0));\n if (q > params.f0) { return 2u; }\n // Boundaries b_k = \u03C0(k/cycles \u2212 1), k = 0 \u2026 2\u00B7cycles. In the half-plane v \u2265 0 the angle lies in [0, \u03C0],\n // between b_cycles and b_2cycles; for v < 0 in [\u2212\u03C0, 0), between b_0 and b_cycles. Within one half-plane,\n // sign(cross(k)) = sign(\u03B8 \u2212 b_k), so a binary search on the cross-product signs finds the sector. A point\n // within the bound of a boundary (the u-axis and the \u00B1\u03C0 cut included) is caught by the two final checks.\n var low = select(0u, params.na, v >= 0.0);\n var high = low + params.na;\n loop {\n if (high - low <= 1u) { break; }\n let middle = (low + high) >> 1u;\n if (cross_d(u, v, middle) >= 0.0) { low = middle; } else { high = middle; }\n }\n let k = low;\n let lo = cross_d(u, v, k);\n let hi = cross_d(u, v, k + 1u);\n if (lo < 0.0 || hi >= 0.0) { flagged = true; }\n check(lo, cross_bound(u, v, k, lo));\n check(hi, cross_bound(u, v, k + 1u, hi));\n return select(0u, 1u, k % 2u == 0u);\n }\n case 3u, 4u: {\n let s = u + 1.0;\n let t = s * params.f0;\n check_integer(t, scaled_sum_bound(u, 1.0, s, params.f0, t, 0.0), 1.0, f32(params.na - 1u));\n return u32(clamp(floor(t), 0.0, f32(params.na - 1u)));\n }\n case 5u: {\n let s = u + 1.0;\n let x = s * params.f0;\n let w = params.f1 - v;\n let y = w * params.f0;\n check_integer(x, scaled_sum_bound(u, 1.0, s, params.f0, x, 0.0), -3.0e38, 3.0e38);\n check_integer(y, scaled_sum_bound(params.f1, v, w, params.f0, y, 0.0), -3.0e38, 3.0e38);\n let i = i32(floor(x));\n let j = i32(floor(y));\n return select(0u, 1u, (i + j) % 2 == 0);\n }\n case 6u: {\n let s = u + 1.0;\n let x = s * params.f0;\n let w = params.f1 - v;\n let y = w * params.f2;\n check_integer(x, scaled_sum_bound(u, 1.0, s, params.f0, x, 0.0), 1.0, f32(params.na - 1u));\n check_integer(y, scaled_sum_bound(params.f1, v, w, params.f2, y, 1.0), 1.0, f32(params.nb - 1u));\n let i = u32(clamp(floor(x), 0.0, f32(params.na - 1u)));\n let j = u32(clamp(floor(y), 0.0, f32(params.nb - 1u)));\n let c = j * params.na + i;\n let dx = u - aux[2u * c];\n let dy = v - aux[2u * c + 1u];\n let ex = E * (abs(u) + abs(aux[2u * c]) + abs(dx));\n let ey = E * (abs(v) + abs(aux[2u * c + 1u]) + abs(dy));\n let d2 = dx * dx + dy * dy;\n check(d2 - params.f3, 2.0 * (abs(dx) * ex + abs(dy) * ey) + ex * ex + ey * ey + E * (2.0 * d2 + params.f3) + SLACK * (d2 + params.f3));\n return select(0u, 1u, d2 <= params.f3);\n }\n default: {\n let s = u + 1.0;\n let fx = s * params.f0;\n let w = params.f1 - v;\n let fy = w * params.f2;\n let bx = scaled_sum_bound(u, 1.0, s, params.f0, fx, 0.0);\n let by = scaled_sum_bound(params.f1, v, w, params.f2, fy, 1.0);\n check_integer(fx, bx, -3.0e38, 3.0e38);\n check_integer(fy, by, -3.0e38, 3.0e38);\n let background = params.nc;\n let ifx = floor(fx);\n let ify = floor(fy);\n if (ifx < 0.0 || ify < 0.0 || ifx >= f32(params.na) || ify >= f32(params.nb)) { return background; }\n let index = u32(ify) * params.na + u32(ifx);\n let x = fx - ifx;\n let y = fy - ify;\n check(x - params.f3, bx + E * params.f3);\n check(x - params.f4, bx + E * params.f4);\n check(y - params.f3, by + E * params.f3);\n check(y - params.f4, by + E * params.f4);\n if (index >= params.nc || x < params.f3 || x > params.f4 || y < params.f3 || y > params.f4) { return background; }\n return index;\n }\n }\n}\n@compute @workgroup_size(256)\nfn main(@builtin(global_invocation_id) gid: vec3<u32>, @builtin(num_workgroups) groups: vec3<u32>) {\n let i = gid.x + gid.y * groups.x * 256u;\n if (i >= params.count) { return; }\n flagged = false;\n let m = material(points[i]);\n if (params.wavelength_mode != 0u) {\n values[i] = table[m * params.columns + selectors[i]];\n } else {\n for (var c = 0u; c < params.columns; c++) { values[i * params.columns + c] = table[m * params.columns + c]; }\n }\n flags[i] = select(0u, 1u, flagged);\n}\n";
|
|
@@ -6,25 +6,26 @@
|
|
|
6
6
|
*
|
|
7
7
|
* Every classification decision (half-plane, disk, sector, cell and gap boundaries) is evaluated with an
|
|
8
8
|
* a-priori bound on how far the float32 decision value can be from the float64 reference value: the
|
|
9
|
-
* point's float32 rounding, each float32 operation (
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
* once to float32.
|
|
9
|
+
* point's float32 rounding, each float32 operation (E = 2^-23: one WGSL rounding of + − ×, in either
|
|
10
|
+
* direction, W3C WGSL "Floating Point Accuracy"), the float32 rounding of the constants and the
|
|
11
|
+
* reference's own float64 rounding. A point whose decision value lies within four times its bound is
|
|
12
|
+
* flagged, and the host recomputes it with the reference. So every unflagged point has exactly the
|
|
13
|
+
* reference's material, and its value is the reference value rounded once to float32.
|
|
14
14
|
*
|
|
15
|
-
* Siemens-star sectors are
|
|
16
|
-
*
|
|
17
|
-
*
|
|
15
|
+
* Siemens-star sectors are decided only by the signs of cross products with the sector boundary
|
|
16
|
+
* directions (host float64 cos/sin, rounded once to float32): the sign of v chooses the half-plane, a
|
|
17
|
+
* binary search over that half-plane's boundaries finds the sector, and the two enclosing cross products
|
|
18
|
+
* carry the bound. The kernel calls no transcendental function, so neither the WGSL `atan2` bound nor the
|
|
19
|
+
* realm's float64 `Math.atan2` enters. See docs/WEBGPU.md, "Scene radiance on the GPU".
|
|
18
20
|
*/
|
|
19
21
|
export const CHART_WORKGROUP_SIZE = 256;
|
|
20
22
|
/** Preset codes of `Params.preset`, in `CHART_SCENE_PRESETS` order. */
|
|
21
23
|
export const CHART_PRESET_CODE = Object.freeze({ uniform: 0, "slanted-edge": 1, "siemens-star": 2, "step-wedge": 3, "grey-ramp": 4, checkerboard: 5,
|
|
22
24
|
"point-grid": 6, patches: 7 });
|
|
23
25
|
export const CHART_RADIANCE_WGSL = /* wgsl */ `
|
|
24
|
-
const
|
|
26
|
+
const E: f32 = 1.1920928955078125e-7;
|
|
25
27
|
const SAFETY: f32 = 4.0;
|
|
26
28
|
const SLACK: f32 = 1e-14;
|
|
27
|
-
const PI: f32 = 3.14159265358979;
|
|
28
29
|
struct Params {
|
|
29
30
|
count: u32, columns: u32, preset: u32, na: u32,
|
|
30
31
|
nb: u32, nc: u32, wavelength_mode: u32, pad0: u32,
|
|
@@ -49,11 +50,11 @@ fn check_integer(t: f32, bound: f32, lo: f32, hi: f32) {
|
|
|
49
50
|
}
|
|
50
51
|
// x = (a + b) * scale, with the bound of its distance from the float64 reference value.
|
|
51
52
|
fn scaled_sum_bound(a: f32, b: f32, s: f32, scale: f32, x: f32, constant_rounding: f32) -> f32 {
|
|
52
|
-
return
|
|
53
|
+
return E * ((abs(a) + abs(b) + abs(s)) * abs(scale) + (1.0 + constant_rounding) * abs(x)) + SLACK * (abs(x) + 1.0);
|
|
53
54
|
}
|
|
54
55
|
fn cross_d(u: f32, v: f32, k: u32) -> f32 { return v * aux[2u * k] - u * aux[2u * k + 1u]; }
|
|
55
56
|
fn cross_bound(u: f32, v: f32, k: u32, d: f32) -> f32 {
|
|
56
|
-
return
|
|
57
|
+
return E * (3.0 * abs(v * aux[2u * k]) + 3.0 * abs(u * aux[2u * k + 1u]) + abs(d)) + SLACK * (abs(u) + abs(v));
|
|
57
58
|
}
|
|
58
59
|
fn material(p: vec2<f32>) -> u32 {
|
|
59
60
|
let u = p.x;
|
|
@@ -65,22 +66,27 @@ fn material(p: vec2<f32>) -> u32 {
|
|
|
65
66
|
let a = u * params.f0;
|
|
66
67
|
let b = v * params.f1;
|
|
67
68
|
let d = a - b;
|
|
68
|
-
check(d,
|
|
69
|
+
check(d, E * (3.0 * abs(a) + 3.0 * abs(b) + abs(d)) + SLACK * (abs(u) + abs(v)));
|
|
69
70
|
return select(0u, 1u, d >= 0.0);
|
|
70
71
|
}
|
|
71
72
|
case 2u: {
|
|
72
73
|
let uu = u * u;
|
|
73
74
|
let vv = v * v;
|
|
74
75
|
let q = uu + vv;
|
|
75
|
-
check(q - params.f0,
|
|
76
|
+
check(q - params.f0, E * (4.0 * q + 2.0 * params.f0) + SLACK * (q + params.f0));
|
|
76
77
|
if (q > params.f0) { return 2u; }
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
78
|
+
// Boundaries b_k = π(k/cycles − 1), k = 0 … 2·cycles. In the half-plane v ≥ 0 the angle lies in [0, π],
|
|
79
|
+
// between b_cycles and b_2cycles; for v < 0 in [−π, 0), between b_0 and b_cycles. Within one half-plane,
|
|
80
|
+
// sign(cross(k)) = sign(θ − b_k), so a binary search on the cross-product signs finds the sector. A point
|
|
81
|
+
// within the bound of a boundary (the u-axis and the ±π cut included) is caught by the two final checks.
|
|
82
|
+
var low = select(0u, params.na, v >= 0.0);
|
|
83
|
+
var high = low + params.na;
|
|
84
|
+
loop {
|
|
85
|
+
if (high - low <= 1u) { break; }
|
|
86
|
+
let middle = (low + high) >> 1u;
|
|
87
|
+
if (cross_d(u, v, middle) >= 0.0) { low = middle; } else { high = middle; }
|
|
83
88
|
}
|
|
89
|
+
let k = low;
|
|
84
90
|
let lo = cross_d(u, v, k);
|
|
85
91
|
let hi = cross_d(u, v, k + 1u);
|
|
86
92
|
if (lo < 0.0 || hi >= 0.0) { flagged = true; }
|
|
@@ -117,10 +123,10 @@ fn material(p: vec2<f32>) -> u32 {
|
|
|
117
123
|
let c = j * params.na + i;
|
|
118
124
|
let dx = u - aux[2u * c];
|
|
119
125
|
let dy = v - aux[2u * c + 1u];
|
|
120
|
-
let ex =
|
|
121
|
-
let ey =
|
|
126
|
+
let ex = E * (abs(u) + abs(aux[2u * c]) + abs(dx));
|
|
127
|
+
let ey = E * (abs(v) + abs(aux[2u * c + 1u]) + abs(dy));
|
|
122
128
|
let d2 = dx * dx + dy * dy;
|
|
123
|
-
check(d2 - params.f3, 2.0 * (abs(dx) * ex + abs(dy) * ey) + ex * ex + ey * ey +
|
|
129
|
+
check(d2 - params.f3, 2.0 * (abs(dx) * ex + abs(dy) * ey) + ex * ex + ey * ey + E * (2.0 * d2 + params.f3) + SLACK * (d2 + params.f3));
|
|
124
130
|
return select(0u, 1u, d2 <= params.f3);
|
|
125
131
|
}
|
|
126
132
|
default: {
|
|
@@ -139,10 +145,10 @@ fn material(p: vec2<f32>) -> u32 {
|
|
|
139
145
|
let index = u32(ify) * params.na + u32(ifx);
|
|
140
146
|
let x = fx - ifx;
|
|
141
147
|
let y = fy - ify;
|
|
142
|
-
check(x - params.f3, bx +
|
|
143
|
-
check(x - params.f4, bx +
|
|
144
|
-
check(y - params.f3, by +
|
|
145
|
-
check(y - params.f4, by +
|
|
148
|
+
check(x - params.f3, bx + E * params.f3);
|
|
149
|
+
check(x - params.f4, bx + E * params.f4);
|
|
150
|
+
check(y - params.f3, by + E * params.f3);
|
|
151
|
+
check(y - params.f4, by + E * params.f4);
|
|
146
152
|
if (index >= params.nc || x < params.f3 || x > params.f4 || y < params.f3 || y > params.f4) { return background; }
|
|
147
153
|
return index;
|
|
148
154
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"wgsl-chart-kernel.js","sourceRoot":"","sources":["../../src/gpu/wgsl-chart-kernel.ts"],"names":[],"mappings":"AAAA,sCAAsC;AAEtC
|
|
1
|
+
{"version":3,"file":"wgsl-chart-kernel.js","sourceRoot":"","sources":["../../src/gpu/wgsl-chart-kernel.ts"],"names":[],"mappings":"AAAA,sCAAsC;AAEtC;;;;;;;;;;;;;;;;;;GAkBG;AAEH,MAAM,CAAC,MAAM,oBAAoB,GAAG,GAAG,CAAC;AACxC,uEAAuE;AACvE,MAAM,CAAC,MAAM,iBAAiB,GAAG,MAAM,CAAC,MAAM,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,cAAc,EAAE,CAAC,EAAE,cAAc,EAAE,CAAC,EAAE,YAAY,EAAE,CAAC,EAAE,WAAW,EAAE,CAAC,EAAE,YAAY,EAAE,CAAC;IACjJ,YAAY,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,EAAW,CAAC,CAAC;AAE1C,MAAM,CAAC,MAAM,mBAAmB,GAAG,UAAU,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;2BAoInB,oBAAoB;;uCAER,oBAAoB;;;;;;;;;;;CAW1D,CAAC","sourcesContent":["// SPDX-License-Identifier: Apache-2.0\n\n/**\n * WGSL kernel for the `webgpu-f32` chart-radiance stage (#277), one invocation per query point. It\n * evaluates the chart's material classification (`resolveChartScene().materialAt`) in float32 and looks up\n * the material's value in a table the host computed in float64 with the reference's own operations.\n *\n * Every classification decision (half-plane, disk, sector, cell and gap boundaries) is evaluated with an\n * a-priori bound on how far the float32 decision value can be from the float64 reference value: the\n * point's float32 rounding, each float32 operation (E = 2^-23: one WGSL rounding of + − ×, in either\n * direction, W3C WGSL \"Floating Point Accuracy\"), the float32 rounding of the constants and the\n * reference's own float64 rounding. A point whose decision value lies within four times its bound is\n * flagged, and the host recomputes it with the reference. So every unflagged point has exactly the\n * reference's material, and its value is the reference value rounded once to float32.\n *\n * Siemens-star sectors are decided only by the signs of cross products with the sector boundary\n * directions (host float64 cos/sin, rounded once to float32): the sign of v chooses the half-plane, a\n * binary search over that half-plane's boundaries finds the sector, and the two enclosing cross products\n * carry the bound. The kernel calls no transcendental function, so neither the WGSL `atan2` bound nor the\n * realm's float64 `Math.atan2` enters. See docs/WEBGPU.md, \"Scene radiance on the GPU\".\n */\n\nexport const CHART_WORKGROUP_SIZE = 256;\n/** Preset codes of `Params.preset`, in `CHART_SCENE_PRESETS` order. */\nexport const CHART_PRESET_CODE = Object.freeze({ uniform: 0, \"slanted-edge\": 1, \"siemens-star\": 2, \"step-wedge\": 3, \"grey-ramp\": 4, checkerboard: 5,\n \"point-grid\": 6, patches: 7 } as const);\n\nexport const CHART_RADIANCE_WGSL = /* wgsl */ `\nconst E: f32 = 1.1920928955078125e-7;\nconst SAFETY: f32 = 4.0;\nconst SLACK: f32 = 1e-14;\nstruct Params {\n count: u32, columns: u32, preset: u32, na: u32,\n nb: u32, nc: u32, wavelength_mode: u32, pad0: u32,\n f0: f32, f1: f32, f2: f32, f3: f32,\n f4: f32, f5: f32, f6: f32, f7: f32,\n};\n@group(0) @binding(0) var<uniform> params: Params;\n@group(0) @binding(1) var<storage, read> points: array<vec2<f32>>;\n@group(0) @binding(2) var<storage, read> table: array<f32>;\n@group(0) @binding(3) var<storage, read> aux: array<f32>;\n@group(0) @binding(4) var<storage, read> selectors: array<u32>;\n@group(0) @binding(5) var<storage, read_write> values: array<f32>;\n@group(0) @binding(6) var<storage, read_write> flags: array<u32>;\n\nvar<private> flagged: bool;\nfn check(distance: f32, bound: f32) { if (!(abs(distance) > SAFETY * bound)) { flagged = true; } }\n// Flags t when it lies within the bound of an integer n with lo <= n <= hi.\nfn check_integer(t: f32, bound: f32, lo: f32, hi: f32) {\n let n = round(t);\n if (SAFETY * bound >= 0.5) { flagged = true; return; }\n if (n >= lo && n <= hi) { check(t - n, bound); }\n}\n// x = (a + b) * scale, with the bound of its distance from the float64 reference value.\nfn scaled_sum_bound(a: f32, b: f32, s: f32, scale: f32, x: f32, constant_rounding: f32) -> f32 {\n return E * ((abs(a) + abs(b) + abs(s)) * abs(scale) + (1.0 + constant_rounding) * abs(x)) + SLACK * (abs(x) + 1.0);\n}\nfn cross_d(u: f32, v: f32, k: u32) -> f32 { return v * aux[2u * k] - u * aux[2u * k + 1u]; }\nfn cross_bound(u: f32, v: f32, k: u32, d: f32) -> f32 {\n return E * (3.0 * abs(v * aux[2u * k]) + 3.0 * abs(u * aux[2u * k + 1u]) + abs(d)) + SLACK * (abs(u) + abs(v));\n}\nfn material(p: vec2<f32>) -> u32 {\n let u = p.x;\n let v = p.y;\n switch (params.preset) {\n case 0u: { return 0u; }\n case 1u: {\n // Light where u·cos t − v·sin t ≥ 0.\n let a = u * params.f0;\n let b = v * params.f1;\n let d = a - b;\n check(d, E * (3.0 * abs(a) + 3.0 * abs(b) + abs(d)) + SLACK * (abs(u) + abs(v)));\n return select(0u, 1u, d >= 0.0);\n }\n case 2u: {\n let uu = u * u;\n let vv = v * v;\n let q = uu + vv;\n check(q - params.f0, E * (4.0 * q + 2.0 * params.f0) + SLACK * (q + params.f0));\n if (q > params.f0) { return 2u; }\n // Boundaries b_k = π(k/cycles − 1), k = 0 … 2·cycles. In the half-plane v ≥ 0 the angle lies in [0, π],\n // between b_cycles and b_2cycles; for v < 0 in [−π, 0), between b_0 and b_cycles. Within one half-plane,\n // sign(cross(k)) = sign(θ − b_k), so a binary search on the cross-product signs finds the sector. A point\n // within the bound of a boundary (the u-axis and the ±π cut included) is caught by the two final checks.\n var low = select(0u, params.na, v >= 0.0);\n var high = low + params.na;\n loop {\n if (high - low <= 1u) { break; }\n let middle = (low + high) >> 1u;\n if (cross_d(u, v, middle) >= 0.0) { low = middle; } else { high = middle; }\n }\n let k = low;\n let lo = cross_d(u, v, k);\n let hi = cross_d(u, v, k + 1u);\n if (lo < 0.0 || hi >= 0.0) { flagged = true; }\n check(lo, cross_bound(u, v, k, lo));\n check(hi, cross_bound(u, v, k + 1u, hi));\n return select(0u, 1u, k % 2u == 0u);\n }\n case 3u, 4u: {\n let s = u + 1.0;\n let t = s * params.f0;\n check_integer(t, scaled_sum_bound(u, 1.0, s, params.f0, t, 0.0), 1.0, f32(params.na - 1u));\n return u32(clamp(floor(t), 0.0, f32(params.na - 1u)));\n }\n case 5u: {\n let s = u + 1.0;\n let x = s * params.f0;\n let w = params.f1 - v;\n let y = w * params.f0;\n check_integer(x, scaled_sum_bound(u, 1.0, s, params.f0, x, 0.0), -3.0e38, 3.0e38);\n check_integer(y, scaled_sum_bound(params.f1, v, w, params.f0, y, 0.0), -3.0e38, 3.0e38);\n let i = i32(floor(x));\n let j = i32(floor(y));\n return select(0u, 1u, (i + j) % 2 == 0);\n }\n case 6u: {\n let s = u + 1.0;\n let x = s * params.f0;\n let w = params.f1 - v;\n let y = w * params.f2;\n check_integer(x, scaled_sum_bound(u, 1.0, s, params.f0, x, 0.0), 1.0, f32(params.na - 1u));\n check_integer(y, scaled_sum_bound(params.f1, v, w, params.f2, y, 1.0), 1.0, f32(params.nb - 1u));\n let i = u32(clamp(floor(x), 0.0, f32(params.na - 1u)));\n let j = u32(clamp(floor(y), 0.0, f32(params.nb - 1u)));\n let c = j * params.na + i;\n let dx = u - aux[2u * c];\n let dy = v - aux[2u * c + 1u];\n let ex = E * (abs(u) + abs(aux[2u * c]) + abs(dx));\n let ey = E * (abs(v) + abs(aux[2u * c + 1u]) + abs(dy));\n let d2 = dx * dx + dy * dy;\n check(d2 - params.f3, 2.0 * (abs(dx) * ex + abs(dy) * ey) + ex * ex + ey * ey + E * (2.0 * d2 + params.f3) + SLACK * (d2 + params.f3));\n return select(0u, 1u, d2 <= params.f3);\n }\n default: {\n let s = u + 1.0;\n let fx = s * params.f0;\n let w = params.f1 - v;\n let fy = w * params.f2;\n let bx = scaled_sum_bound(u, 1.0, s, params.f0, fx, 0.0);\n let by = scaled_sum_bound(params.f1, v, w, params.f2, fy, 1.0);\n check_integer(fx, bx, -3.0e38, 3.0e38);\n check_integer(fy, by, -3.0e38, 3.0e38);\n let background = params.nc;\n let ifx = floor(fx);\n let ify = floor(fy);\n if (ifx < 0.0 || ify < 0.0 || ifx >= f32(params.na) || ify >= f32(params.nb)) { return background; }\n let index = u32(ify) * params.na + u32(ifx);\n let x = fx - ifx;\n let y = fy - ify;\n check(x - params.f3, bx + E * params.f3);\n check(x - params.f4, bx + E * params.f4);\n check(y - params.f3, by + E * params.f3);\n check(y - params.f4, by + E * params.f4);\n if (index >= params.nc || x < params.f3 || x > params.f4 || y < params.f3 || y > params.f4) { return background; }\n return index;\n }\n }\n}\n@compute @workgroup_size(${CHART_WORKGROUP_SIZE})\nfn main(@builtin(global_invocation_id) gid: vec3<u32>, @builtin(num_workgroups) groups: vec3<u32>) {\n let i = gid.x + gid.y * groups.x * ${CHART_WORKGROUP_SIZE}u;\n if (i >= params.count) { return; }\n flagged = false;\n let m = material(points[i]);\n if (params.wavelength_mode != 0u) {\n values[i] = table[m * params.columns + selectors[i]];\n } else {\n for (var c = 0u; c < params.columns; c++) { values[i * params.columns + c] = table[m * params.columns + c]; }\n }\n flags[i] = select(0u, 1u, flagged);\n}\n`;\n"]}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
export declare const DISPLAY_VIEW_QUALITY_WORKGROUP_SIZE = 64;
|
|
2
|
+
/** Largest footprint per axis (real pixels), as the Speed kernel. */
|
|
3
|
+
export declare const DISPLAY_VIEW_QUALITY_MAXIMUM_PIXELS_PER_AXIS = 6;
|
|
4
|
+
/** Uniform parameter words (see `Params`): 56 words, 224 bytes. */
|
|
5
|
+
export declare const DISPLAY_VIEW_QUALITY_PARAM_WORDS = 56;
|
|
6
|
+
/** Words per lattice-group record: lx0, ly0, lw, lh, lbase, first tap (the blocked site pass, #492), cu (f32), cv (f32). */
|
|
7
|
+
export declare const DISPLAY_VIEW_QUALITY_GROUP_WORDS = 8;
|
|
8
|
+
/** Words per tap record: group, dp offset, dq offset, pad. */
|
|
9
|
+
export declare const DISPLAY_VIEW_QUALITY_TAP_WORDS = 4;
|
|
10
|
+
/** Floats per native site of the site buffer: mean, variance, third cumulant, their three bounds, flag (u32 bits), electrons. */
|
|
11
|
+
export declare const DISPLAY_VIEW_QUALITY_SITE_FLOATS = 8;
|
|
12
|
+
/** Floats per display pixel in the output buffer: m0 m1 m2, v00 v11 v22, c01 c02 c12, mean bound, variance bound (absolute). */
|
|
13
|
+
export declare const DISPLAY_VIEW_QUALITY_OUTPUT_FLOATS = 11;
|
|
14
|
+
/** Floats per column or row record of the geometry buffer (the Speed layout). */
|
|
15
|
+
export declare const DISPLAY_VIEW_QUALITY_GEOMETRY_STRIDE = 20;
|
|
16
|
+
/** Candidate table: entries per phase (the 7 × 7 window of AHD's reach 3) and floats per entry (dx, dy, dh0, dh1, dh2, dv0, dv1, dv2). */
|
|
17
|
+
export declare const DISPLAY_VIEW_QUALITY_CANDIDATE_ENTRIES = 49;
|
|
18
|
+
export declare const DISPLAY_VIEW_QUALITY_CANDIDATE_FLOATS = 8;
|
|
19
|
+
/** Layout of the aux table (float32 words). */
|
|
20
|
+
export declare const DISPLAY_VIEW_QUALITY_AUX: Readonly<{
|
|
21
|
+
/** Per phase p, channel c, tap t: dx, dy, weight, 0 (the bilinear kernels, 4 taps; unused taps have weight 0). */
|
|
22
|
+
bilinear: 0;
|
|
23
|
+
/** Per phase p, channel c: 1 / Σ_t w_t² of the bilinear kernel (host float64). */
|
|
24
|
+
inverseW2: 192;
|
|
25
|
+
/** 16 Gauss–Legendre nodes then 16 weights on [−1, 1]. */
|
|
26
|
+
gaussLegendre: 208;
|
|
27
|
+
/** Per phase: the entry count (as u32 bits) and 3 pad words, then the entries (dx, dy, dense weights of candidate 0 and 1 per channel). */
|
|
28
|
+
candidates: 240;
|
|
29
|
+
candidatePhaseStride: number;
|
|
30
|
+
size: number;
|
|
31
|
+
}>;
|
|
32
|
+
/** Pixel flag bits of the output flag word. */
|
|
33
|
+
export declare const DISPLAY_VIEW_QUALITY_FLAG: Readonly<{
|
|
34
|
+
/** A site the pixel read had an uncertain lattice material (recompute on the host). */
|
|
35
|
+
materialUncertain: 1;
|
|
36
|
+
/** A site the pixel read lies outside the band's site window (a host geometry error; recompute). */
|
|
37
|
+
siteOutsideWindow: 2;
|
|
38
|
+
/** The correction needed a grid state the table does not hold (recompute, or compute the state and dispatch again). */
|
|
39
|
+
stateMissing: 4;
|
|
40
|
+
/** A sample's pixel variance lies within its bound of the correction threshold (recompute). */
|
|
41
|
+
thresholdTie: 8;
|
|
42
|
+
/** Informational: the pixel's expected value includes the low-signal correction. */
|
|
43
|
+
corrected: 16;
|
|
44
|
+
/** The bits that require the host's recomputation. */
|
|
45
|
+
recompute: 15;
|
|
46
|
+
}>;
|
|
47
|
+
/** Words of the grid-key bitmap: one bit per grid key (AHD_KEY_LEVELS³ keys). */
|
|
48
|
+
export declare const DISPLAY_VIEW_QUALITY_BITMAP_WORDS: number;
|
|
49
|
+
/**
|
|
50
|
+
* A site whose uncertain lattice points (the material module's flag) could move its electrons by more than this fraction of
|
|
51
|
+
* their value is flagged for host recomputation; a smaller uncertainty enters the site's bound instead (the tap weights times
|
|
52
|
+
* the largest value difference between any two materials at that tap).
|
|
53
|
+
*/
|
|
54
|
+
export declare const DISPLAY_VIEW_QUALITY_UNCERTAINTY_LIMIT: number;
|
|
55
|
+
/** Bound constant of a clipped-normal slope Φ(z) (in units of E): z's 8E through φ·|z| ≤ 0.24 plus the Abramowitz–Stegun 147E. */
|
|
56
|
+
export declare const DISPLAY_VIEW_QUALITY_SLOPE_U = 200;
|
|
57
|
+
/**
|
|
58
|
+
* Pass 1: the material of every lattice point of every group (the module's `material_at`, its uncertainty in bit 31; the output
|
|
59
|
+
* is `lattice_out`, since a material module may declare its own `lattice`, as #491's composite module does). The group
|
|
60
|
+
* records hold each group's lattice window (lx0, ly0, lw, lh), its first index in the lattice buffer and the point constants
|
|
61
|
+
* c_u = 0.5 + f_x − W/2 and c_v = 0.5 + f_y − H/2 (exact in float32, checked by the host).
|
|
62
|
+
*/
|
|
63
|
+
export declare const DISPLAY_VIEW_QUALITY_LATTICE_WGSL: string;
|
|
64
|
+
/**
|
|
65
|
+
* Pass 2: the expected electrons, readout moments and white clip of every native site of the band's window, with their bounds.
|
|
66
|
+
* The site's electrons are Σ_t table[(m_t·3 + c)·T + t] + dark_c, every term non-negative and rounded once on the host, so the
|
|
67
|
+
* sum carries (T + 3)·E relative (`params.site_gamma`, the host's value). An uncertain lattice point adds the tap's value spread
|
|
68
|
+
* over the materials (`spread`, host float64) to the site's uncertainty, which enters its bounds, or flags the site when it
|
|
69
|
+
* exceeds UNCERTAINTY_LIMIT of the electrons. One dispatch covers the `params.count` sites from `params.site_first` (#492: the
|
|
70
|
+
* host splits a band's sites over several dispatches so that no dispatch gathers more than its tap budget).
|
|
71
|
+
*/
|
|
72
|
+
export declare const DISPLAY_VIEW_QUALITY_SITES_WGSL: string;
|
|
73
|
+
/**
|
|
74
|
+
* Pass 2 with the defocus disc (#492): the same site electrons from a factored table, summed in blocks.
|
|
75
|
+
*
|
|
76
|
+
* - **Factored table.** A neutral material's value is its reflectance times the white's combined kernel (the reference forms it
|
|
77
|
+
* so: reflectance[m]·neutral_c(t)), so the table holds one neutral row per channel and one row per spectral material, and
|
|
78
|
+
* `materials` holds per material its row (0 for neutral, 1 + s for spectral material s) and its reflectance as float32 bits
|
|
79
|
+
* (1 for a spectral material, an exact product). A scene of thousands of neutral materials (#491's composite: 4 143) then
|
|
80
|
+
* needs 3·T values instead of 3·T per material.
|
|
81
|
+
* - **Blocked sum.** Every group's taps form a dense cw × ch rectangle (the combined kernel's window, row-major from the group's
|
|
82
|
+
* first tap, `groups[g + 5]`), so a site sums each row of a group into a row partial, the rows into a group partial and the
|
|
83
|
+
* groups into the total. No tap records are read: the tap's lattice offset within its group is its (dx, dy).
|
|
84
|
+
*
|
|
85
|
+
* Every term is non-negative and carries two host roundings (u each, u = E/2: the row value and the reflectance), one product
|
|
86
|
+
* (E) and at most cw + ch + G additions, plus the dark electrons' addition, so the electrons carry (cw_max + ch_max + G + 3)·E
|
|
87
|
+
* relative (`params.site_gamma`; recursive summation of the up to 4·(2·96 + 2)² = 150 544 taps of a combined radius of 96 would
|
|
88
|
+
* carry (T + 3)·E ≈ 1.8 % instead). The uncertainty, summed the same way, is scaled by 1 + site_gamma, and `params.site_tiny`
|
|
89
|
+
* (host float64, rounded up) bounds the terms below the float32 normal range, which a device may flush to zero.
|
|
90
|
+
*/
|
|
91
|
+
export declare const DISPLAY_VIEW_QUALITY_SITES_BLOCKED_WGSL: string;
|
|
92
|
+
/** Pass 3: the display pixels (see the module comment). */
|
|
93
|
+
export declare const DISPLAY_VIEW_QUALITY_PIXELS_WGSL: string;
|
|
94
|
+
/** The lattice pass with a material module appended (`fn material_at(p: vec2<f32>) -> u32`, `var<private> material_uncertain: bool`). */
|
|
95
|
+
export declare function assembleDisplayViewQualityLatticeWgsl(materialModuleWgsl: string): string;
|