@yali-engine/render 0.0.0-stage → 0.4.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/dist/bvh.d.ts +66 -0
- package/dist/bvh.js +493 -0
- package/dist/bvh.js.map +1 -0
- package/dist/camera.d.ts +34 -0
- package/dist/camera.js +66 -0
- package/dist/camera.js.map +1 -0
- package/dist/cluster-build.d.ts +46 -0
- package/dist/cluster-build.js +179 -0
- package/dist/cluster-build.js.map +1 -0
- package/dist/cluster-pages.d.ts +70 -0
- package/dist/cluster-pages.js +203 -0
- package/dist/cluster-pages.js.map +1 -0
- package/dist/cluster-stream.d.ts +83 -0
- package/dist/cluster-stream.js +226 -0
- package/dist/cluster-stream.js.map +1 -0
- package/dist/culled-draws.d.ts +30 -0
- package/dist/culled-draws.js +103 -0
- package/dist/culled-draws.js.map +1 -0
- package/dist/debug-lines.d.ts +51 -0
- package/dist/debug-lines.js +158 -0
- package/dist/debug-lines.js.map +1 -0
- package/dist/device-profile.d.ts +100 -0
- package/dist/device-profile.js +131 -0
- package/dist/device-profile.js.map +1 -0
- package/dist/device.d.ts +39 -0
- package/dist/device.js +79 -0
- package/dist/device.js.map +1 -0
- package/dist/environment.d.ts +60 -0
- package/dist/environment.js +204 -0
- package/dist/environment.js.map +1 -0
- package/dist/extract.d.ts +74 -0
- package/dist/extract.js +194 -0
- package/dist/extract.js.map +1 -0
- package/dist/frame-graph.d.ts +112 -0
- package/dist/frame-graph.js +226 -0
- package/dist/frame-graph.js.map +1 -0
- package/dist/gpu-health.d.ts +74 -0
- package/dist/gpu-health.js +106 -0
- package/dist/gpu-health.js.map +1 -0
- package/dist/gpu-timer.d.ts +26 -0
- package/dist/gpu-timer.js +93 -0
- package/dist/gpu-timer.js.map +1 -0
- package/dist/headless.d.ts +28 -0
- package/dist/headless.js +91 -0
- package/dist/headless.js.map +1 -0
- package/dist/index.d.ts +34 -0
- package/dist/index.js +36 -0
- package/dist/index.js.map +1 -0
- package/dist/lights.d.ts +81 -0
- package/dist/lights.js +247 -0
- package/dist/lights.js.map +1 -0
- package/dist/matrix.d.ts +6 -0
- package/dist/matrix.js +59 -0
- package/dist/matrix.js.map +1 -0
- package/dist/meshes.d.ts +411 -0
- package/dist/meshes.js +545 -0
- package/dist/meshes.js.map +1 -0
- package/dist/meshlets-decoder.d.ts +20 -0
- package/dist/meshlets-decoder.js +47 -0
- package/dist/meshlets-decoder.js.map +1 -0
- package/dist/meshlets.d.ts +186 -0
- package/dist/meshlets.js +1040 -0
- package/dist/meshlets.js.map +1 -0
- package/dist/occlusion.d.ts +51 -0
- package/dist/occlusion.js +188 -0
- package/dist/occlusion.js.map +1 -0
- package/dist/pick-decoder.d.ts +19 -0
- package/dist/pick-decoder.js +45 -0
- package/dist/pick-decoder.js.map +1 -0
- package/dist/pick.d.ts +86 -0
- package/dist/pick.js +452 -0
- package/dist/pick.js.map +1 -0
- package/dist/pipeline-cache.d.ts +53 -0
- package/dist/pipeline-cache.js +96 -0
- package/dist/pipeline-cache.js.map +1 -0
- package/dist/post-options.d.ts +109 -0
- package/dist/post-options.js +23 -0
- package/dist/post-options.js.map +1 -0
- package/dist/post-shaders.gen.d.ts +40 -0
- package/dist/post-shaders.gen.js +43 -0
- package/dist/post-shaders.gen.js.map +1 -0
- package/dist/post.d.ts +101 -0
- package/dist/post.js +555 -0
- package/dist/post.js.map +1 -0
- package/dist/render-plugin.d.ts +239 -0
- package/dist/render-plugin.js +633 -0
- package/dist/render-plugin.js.map +1 -0
- package/dist/scene.d.ts +406 -0
- package/dist/scene.js +2005 -0
- package/dist/scene.js.map +1 -0
- package/dist/shaders.gen.d.ts +40 -0
- package/dist/shaders.gen.js +43 -0
- package/dist/shaders.gen.js.map +1 -0
- package/dist/shadows.d.ts +104 -0
- package/dist/shadows.js +227 -0
- package/dist/shadows.js.map +1 -0
- package/dist/staging-ring.d.ts +41 -0
- package/dist/staging-ring.js +125 -0
- package/dist/staging-ring.js.map +1 -0
- package/dist/textures.d.ts +165 -0
- package/dist/textures.js +361 -0
- package/dist/textures.js.map +1 -0
- package/dist/tonemap.d.ts +45 -0
- package/dist/tonemap.js +141 -0
- package/dist/tonemap.js.map +1 -0
- package/dist/viewport.d.ts +78 -0
- package/dist/viewport.js +130 -0
- package/dist/viewport.js.map +1 -0
- package/dist/webgl2/plugin.d.ts +40 -0
- package/dist/webgl2/plugin.js +190 -0
- package/dist/webgl2/plugin.js.map +1 -0
- package/dist/webgl2/program.d.ts +27 -0
- package/dist/webgl2/program.js +74 -0
- package/dist/webgl2/program.js.map +1 -0
- package/dist/webgl2/renderer.d.ts +128 -0
- package/dist/webgl2/renderer.js +976 -0
- package/dist/webgl2/renderer.js.map +1 -0
- package/dist/webgl2/shaders.glsl.gen.d.ts +27 -0
- package/dist/webgl2/shaders.glsl.gen.js +20 -0
- package/dist/webgl2/shaders.glsl.gen.js.map +1 -0
- package/dist/webgl2/textures.d.ts +11 -0
- package/dist/webgl2/textures.js +82 -0
- package/dist/webgl2/textures.js.map +1 -0
- package/dist/webgl2/webgl2.d.ts +1 -0
- package/dist/webgl2/webgl2.js +4 -0
- package/dist/webgl2/webgl2.js.map +1 -0
- package/dist/worker-mode.d.ts +85 -0
- package/dist/worker-mode.js +204 -0
- package/dist/worker-mode.js.map +1 -0
- package/package.json +47 -3
- package/README.md +0 -3
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import type { OrbitCamera } from './camera.ts';
|
|
2
|
+
/** The sun's shadows (ADR-0068): `false` turns them off. */
|
|
3
|
+
export interface ShadowOptions {
|
|
4
|
+
/** Cascades, 1–4 (default per tier: T0 2, else 3). */
|
|
5
|
+
cascades?: number;
|
|
6
|
+
/** Texels per side of each cascade (default per tier: T0 1024, T1 1024, T2 2048). */
|
|
7
|
+
size?: number;
|
|
8
|
+
/** View depth in metres where shadows end, fading out over the last tenth (default per tier: T0 80, T1 120, T2 150). */
|
|
9
|
+
distance?: number;
|
|
10
|
+
/** Blend between logarithmic (1) and uniform (0) cascade splits (default 0.75). */
|
|
11
|
+
splitLambda?: number;
|
|
12
|
+
/**
|
|
13
|
+
* The edge filter: `soft` is a 3×3 grid of 2×2 comparisons (9 fetches), `hard` one 2×2
|
|
14
|
+
* comparison (1 fetch). Default per tier: T0 `hard`, else `soft`.
|
|
15
|
+
*/
|
|
16
|
+
filter?: 'soft' | 'hard';
|
|
17
|
+
}
|
|
18
|
+
/** Floats of the draw's shadow uniform (`Shadow` in draw.wesl). */
|
|
19
|
+
export declare const SHADOW_FLOATS = 80;
|
|
20
|
+
/** Most cascades (the fragment uniform's matrix array). */
|
|
21
|
+
export declare const MAX_CASCADES = 4;
|
|
22
|
+
/** One cascade's vertex uniform (`Cascade` in shadow.wesl): the matrix, then the camera's offset from the instance anchor (ADR-0075). */
|
|
23
|
+
export declare const CASCADE_UNIFORM_BYTES = 80;
|
|
24
|
+
/**
|
|
25
|
+
* Cascaded shadow maps for the sun (ADR-0068). The view's depth range up to `distance` is
|
|
26
|
+
* split into `cascades` slices (the practical scheme: logarithmic blended with uniform);
|
|
27
|
+
* each slice's bounding sphere is fitted with an orthographic box looking down the light,
|
|
28
|
+
* its centre snapped to the shadow-map texel grid in world space (the camera's eye is f64,
|
|
29
|
+
* ADR-0008), so the map does not shimmer as the camera moves or turns. Every cascade is a
|
|
30
|
+
* square tile of one depth atlas (`cascades × size` by `size`): one texture to bind, one
|
|
31
|
+
* comparison sampler, no texture array, which compatibility mode and WebGL2 both sample.
|
|
32
|
+
*/
|
|
33
|
+
export declare class SunShadows {
|
|
34
|
+
#private;
|
|
35
|
+
readonly device: GPUDevice;
|
|
36
|
+
readonly cascades: number;
|
|
37
|
+
readonly size: number;
|
|
38
|
+
distance: number;
|
|
39
|
+
splitLambda: number;
|
|
40
|
+
/** PCF grid radius in taps: 1 = 3×3 (`soft`), 0 = one tap (`hard`). */
|
|
41
|
+
pcfRadius: number;
|
|
42
|
+
readonly atlas: GPUTexture;
|
|
43
|
+
readonly view: GPUTextureView;
|
|
44
|
+
readonly sampler: GPUSampler;
|
|
45
|
+
/** The vertex stage's matrices: one per cascade, `CASCADE_STRIDE` apart. */
|
|
46
|
+
readonly cascadeBuffer: GPUBuffer;
|
|
47
|
+
/** The fragment stage's: every matrix, the split depths, the texel sizes and the parameters. */
|
|
48
|
+
readonly paramsBuffer: GPUBuffer;
|
|
49
|
+
/** 4 matrices (64), splits (4), texel (4), params (4), filter (4) = 80 floats. */
|
|
50
|
+
readonly params: Float32Array<ArrayBuffer>;
|
|
51
|
+
constructor(device: GPUDevice, options: {
|
|
52
|
+
cascades: number;
|
|
53
|
+
size: number;
|
|
54
|
+
distance?: number;
|
|
55
|
+
splitLambda?: number;
|
|
56
|
+
filter?: 'soft' | 'hard';
|
|
57
|
+
});
|
|
58
|
+
get enabled(): boolean;
|
|
59
|
+
/** Byte offset of cascade `c`'s vertex uniform in `cascadeBuffer`. */
|
|
60
|
+
static offset(c: number): number;
|
|
61
|
+
/** The cascade split depths (view depth where each ends), into `out`. */
|
|
62
|
+
splits(near: number, out: Float32Array | number[]): void;
|
|
63
|
+
/**
|
|
64
|
+
* Fits every cascade to `camera`'s view (aspect = width / height) and uploads the matrices.
|
|
65
|
+
* `origin` is the camera's offset from the anchor the instance records are relative to
|
|
66
|
+
* (ADR-0075); the storage path's casters subtract it. Omit it for camera-relative records.
|
|
67
|
+
*/
|
|
68
|
+
update(camera: OrbitCamera, aspect: number, origin?: ArrayLike<number>): void;
|
|
69
|
+
/**
|
|
70
|
+
* The camera's frustum planes swept towards the light, into `out` (24 floats): a plane
|
|
71
|
+
* whose inward normal faces away from the light is dropped (it always passes), so an
|
|
72
|
+
* instance between the light and the view still casts. Conservative: the hull's side
|
|
73
|
+
* planes along the frustum's silhouette are not added.
|
|
74
|
+
*/
|
|
75
|
+
static sweptPlanes(camera: OrbitCamera, out: Float32Array, at: number): void;
|
|
76
|
+
destroy(): void;
|
|
77
|
+
}
|
|
78
|
+
/** The cascade splits (view depth where each ends) for a configuration, into `out`. */
|
|
79
|
+
export declare function cascadeSplits(fit: {
|
|
80
|
+
readonly cascades: number;
|
|
81
|
+
readonly distance: number;
|
|
82
|
+
readonly splitLambda: number;
|
|
83
|
+
}, near: number, out: Float32Array | number[]): void;
|
|
84
|
+
/**
|
|
85
|
+
* Fits every cascade to `camera`'s view (ADR-0068) and writes the draw's shadow uniform into
|
|
86
|
+
* `p` (80 floats: 4 matrices, the splits, the texel sizes, the parameters, the filter); cascade c's
|
|
87
|
+
* matrix is `p[c * 16 .. c * 16 + 16]`. Every backend uses this one fit.
|
|
88
|
+
*/
|
|
89
|
+
export declare function fitCascades(fit: {
|
|
90
|
+
readonly cascades: number;
|
|
91
|
+
readonly size: number;
|
|
92
|
+
readonly distance: number;
|
|
93
|
+
readonly splitLambda: number;
|
|
94
|
+
readonly pcfRadius: number;
|
|
95
|
+
}, camera: OrbitCamera, aspect: number, p: Float32Array): void;
|
|
96
|
+
/**
|
|
97
|
+
* Each cascade's light-space box as the CPU cull's caster planes (ADR-0073), into `out[at…]`
|
|
98
|
+
* (five planes of 4 floats per cascade, inward, normalized): the four sides and the far side
|
|
99
|
+
* of cascade c's box, from the fit's matrices in `p` (`fitCascades`). The side towards the
|
|
100
|
+
* light stays open: casters there are pancaked onto the box (ADR-0068).
|
|
101
|
+
*/
|
|
102
|
+
export declare function cascadeCullPlanes(p: Float32Array, cascades: number, out: Float32Array, at: number): void;
|
|
103
|
+
/** `SunShadows.sweptPlanes`, as a function (every backend's cull uses it). */
|
|
104
|
+
export declare function sweptPlanes(camera: OrbitCamera, out: Float32Array, at: number): void;
|
package/dist/shadows.js
ADDED
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
/** Floats of the draw's shadow uniform (`Shadow` in draw.wesl). */
|
|
2
|
+
export const SHADOW_FLOATS = 80;
|
|
3
|
+
/** Most cascades (the fragment uniform's matrix array). */
|
|
4
|
+
export const MAX_CASCADES = 4;
|
|
5
|
+
/** Bytes between cascades' vertex uniforms (minUniformBufferOffsetAlignment's ceiling). */
|
|
6
|
+
const CASCADE_STRIDE = 256;
|
|
7
|
+
/** One cascade's vertex uniform (`Cascade` in shadow.wesl): the matrix, then the camera's offset from the instance anchor (ADR-0075). */
|
|
8
|
+
export const CASCADE_UNIFORM_BYTES = 80;
|
|
9
|
+
/** The cascade box's depth range, in sphere radii beyond the sphere towards the light (casters there are not pancaked). */
|
|
10
|
+
const PULL = 1;
|
|
11
|
+
/**
|
|
12
|
+
* Cascaded shadow maps for the sun (ADR-0068). The view's depth range up to `distance` is
|
|
13
|
+
* split into `cascades` slices (the practical scheme: logarithmic blended with uniform);
|
|
14
|
+
* each slice's bounding sphere is fitted with an orthographic box looking down the light,
|
|
15
|
+
* its centre snapped to the shadow-map texel grid in world space (the camera's eye is f64,
|
|
16
|
+
* ADR-0008), so the map does not shimmer as the camera moves or turns. Every cascade is a
|
|
17
|
+
* square tile of one depth atlas (`cascades × size` by `size`): one texture to bind, one
|
|
18
|
+
* comparison sampler, no texture array, which compatibility mode and WebGL2 both sample.
|
|
19
|
+
*/
|
|
20
|
+
export class SunShadows {
|
|
21
|
+
device;
|
|
22
|
+
cascades;
|
|
23
|
+
size;
|
|
24
|
+
distance;
|
|
25
|
+
splitLambda;
|
|
26
|
+
/** PCF grid radius in taps: 1 = 3×3 (`soft`), 0 = one tap (`hard`). */
|
|
27
|
+
pcfRadius;
|
|
28
|
+
atlas;
|
|
29
|
+
view;
|
|
30
|
+
sampler;
|
|
31
|
+
/** The vertex stage's matrices: one per cascade, `CASCADE_STRIDE` apart. */
|
|
32
|
+
cascadeBuffer;
|
|
33
|
+
/** The fragment stage's: every matrix, the split depths, the texel sizes and the parameters. */
|
|
34
|
+
paramsBuffer;
|
|
35
|
+
#cascadeData = new Float32Array((MAX_CASCADES * CASCADE_STRIDE) / 4);
|
|
36
|
+
/** 4 matrices (64), splits (4), texel (4), params (4), filter (4) = 80 floats. */
|
|
37
|
+
params = new Float32Array(SHADOW_FLOATS);
|
|
38
|
+
constructor(device, options) {
|
|
39
|
+
this.device = device;
|
|
40
|
+
this.cascades = Math.max(0, Math.min(MAX_CASCADES, Math.floor(options.cascades)));
|
|
41
|
+
const maxSide = device.limits.maxTextureDimension2D;
|
|
42
|
+
this.size = this.cascades > 0 ? Math.min(options.size, Math.floor(maxSide / this.cascades)) : 1;
|
|
43
|
+
this.distance = options.distance ?? 150;
|
|
44
|
+
this.splitLambda = options.splitLambda ?? 0.75;
|
|
45
|
+
this.pcfRadius = options.filter === 'hard' ? 0 : 1;
|
|
46
|
+
this.atlas = device.createTexture({
|
|
47
|
+
label: 'yali:shadow:atlas',
|
|
48
|
+
size: [Math.max(1, this.cascades) * this.size, this.size],
|
|
49
|
+
format: 'depth32float',
|
|
50
|
+
usage: GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.TEXTURE_BINDING,
|
|
51
|
+
});
|
|
52
|
+
this.view = this.atlas.createView();
|
|
53
|
+
// Lit when the fragment's depth is at least the stored one (reverse-Z: larger is nearer the light).
|
|
54
|
+
this.sampler = device.createSampler({ label: 'yali:shadow', compare: 'greater-equal', magFilter: 'linear', minFilter: 'linear' });
|
|
55
|
+
this.cascadeBuffer = device.createBuffer({ label: 'yali:shadow:cascades', size: Math.max(1, this.cascades) * CASCADE_STRIDE, usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST });
|
|
56
|
+
this.paramsBuffer = device.createBuffer({ label: 'yali:shadow:params', size: this.params.byteLength, usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST });
|
|
57
|
+
device.queue.writeBuffer(this.paramsBuffer, 0, this.params); // count 0 until the first update
|
|
58
|
+
}
|
|
59
|
+
get enabled() {
|
|
60
|
+
return this.cascades > 0;
|
|
61
|
+
}
|
|
62
|
+
/** Byte offset of cascade `c`'s vertex uniform in `cascadeBuffer`. */
|
|
63
|
+
static offset(c) {
|
|
64
|
+
return c * CASCADE_STRIDE;
|
|
65
|
+
}
|
|
66
|
+
/** The cascade split depths (view depth where each ends), into `out`. */
|
|
67
|
+
splits(near, out) {
|
|
68
|
+
cascadeSplits(this, near, out);
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Fits every cascade to `camera`'s view (aspect = width / height) and uploads the matrices.
|
|
72
|
+
* `origin` is the camera's offset from the anchor the instance records are relative to
|
|
73
|
+
* (ADR-0075); the storage path's casters subtract it. Omit it for camera-relative records.
|
|
74
|
+
*/
|
|
75
|
+
update(camera, aspect, origin) {
|
|
76
|
+
if (!this.enabled)
|
|
77
|
+
return;
|
|
78
|
+
fitCascades(this, camera, aspect, this.params);
|
|
79
|
+
for (let c = 0; c < this.cascades; c++) {
|
|
80
|
+
const at = (c * CASCADE_STRIDE) / 4;
|
|
81
|
+
this.#cascadeData.set(this.params.subarray(c * 16, c * 16 + 16), at);
|
|
82
|
+
for (let i = 0; i < 3; i++)
|
|
83
|
+
this.#cascadeData[at + 16 + i] = origin ? origin[i] : 0;
|
|
84
|
+
}
|
|
85
|
+
this.device.queue.writeBuffer(this.cascadeBuffer, 0, this.#cascadeData, 0, (this.cascades * CASCADE_STRIDE) / 4);
|
|
86
|
+
this.device.queue.writeBuffer(this.paramsBuffer, 0, this.params);
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* The camera's frustum planes swept towards the light, into `out` (24 floats): a plane
|
|
90
|
+
* whose inward normal faces away from the light is dropped (it always passes), so an
|
|
91
|
+
* instance between the light and the view still casts. Conservative: the hull's side
|
|
92
|
+
* planes along the frustum's silhouette are not added.
|
|
93
|
+
*/
|
|
94
|
+
static sweptPlanes(camera, out, at) {
|
|
95
|
+
sweptPlanes(camera, out, at);
|
|
96
|
+
}
|
|
97
|
+
destroy() {
|
|
98
|
+
this.atlas.destroy();
|
|
99
|
+
this.cascadeBuffer.destroy();
|
|
100
|
+
this.paramsBuffer.destroy();
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
/** The cascade splits (view depth where each ends) for a configuration, into `out`. */
|
|
104
|
+
export function cascadeSplits(fit, near, out) {
|
|
105
|
+
const n = Math.max(near, 0.05);
|
|
106
|
+
const f = Math.max(fit.distance, n * 2);
|
|
107
|
+
const k = fit.cascades;
|
|
108
|
+
for (let i = 1; i <= k; i++) {
|
|
109
|
+
const log = n * (f / n) ** (i / k);
|
|
110
|
+
const uni = n + ((f - n) * i) / k;
|
|
111
|
+
out[i - 1] = fit.splitLambda * log + (1 - fit.splitLambda) * uni;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Fits every cascade to `camera`'s view (ADR-0068) and writes the draw's shadow uniform into
|
|
116
|
+
* `p` (80 floats: 4 matrices, the splits, the texel sizes, the parameters, the filter); cascade c's
|
|
117
|
+
* matrix is `p[c * 16 .. c * 16 + 16]`. Every backend uses this one fit.
|
|
118
|
+
*/
|
|
119
|
+
export function fitCascades(fit, camera, aspect, p) {
|
|
120
|
+
const v = camera.view;
|
|
121
|
+
// Forward is minus the view's third row (lookTo's back axis).
|
|
122
|
+
const fx = -v[2], fy = -v[6], fz = -v[10];
|
|
123
|
+
const lx = camera.uniform[40], ly = camera.uniform[41], lz = camera.uniform[42];
|
|
124
|
+
// The light's basis: right and up across it; the reference flips near a vertical sun.
|
|
125
|
+
let ux = 0, uy = 1, uz = 0;
|
|
126
|
+
if (Math.abs(ly) > 0.99)
|
|
127
|
+
[ux, uy, uz] = [0, 0, 1];
|
|
128
|
+
let rx = uy * lz - uz * ly, ry = uz * lx - ux * lz, rz = ux * ly - uy * lx;
|
|
129
|
+
const rl = Math.hypot(rx, ry, rz);
|
|
130
|
+
rx /= rl;
|
|
131
|
+
ry /= rl;
|
|
132
|
+
rz /= rl;
|
|
133
|
+
const upx = ly * rz - lz * ry, upy = lz * rx - lx * rz, upz = lx * ry - ly * rx;
|
|
134
|
+
const tanY = Math.tan(camera.fovY / 2);
|
|
135
|
+
const k2 = tanY * tanY * (1 + aspect * aspect); // squared half-diagonal per unit depth
|
|
136
|
+
const splits = p.subarray(64, 68);
|
|
137
|
+
cascadeSplits(fit, camera.near, splits);
|
|
138
|
+
const [ex, ey, ez] = camera.eye;
|
|
139
|
+
let d0 = camera.near;
|
|
140
|
+
for (let c = 0; c < fit.cascades; c++) {
|
|
141
|
+
const d1 = splits[c];
|
|
142
|
+
// The slice's bounding sphere: centre on the view axis, radius to the far corners.
|
|
143
|
+
let zc = ((d0 + d1) / 2) * (1 + k2);
|
|
144
|
+
if (zc > d1)
|
|
145
|
+
zc = d1;
|
|
146
|
+
let r = Math.sqrt((d1 - zc) ** 2 + k2 * d1 * d1);
|
|
147
|
+
r = Math.ceil(r * 16) / 16; // a constant radius keeps the texel size constant
|
|
148
|
+
const texel = (2 * r) / fit.size;
|
|
149
|
+
// Snap the centre to the texel grid in world space (f64), then back to camera-relative.
|
|
150
|
+
const wx = fx * zc + ex, wy = fy * zc + ey, wz = fz * zc + ez;
|
|
151
|
+
const sr = rx * wx + ry * wy + rz * wz;
|
|
152
|
+
const su = upx * wx + upy * wy + upz * wz;
|
|
153
|
+
const dr = Math.round(sr / texel) * texel - sr;
|
|
154
|
+
const du = Math.round(su / texel) * texel - su;
|
|
155
|
+
const ox = fx * zc + dr * rx + du * upx;
|
|
156
|
+
const oy = fy * zc + dr * ry + du * upy;
|
|
157
|
+
const oz = fz * zc + dr * rz + du * upz;
|
|
158
|
+
// Column-major: x = right / r, y = up / r, depth = (height along the light + r) / ((2 + PULL) r).
|
|
159
|
+
const zr = (2 + PULL) * r;
|
|
160
|
+
const m = p.subarray(c * 16, c * 16 + 16);
|
|
161
|
+
m[0] = rx / r;
|
|
162
|
+
m[4] = ry / r;
|
|
163
|
+
m[8] = rz / r;
|
|
164
|
+
m[12] = -(rx * ox + ry * oy + rz * oz) / r;
|
|
165
|
+
m[1] = upx / r;
|
|
166
|
+
m[5] = upy / r;
|
|
167
|
+
m[9] = upz / r;
|
|
168
|
+
m[13] = -(upx * ox + upy * oy + upz * oz) / r;
|
|
169
|
+
m[2] = lx / zr;
|
|
170
|
+
m[6] = ly / zr;
|
|
171
|
+
m[10] = lz / zr;
|
|
172
|
+
m[14] = (r - (lx * ox + ly * oy + lz * oz)) / zr;
|
|
173
|
+
m[3] = 0;
|
|
174
|
+
m[7] = 0;
|
|
175
|
+
m[11] = 0;
|
|
176
|
+
m[15] = 1;
|
|
177
|
+
p[68 + c] = texel;
|
|
178
|
+
d0 = d1;
|
|
179
|
+
}
|
|
180
|
+
p[72] = fit.cascades;
|
|
181
|
+
p[73] = 1 / fit.size;
|
|
182
|
+
p[74] = fit.distance * 0.9; // the fade starts
|
|
183
|
+
p[75] = fit.distance;
|
|
184
|
+
p[76] = fit.pcfRadius;
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* Each cascade's light-space box as the CPU cull's caster planes (ADR-0073), into `out[at…]`
|
|
188
|
+
* (five planes of 4 floats per cascade, inward, normalized): the four sides and the far side
|
|
189
|
+
* of cascade c's box, from the fit's matrices in `p` (`fitCascades`). The side towards the
|
|
190
|
+
* light stays open: casters there are pancaked onto the box (ADR-0068).
|
|
191
|
+
*/
|
|
192
|
+
export function cascadeCullPlanes(p, cascades, out, at) {
|
|
193
|
+
for (let c = 0; c < cascades; c++) {
|
|
194
|
+
const m = c * 16;
|
|
195
|
+
const row = (r) => [p[m + r], p[m + 4 + r], p[m + 8 + r], p[m + 12 + r]];
|
|
196
|
+
const planes = [
|
|
197
|
+
[...row(0).slice(0, 3), row(0)[3] + 1],
|
|
198
|
+
[...row(0).slice(0, 3).map((v) => -v), 1 - row(0)[3]],
|
|
199
|
+
[...row(1).slice(0, 3), row(1)[3] + 1],
|
|
200
|
+
[...row(1).slice(0, 3).map((v) => -v), 1 - row(1)[3]],
|
|
201
|
+
row(2),
|
|
202
|
+
];
|
|
203
|
+
for (let j = 0; j < 5; j++) {
|
|
204
|
+
const [x, y, z, w] = planes[j];
|
|
205
|
+
const l = Math.hypot(x, y, z) || 1;
|
|
206
|
+
const o = at + (c * 5 + j) * 4;
|
|
207
|
+
out[o] = x / l;
|
|
208
|
+
out[o + 1] = y / l;
|
|
209
|
+
out[o + 2] = z / l;
|
|
210
|
+
out[o + 3] = w / l;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
/** `SunShadows.sweptPlanes`, as a function (every backend's cull uses it). */
|
|
215
|
+
export function sweptPlanes(camera, out, at) {
|
|
216
|
+
const u = camera.uniform;
|
|
217
|
+
const lx = u[40], ly = u[41], lz = u[42];
|
|
218
|
+
for (let k = 0; k < 6; k++) {
|
|
219
|
+
const o = 16 + k * 4;
|
|
220
|
+
const away = u[o] * lx + u[o + 1] * ly + u[o + 2] * lz < 0;
|
|
221
|
+
out[at + k * 4] = away ? 0 : u[o];
|
|
222
|
+
out[at + k * 4 + 1] = away ? 0 : u[o + 1];
|
|
223
|
+
out[at + k * 4 + 2] = away ? 0 : u[o + 2];
|
|
224
|
+
out[at + k * 4 + 3] = away ? 1 : u[o + 3];
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
//# sourceMappingURL=shadows.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shadows.js","sourceRoot":"","sources":["../src/shadows.ts"],"names":[],"mappings":"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,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;QAC3D,GAAG,CAAC,EAAE,GAAG,CAAC,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAClC,GAAG,CAAC,EAAE,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;QAC1C,GAAG,CAAC,EAAE,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;QAC1C,GAAG,CAAC,EAAE,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;IAC5C,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-frame uploads through a ring of mapped `MAP_WRITE | COPY_SRC` buffers: copy into a
|
|
3
|
+
* mapped range, unmap, record `copyBufferToBuffer`, and re-map after submit.
|
|
4
|
+
*
|
|
5
|
+
* Measured 2026-10-16 (Chrome 155, `bench/webgpu-upload`): `queue.writeBuffer` runs at
|
|
6
|
+
* ~1.7 GB/s for writes under 4 MiB and ~10 GB/s from 4 MiB up. The ring runs at
|
|
7
|
+
* 6–11 GB/s from 0.8 MiB up, so a 1.6 MiB instance upload drops from ~1.0 ms to ~0.2 ms.
|
|
8
|
+
*
|
|
9
|
+
* When no buffer is mapped yet (the GPU is behind), `write` returns false and the caller
|
|
10
|
+
* uses `writeBuffer`. The one per-frame allocation is the `Uint8Array` over the mapped
|
|
11
|
+
* range: WebGPU hands out a new `ArrayBuffer` on every map.
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* const ring = new StagingRing(device, bytes);
|
|
15
|
+
* if (!ring.write(heap, address, n)) device.queue.writeBuffer(dst, 0, heap, address, n);
|
|
16
|
+
* const encoder = device.createCommandEncoder();
|
|
17
|
+
* ring.record(encoder, dst); // before the passes that read `dst`
|
|
18
|
+
* device.queue.submit([encoder.finish()]);
|
|
19
|
+
* ring.recycle();
|
|
20
|
+
*/
|
|
21
|
+
export declare class StagingRing {
|
|
22
|
+
#private;
|
|
23
|
+
readonly size: number;
|
|
24
|
+
/** Writes that went through the ring. */
|
|
25
|
+
writes: number;
|
|
26
|
+
/** Writes that found no mapped buffer (the caller fell back to `writeBuffer`). */
|
|
27
|
+
fallbacks: number;
|
|
28
|
+
constructor(device: GPUDevice, size: number, slots?: number);
|
|
29
|
+
/** Copies `bytes` of `src` from `offset` into a mapped buffer. False when none is mapped. */
|
|
30
|
+
write(src: Uint8Array, offset: number, bytes: number): boolean;
|
|
31
|
+
/** Records the pending copy into `dst` (call before any pass reads `dst`). */
|
|
32
|
+
record(encoder: GPUCommandEncoder, dst: GPUBuffer, dstOffset?: number): void;
|
|
33
|
+
/** Call after `queue.submit`: re-maps the buffers whose copies were submitted. */
|
|
34
|
+
recycle(): void;
|
|
35
|
+
/** GPU buffers owned (for the dispose leak check). */
|
|
36
|
+
get liveBuffers(): number;
|
|
37
|
+
/** GPU bytes owned (for the memory counter). */
|
|
38
|
+
get bytes(): number;
|
|
39
|
+
/** Destroys every staging buffer. */
|
|
40
|
+
destroy(): void;
|
|
41
|
+
}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
const IDLE = 0; // mapped, ready for a write
|
|
2
|
+
const WRITTEN = 1; // unmapped, its copy not yet recorded
|
|
3
|
+
const RECORDED = 2; // copy recorded; re-map after submit
|
|
4
|
+
const MAPPING = 3; // mapAsync in flight
|
|
5
|
+
const DEAD = 4; // mapping failed (device lost): never used again
|
|
6
|
+
/**
|
|
7
|
+
* Per-frame uploads through a ring of mapped `MAP_WRITE | COPY_SRC` buffers: copy into a
|
|
8
|
+
* mapped range, unmap, record `copyBufferToBuffer`, and re-map after submit.
|
|
9
|
+
*
|
|
10
|
+
* Measured 2026-10-16 (Chrome 155, `bench/webgpu-upload`): `queue.writeBuffer` runs at
|
|
11
|
+
* ~1.7 GB/s for writes under 4 MiB and ~10 GB/s from 4 MiB up. The ring runs at
|
|
12
|
+
* 6–11 GB/s from 0.8 MiB up, so a 1.6 MiB instance upload drops from ~1.0 ms to ~0.2 ms.
|
|
13
|
+
*
|
|
14
|
+
* When no buffer is mapped yet (the GPU is behind), `write` returns false and the caller
|
|
15
|
+
* uses `writeBuffer`. The one per-frame allocation is the `Uint8Array` over the mapped
|
|
16
|
+
* range: WebGPU hands out a new `ArrayBuffer` on every map.
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* const ring = new StagingRing(device, bytes);
|
|
20
|
+
* if (!ring.write(heap, address, n)) device.queue.writeBuffer(dst, 0, heap, address, n);
|
|
21
|
+
* const encoder = device.createCommandEncoder();
|
|
22
|
+
* ring.record(encoder, dst); // before the passes that read `dst`
|
|
23
|
+
* device.queue.submit([encoder.finish()]);
|
|
24
|
+
* ring.recycle();
|
|
25
|
+
*/
|
|
26
|
+
export class StagingRing {
|
|
27
|
+
size;
|
|
28
|
+
/** Writes that went through the ring. */
|
|
29
|
+
writes = 0;
|
|
30
|
+
/** Writes that found no mapped buffer (the caller fell back to `writeBuffer`). */
|
|
31
|
+
fallbacks = 0;
|
|
32
|
+
#slots = [];
|
|
33
|
+
#pending = null;
|
|
34
|
+
#source = null;
|
|
35
|
+
#sourceKey = { buffer: null, offset: -1, bytes: -1 };
|
|
36
|
+
#destroyed = false;
|
|
37
|
+
constructor(device, size, slots = 3) {
|
|
38
|
+
this.size = size;
|
|
39
|
+
for (let i = 0; i < slots; i++) {
|
|
40
|
+
const slot = {
|
|
41
|
+
buffer: device.createBuffer({ label: `yali:staging${i}`, size, usage: GPUBufferUsage.MAP_WRITE | GPUBufferUsage.COPY_SRC, mappedAtCreation: true }),
|
|
42
|
+
state: IDLE,
|
|
43
|
+
bytes: 0,
|
|
44
|
+
onMapped: () => {
|
|
45
|
+
if (slot.state === MAPPING)
|
|
46
|
+
slot.state = IDLE;
|
|
47
|
+
},
|
|
48
|
+
onFailed: () => {
|
|
49
|
+
slot.state = DEAD;
|
|
50
|
+
},
|
|
51
|
+
};
|
|
52
|
+
this.#slots.push(slot);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
/** Copies `bytes` of `src` from `offset` into a mapped buffer. False when none is mapped. */
|
|
56
|
+
write(src, offset, bytes) {
|
|
57
|
+
if (this.#destroyed || bytes > this.size)
|
|
58
|
+
return false;
|
|
59
|
+
let slot = null;
|
|
60
|
+
for (let i = 0; i < this.#slots.length; i++) {
|
|
61
|
+
if (this.#slots[i].state === IDLE) {
|
|
62
|
+
slot = this.#slots[i];
|
|
63
|
+
break;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
if (!slot || this.#pending) {
|
|
67
|
+
this.fallbacks++;
|
|
68
|
+
return false;
|
|
69
|
+
}
|
|
70
|
+
// The source view is cached: the extract output's address and size rarely change.
|
|
71
|
+
const k = this.#sourceKey;
|
|
72
|
+
if (k.buffer !== src.buffer || k.offset !== src.byteOffset + offset || k.bytes !== bytes) {
|
|
73
|
+
k.buffer = src.buffer;
|
|
74
|
+
k.offset = src.byteOffset + offset;
|
|
75
|
+
k.bytes = bytes;
|
|
76
|
+
this.#source = new Uint8Array(src.buffer, k.offset, bytes);
|
|
77
|
+
}
|
|
78
|
+
new Uint8Array(slot.buffer.getMappedRange(0, bytes)).set(this.#source);
|
|
79
|
+
slot.buffer.unmap();
|
|
80
|
+
slot.state = WRITTEN;
|
|
81
|
+
slot.bytes = bytes;
|
|
82
|
+
this.#pending = slot;
|
|
83
|
+
this.writes++;
|
|
84
|
+
return true;
|
|
85
|
+
}
|
|
86
|
+
/** Records the pending copy into `dst` (call before any pass reads `dst`). */
|
|
87
|
+
record(encoder, dst, dstOffset = 0) {
|
|
88
|
+
const slot = this.#pending;
|
|
89
|
+
if (!slot)
|
|
90
|
+
return;
|
|
91
|
+
encoder.copyBufferToBuffer(slot.buffer, 0, dst, dstOffset, slot.bytes);
|
|
92
|
+
slot.state = RECORDED;
|
|
93
|
+
this.#pending = null;
|
|
94
|
+
}
|
|
95
|
+
/** Call after `queue.submit`: re-maps the buffers whose copies were submitted. */
|
|
96
|
+
recycle() {
|
|
97
|
+
if (this.#destroyed)
|
|
98
|
+
return;
|
|
99
|
+
for (let i = 0; i < this.#slots.length; i++) {
|
|
100
|
+
const s = this.#slots[i];
|
|
101
|
+
if (s.state === RECORDED || (s.state === WRITTEN && s !== this.#pending)) {
|
|
102
|
+
s.state = MAPPING;
|
|
103
|
+
s.buffer.mapAsync(GPUMapMode.WRITE).then(s.onMapped, s.onFailed);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
/** GPU buffers owned (for the dispose leak check). */
|
|
108
|
+
get liveBuffers() {
|
|
109
|
+
return this.#destroyed ? 0 : this.#slots.length;
|
|
110
|
+
}
|
|
111
|
+
/** GPU bytes owned (for the memory counter). */
|
|
112
|
+
get bytes() {
|
|
113
|
+
return this.#destroyed ? 0 : this.#slots.length * this.size;
|
|
114
|
+
}
|
|
115
|
+
/** Destroys every staging buffer. */
|
|
116
|
+
destroy() {
|
|
117
|
+
if (this.#destroyed)
|
|
118
|
+
return;
|
|
119
|
+
this.#destroyed = true;
|
|
120
|
+
for (const s of this.#slots)
|
|
121
|
+
s.buffer.destroy();
|
|
122
|
+
this.#pending = null;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
//# sourceMappingURL=staging-ring.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"staging-ring.js","sourceRoot":"","sources":["../src/staging-ring.ts"],"names":[],"mappings":"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"}
|