@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,158 @@
|
|
|
1
|
+
import { debugLines } from "./shaders.gen.js";
|
|
2
|
+
/** Bytes per debug vertex: x, y, z (f32, camera-relative once queued) and an RGBA8 colour. */
|
|
3
|
+
export const DEBUG_VERTEX_BYTES = 16;
|
|
4
|
+
/**
|
|
5
|
+
* Debug line segments for this frame (`render.debug`, ADR-0087): plugins queue them after the
|
|
6
|
+
* camera has moved (Extract or later), the renderer draws them at the end of the scene pass,
|
|
7
|
+
* depth-tested against it, and the queue empties every frame. Positions are converted to
|
|
8
|
+
* camera-relative f32 as they are queued, from f64 origins, so lines far from the world origin
|
|
9
|
+
* stay exact. Allocation-free once the buffer has grown.
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* const render = app.resource<RenderContext>('render');
|
|
13
|
+
* render.debug.line([0, 0, 0], [0, 2, 0], 0xff0000ff); // a red post, 2 m tall
|
|
14
|
+
*/
|
|
15
|
+
export class DebugLines {
|
|
16
|
+
/** Vertices queued this frame (two per segment). */
|
|
17
|
+
count = 0;
|
|
18
|
+
/** At most this many vertices per frame; more are dropped (and counted). */
|
|
19
|
+
capacity;
|
|
20
|
+
/** Vertices dropped this frame over `capacity`. */
|
|
21
|
+
dropped = 0;
|
|
22
|
+
/** Vertices the last drawn frame had (what `clear` emptied; the `render.debugLines` counter). */
|
|
23
|
+
drawn = 0;
|
|
24
|
+
#f32 = new Float32Array(0);
|
|
25
|
+
#u32 = new Uint32Array(0);
|
|
26
|
+
#camera;
|
|
27
|
+
constructor(camera, capacity = 1 << 20) {
|
|
28
|
+
this.#camera = camera;
|
|
29
|
+
this.capacity = capacity;
|
|
30
|
+
}
|
|
31
|
+
/** The queued vertices (`count × 16` bytes from offset 0). */
|
|
32
|
+
get data() {
|
|
33
|
+
return this.#f32;
|
|
34
|
+
}
|
|
35
|
+
#reserve(n) {
|
|
36
|
+
const room = Math.max(0, Math.min(n, this.capacity - this.count));
|
|
37
|
+
this.dropped += n - room;
|
|
38
|
+
const need = (this.count + room) * 4;
|
|
39
|
+
if (need > this.#f32.length) {
|
|
40
|
+
const next = new Float32Array(Math.max(need, this.#f32.length * 2, 4096));
|
|
41
|
+
next.set(this.#f32.subarray(0, this.count * 4));
|
|
42
|
+
this.#f32 = next;
|
|
43
|
+
this.#u32 = new Uint32Array(next.buffer);
|
|
44
|
+
}
|
|
45
|
+
return room;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Queues `count` vertices from a packed buffer: 16 bytes each (x, y, z as f32 relative to
|
|
49
|
+
* `origin`, then RGBA8), starting at vertex `first` of the views `f32` / `u32` (one buffer,
|
|
50
|
+
* e.g. WASM memory). `origin` is f64 world coordinates.
|
|
51
|
+
*/
|
|
52
|
+
add(f32, u32, first, count, origin) {
|
|
53
|
+
const eye = this.#camera.eye;
|
|
54
|
+
const ox = origin[0] - eye[0];
|
|
55
|
+
const oy = origin[1] - eye[1];
|
|
56
|
+
const oz = origin[2] - eye[2];
|
|
57
|
+
const n = this.#reserve(count);
|
|
58
|
+
const dst = this.#f32;
|
|
59
|
+
const dstU = this.#u32;
|
|
60
|
+
for (let i = 0; i < n; i++) {
|
|
61
|
+
const s = (first + i) * 4;
|
|
62
|
+
const d = (this.count + i) * 4;
|
|
63
|
+
dst[d] = f32[s] + ox;
|
|
64
|
+
dst[d + 1] = f32[s + 1] + oy;
|
|
65
|
+
dst[d + 2] = f32[s + 2] + oz;
|
|
66
|
+
dstU[d + 3] = u32[s + 3];
|
|
67
|
+
}
|
|
68
|
+
this.count += n;
|
|
69
|
+
}
|
|
70
|
+
/** Queues one segment between world points `a` and `b`, colour `rgba` (red in the low byte). */
|
|
71
|
+
line(a, b, rgba) {
|
|
72
|
+
if (this.#reserve(2) < 2)
|
|
73
|
+
return;
|
|
74
|
+
const eye = this.#camera.eye;
|
|
75
|
+
const d = this.count * 4;
|
|
76
|
+
const f = this.#f32;
|
|
77
|
+
f[d] = a[0] - eye[0];
|
|
78
|
+
f[d + 1] = a[1] - eye[1];
|
|
79
|
+
f[d + 2] = a[2] - eye[2];
|
|
80
|
+
f[d + 4] = b[0] - eye[0];
|
|
81
|
+
f[d + 5] = b[1] - eye[1];
|
|
82
|
+
f[d + 6] = b[2] - eye[2];
|
|
83
|
+
this.#u32[d + 3] = rgba >>> 0;
|
|
84
|
+
this.#u32[d + 7] = rgba >>> 0;
|
|
85
|
+
this.count += 2;
|
|
86
|
+
}
|
|
87
|
+
/** Empties the queue (the renderer calls it after each frame). */
|
|
88
|
+
clear() {
|
|
89
|
+
this.drawn = this.count;
|
|
90
|
+
this.count = 0;
|
|
91
|
+
this.dropped = 0;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
/** Draws a `DebugLines` queue in the scene pass (WebGPU). */
|
|
95
|
+
export class DebugLinesPass {
|
|
96
|
+
device;
|
|
97
|
+
#pipeline;
|
|
98
|
+
#group = null;
|
|
99
|
+
#buffer = null;
|
|
100
|
+
#format;
|
|
101
|
+
#camera;
|
|
102
|
+
constructor(device, format, camera) {
|
|
103
|
+
this.device = device;
|
|
104
|
+
this.#format = format;
|
|
105
|
+
this.#camera = camera;
|
|
106
|
+
}
|
|
107
|
+
async init(cache) {
|
|
108
|
+
this.#pipeline = await cache.render({
|
|
109
|
+
label: 'yali:debug-lines',
|
|
110
|
+
vertex: {
|
|
111
|
+
shader: debugLines,
|
|
112
|
+
entryPoint: 'vs',
|
|
113
|
+
buffers: [
|
|
114
|
+
{
|
|
115
|
+
arrayStride: DEBUG_VERTEX_BYTES,
|
|
116
|
+
attributes: [
|
|
117
|
+
{ shaderLocation: 0, offset: 0, format: 'float32x3' },
|
|
118
|
+
{ shaderLocation: 1, offset: 12, format: 'unorm8x4' },
|
|
119
|
+
],
|
|
120
|
+
},
|
|
121
|
+
],
|
|
122
|
+
},
|
|
123
|
+
fragment: { shader: debugLines, entryPoint: 'fs', targets: [{ format: this.#format }] },
|
|
124
|
+
primitive: { topology: 'line-list' },
|
|
125
|
+
// Reverse-Z: nearer is greater. Lines never write depth, so they never hide each other.
|
|
126
|
+
depthStencil: { format: 'depth32float', depthWriteEnabled: false, depthCompare: 'greater-equal' },
|
|
127
|
+
});
|
|
128
|
+
this.#group = this.device.createBindGroup({
|
|
129
|
+
label: 'yali:debug-lines',
|
|
130
|
+
layout: this.#pipeline.getBindGroupLayout(0),
|
|
131
|
+
entries: [{ binding: 0, resource: { buffer: this.#camera } }],
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
/** Uploads and draws `lines` into `pass` (inside the scene pass, after its draws). */
|
|
135
|
+
encode(pass, lines) {
|
|
136
|
+
if (lines.count === 0 || !this.#group)
|
|
137
|
+
return;
|
|
138
|
+
const bytes = lines.count * DEBUG_VERTEX_BYTES;
|
|
139
|
+
if (!this.#buffer || this.#buffer.size < bytes) {
|
|
140
|
+
this.#buffer?.destroy();
|
|
141
|
+
this.#buffer = this.device.createBuffer({ label: 'yali:debug-lines', size: Math.max(bytes, 1 << 16) * 2, usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST });
|
|
142
|
+
}
|
|
143
|
+
this.device.queue.writeBuffer(this.#buffer, 0, lines.data.buffer, 0, bytes);
|
|
144
|
+
pass.setPipeline(this.#pipeline);
|
|
145
|
+
pass.setBindGroup(0, this.#group);
|
|
146
|
+
pass.setVertexBuffer(0, this.#buffer, 0, bytes);
|
|
147
|
+
pass.draw(lines.count);
|
|
148
|
+
}
|
|
149
|
+
/** GPU buffers held (the leak check). */
|
|
150
|
+
get liveBuffers() {
|
|
151
|
+
return this.#buffer ? 1 : 0;
|
|
152
|
+
}
|
|
153
|
+
destroy() {
|
|
154
|
+
this.#buffer?.destroy();
|
|
155
|
+
this.#buffer = null;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
//# sourceMappingURL=debug-lines.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"debug-lines.js","sourceRoot":"","sources":["../src/debug-lines.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Device identity and tier selection (ADR-0010: identity collection ported from Character XYZ
|
|
3
|
+
* `mesh/sculpt/instrument/device-profile.ts`; DESIGN.md §14 tier rules).
|
|
4
|
+
*
|
|
5
|
+
* The GPU is identified twice on purpose. `GPUAdapterInfo` is vague on most drivers
|
|
6
|
+
* (vendor "amd", architecture "" is a normal answer), but it is the only source of the
|
|
7
|
+
* limits. `WEBGL_debug_renderer_info` is the only reliable human-readable card name, and
|
|
8
|
+
* it needs a real GL context, so the probe creates a 1×1 context and immediately forces it
|
|
9
|
+
* lost (browsers cap live GL contexts and drop the oldest).
|
|
10
|
+
*
|
|
11
|
+
* Nothing here throws: it runs on machines we have never seen, in browsers missing half
|
|
12
|
+
* these APIs. Every read is feature-detected and degrades to null.
|
|
13
|
+
*/
|
|
14
|
+
/** Everything a perf log needs to name the machine that produced it. */
|
|
15
|
+
export interface DeviceProfile {
|
|
16
|
+
capturedAt: string;
|
|
17
|
+
userAgent: string;
|
|
18
|
+
platform: string | null;
|
|
19
|
+
mobile: boolean | null;
|
|
20
|
+
deviceModel: string | null;
|
|
21
|
+
cpuCores: number | null;
|
|
22
|
+
deviceMemoryGb: number | null;
|
|
23
|
+
screen: {
|
|
24
|
+
width: number;
|
|
25
|
+
height: number;
|
|
26
|
+
dpr: number;
|
|
27
|
+
};
|
|
28
|
+
touchPoints: number | null;
|
|
29
|
+
gpu: {
|
|
30
|
+
vendor: string | null;
|
|
31
|
+
architecture: string | null;
|
|
32
|
+
description: string | null;
|
|
33
|
+
webglRenderer: string | null;
|
|
34
|
+
maxStorageBufferBindingSizeMb: number | null;
|
|
35
|
+
maxBufferSizeMb: number | null;
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
/** Collects the profile. Never throws. */
|
|
39
|
+
export declare function collectDeviceProfile(adapter?: GPUAdapter | null): Promise<DeviceProfile>;
|
|
40
|
+
/**
|
|
41
|
+
* What a device-filter rule can turn off. Each changes only what the render plugin picks by
|
|
42
|
+
* default; an option the app sets explicitly wins.
|
|
43
|
+
* - `shadows`: no sun shadows (ADR-0068).
|
|
44
|
+
* - `staging`: uploads through `writeBuffer`, not the mapped staging ring (ADR-0056).
|
|
45
|
+
* - `bundles`: draws encoded each frame, not replayed from a render bundle (ADR-0050).
|
|
46
|
+
* - `gpu-cull`: the CPU cull with ordinary draws, no indirect draws (ADR-0057).
|
|
47
|
+
* - `storage-instances`: instances as vertex attributes, no vertex-stage storage (ADR-0045).
|
|
48
|
+
* - `compressed-textures`: RGBA8 textures, no BC / ASTC / ETC2 (ADR-0060).
|
|
49
|
+
*/
|
|
50
|
+
export type DisableableFeature = 'shadows' | 'staging' | 'bundles' | 'gpu-cull' | 'storage-instances' | 'compressed-textures';
|
|
51
|
+
/** A device-filter rule (DESIGN.md §14: Unity's Graphics Device Filtering, as data). */
|
|
52
|
+
export interface DeviceFilterRule {
|
|
53
|
+
readonly id: string;
|
|
54
|
+
/**
|
|
55
|
+
* Every present field must match (substring, case-insensitive): the user agent (browser,
|
|
56
|
+
* version), the platform (OS), the device model, and the GPU's vendor, architecture and
|
|
57
|
+
* WebGL renderer string.
|
|
58
|
+
*/
|
|
59
|
+
readonly match: {
|
|
60
|
+
readonly userAgent?: string;
|
|
61
|
+
readonly platform?: string;
|
|
62
|
+
readonly device?: string;
|
|
63
|
+
readonly vendor?: string;
|
|
64
|
+
readonly architecture?: string;
|
|
65
|
+
readonly renderer?: string;
|
|
66
|
+
/** The WebGPU backend the adapter came up on (exact). */
|
|
67
|
+
readonly backend?: 'webgpu' | 'webgpu-compat';
|
|
68
|
+
};
|
|
69
|
+
readonly action: {
|
|
70
|
+
readonly maxTier?: Tier;
|
|
71
|
+
readonly disableFeatures?: readonly DisableableFeature[];
|
|
72
|
+
/** Use the WebGL2 backend instead of WebGPU (unless the app asked for `backend: 'webgpu'`). */
|
|
73
|
+
readonly useWebGL2?: boolean;
|
|
74
|
+
};
|
|
75
|
+
/** Why, with the measurement behind it. */
|
|
76
|
+
readonly reason: string;
|
|
77
|
+
readonly issueUrl?: string;
|
|
78
|
+
}
|
|
79
|
+
/** Quality tiers (DESIGN.md §14). */
|
|
80
|
+
export type Tier = 't0' | 't1' | 't2';
|
|
81
|
+
/**
|
|
82
|
+
* Known-bad combinations, shipped with the engine; an app replaces or extends it with the
|
|
83
|
+
* render plugin's `deviceFilter` option. An entry needs a measured failure behind it. (Character XYZ's Brave depth-buffer boot race was checked against this
|
|
84
|
+
* renderer on 2026-10-16 and does not reproduce, so it is not listed. The Galaxy F04's slow
|
|
85
|
+
* indirect draws and staging maps are compatibility-mode costs, handled by the backend's
|
|
86
|
+
* defaults, not by a device row: ADR-0056, ADR-0057.)
|
|
87
|
+
*/
|
|
88
|
+
export declare const DEVICE_FILTER: readonly DeviceFilterRule[];
|
|
89
|
+
/**
|
|
90
|
+
* Picks the tier: compatibility mode or WebGL2 → T0; a mobile/touch-first device → T1; otherwise T2. Then
|
|
91
|
+
* the device-filter table may lower it. Returns the tier, why, the matching rule ids, and
|
|
92
|
+
* the features the matching rules turn off (an override sets the tier only).
|
|
93
|
+
*/
|
|
94
|
+
export declare function selectTier(backend: 'webgpu' | 'webgpu-compat' | 'webgl2', profile: DeviceProfile, rules?: readonly DeviceFilterRule[], override?: Tier): {
|
|
95
|
+
tier: Tier;
|
|
96
|
+
source: 'caps' | 'filter' | 'override';
|
|
97
|
+
matched: string[];
|
|
98
|
+
disable: DisableableFeature[];
|
|
99
|
+
useWebGL2: boolean;
|
|
100
|
+
};
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Device identity and tier selection (ADR-0010: identity collection ported from Character XYZ
|
|
3
|
+
* `mesh/sculpt/instrument/device-profile.ts`; DESIGN.md §14 tier rules).
|
|
4
|
+
*
|
|
5
|
+
* The GPU is identified twice on purpose. `GPUAdapterInfo` is vague on most drivers
|
|
6
|
+
* (vendor "amd", architecture "" is a normal answer), but it is the only source of the
|
|
7
|
+
* limits. `WEBGL_debug_renderer_info` is the only reliable human-readable card name, and
|
|
8
|
+
* it needs a real GL context, so the probe creates a 1×1 context and immediately forces it
|
|
9
|
+
* lost (browsers cap live GL contexts and drop the oldest).
|
|
10
|
+
*
|
|
11
|
+
* Nothing here throws: it runs on machines we have never seen, in browsers missing half
|
|
12
|
+
* these APIs. Every read is feature-detected and degrades to null.
|
|
13
|
+
*/
|
|
14
|
+
const str = (v) => (typeof v === 'string' && v.length > 0 ? v : null);
|
|
15
|
+
const num = (v) => (typeof v === 'number' && Number.isFinite(v) ? v : null);
|
|
16
|
+
const mb = (v) => {
|
|
17
|
+
const n = num(v);
|
|
18
|
+
return n === null ? null : Math.round(n / 1048576);
|
|
19
|
+
};
|
|
20
|
+
/** The card name via WebGL, in a context that is created and immediately lost. */
|
|
21
|
+
function probeWebglRenderer() {
|
|
22
|
+
try {
|
|
23
|
+
if (typeof document === 'undefined')
|
|
24
|
+
return null;
|
|
25
|
+
const canvas = document.createElement('canvas');
|
|
26
|
+
canvas.width = canvas.height = 1;
|
|
27
|
+
const gl = canvas.getContext('webgl');
|
|
28
|
+
if (!gl)
|
|
29
|
+
return null;
|
|
30
|
+
try {
|
|
31
|
+
const dbg = gl.getExtension('WEBGL_debug_renderer_info');
|
|
32
|
+
return str(dbg ? gl.getParameter(dbg.UNMASKED_RENDERER_WEBGL) : gl.getParameter(gl.RENDERER));
|
|
33
|
+
}
|
|
34
|
+
finally {
|
|
35
|
+
gl.getExtension('WEBGL_lose_context')?.loseContext();
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
/** Collects the profile. Never throws. */
|
|
43
|
+
export async function collectDeviceProfile(adapter) {
|
|
44
|
+
const nav = typeof navigator === 'undefined' ? null : navigator;
|
|
45
|
+
let platform = null;
|
|
46
|
+
let mobile = null;
|
|
47
|
+
let model = null;
|
|
48
|
+
try {
|
|
49
|
+
const ua = nav?.userAgentData;
|
|
50
|
+
platform = str(ua?.platform);
|
|
51
|
+
mobile = typeof ua?.mobile === 'boolean' ? ua.mobile : null;
|
|
52
|
+
// Rejects (rather than resolving empty) on browsers that refuse the hints.
|
|
53
|
+
const high = await ua?.getHighEntropyValues?.(['model']).catch(() => ({}));
|
|
54
|
+
model = str(high?.model); // "" on desktops and iPadOS collapses to null
|
|
55
|
+
}
|
|
56
|
+
catch {
|
|
57
|
+
/* no UA-CH */
|
|
58
|
+
}
|
|
59
|
+
const info = adapter?.info;
|
|
60
|
+
const limits = adapter?.limits;
|
|
61
|
+
return {
|
|
62
|
+
capturedAt: new Date().toISOString(),
|
|
63
|
+
userAgent: nav?.userAgent ?? '',
|
|
64
|
+
platform: platform ?? str(nav?.platform),
|
|
65
|
+
mobile,
|
|
66
|
+
deviceModel: model,
|
|
67
|
+
cpuCores: num(nav?.hardwareConcurrency),
|
|
68
|
+
deviceMemoryGb: num(nav?.deviceMemory),
|
|
69
|
+
screen: {
|
|
70
|
+
width: typeof screen === 'undefined' ? 0 : screen.width,
|
|
71
|
+
height: typeof screen === 'undefined' ? 0 : screen.height,
|
|
72
|
+
dpr: typeof devicePixelRatio === 'undefined' ? 1 : devicePixelRatio,
|
|
73
|
+
},
|
|
74
|
+
touchPoints: num(nav?.maxTouchPoints),
|
|
75
|
+
gpu: {
|
|
76
|
+
vendor: str(info?.vendor),
|
|
77
|
+
architecture: str(info?.architecture),
|
|
78
|
+
description: str(info?.description),
|
|
79
|
+
webglRenderer: probeWebglRenderer(),
|
|
80
|
+
maxStorageBufferBindingSizeMb: mb(limits?.maxStorageBufferBindingSize),
|
|
81
|
+
maxBufferSizeMb: mb(limits?.maxBufferSize),
|
|
82
|
+
},
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Known-bad combinations, shipped with the engine; an app replaces or extends it with the
|
|
87
|
+
* render plugin's `deviceFilter` option. An entry needs a measured failure behind it. (Character XYZ's Brave depth-buffer boot race was checked against this
|
|
88
|
+
* renderer on 2026-10-16 and does not reproduce, so it is not listed. The Galaxy F04's slow
|
|
89
|
+
* indirect draws and staging maps are compatibility-mode costs, handled by the backend's
|
|
90
|
+
* defaults, not by a device row: ADR-0056, ADR-0057.)
|
|
91
|
+
*/
|
|
92
|
+
export const DEVICE_FILTER = [
|
|
93
|
+
{
|
|
94
|
+
id: 'powervr-compat-no-depth-writes',
|
|
95
|
+
match: { vendor: 'img-tec', backend: 'webgpu-compat' },
|
|
96
|
+
action: { useWebGL2: true },
|
|
97
|
+
reason: 'Galaxy F04 (PowerVR GE8320), Chrome 155, WebGPU compatibility mode, 2026-10-20: depth writes never land (a draw tests against the cleared depth, never against earlier draws; every depth format, across passes too), so later draws paint over nearer ones and shadow maps stay empty. WebGL2 on the same phone renders the goldens and is as fast or faster (20k cubes governed 48-57 fps vs 48-52; full PBR 29-38 vs 25).',
|
|
98
|
+
issueUrl: 'https://issues.chromium.org/issues/575736568',
|
|
99
|
+
},
|
|
100
|
+
];
|
|
101
|
+
const TIER_ORDER = ['t0', 't1', 't2'];
|
|
102
|
+
/**
|
|
103
|
+
* Picks the tier: compatibility mode or WebGL2 → T0; a mobile/touch-first device → T1; otherwise T2. Then
|
|
104
|
+
* the device-filter table may lower it. Returns the tier, why, the matching rule ids, and
|
|
105
|
+
* the features the matching rules turn off (an override sets the tier only).
|
|
106
|
+
*/
|
|
107
|
+
export function selectTier(backend, profile, rules = DEVICE_FILTER, override) {
|
|
108
|
+
const has = (hay, needle) => needle === undefined || (hay ?? '').toLowerCase().includes(needle.toLowerCase());
|
|
109
|
+
const matched = rules.filter((r) => has(profile.userAgent, r.match.userAgent) &&
|
|
110
|
+
has(profile.platform, r.match.platform) &&
|
|
111
|
+
has(profile.deviceModel, r.match.device) &&
|
|
112
|
+
has(profile.gpu.vendor, r.match.vendor) &&
|
|
113
|
+
has(profile.gpu.architecture, r.match.architecture) &&
|
|
114
|
+
has(profile.gpu.webglRenderer, r.match.renderer) &&
|
|
115
|
+
(r.match.backend === undefined || r.match.backend === backend));
|
|
116
|
+
const useWebGL2 = backend !== 'webgl2' && matched.some((r) => r.action.useWebGL2);
|
|
117
|
+
const disable = [...new Set(matched.flatMap((r) => r.action.disableFeatures ?? []))];
|
|
118
|
+
if (override)
|
|
119
|
+
return { tier: override, source: 'override', matched: matched.map((r) => r.id), disable, useWebGL2 };
|
|
120
|
+
const touchFirst = profile.mobile === true || ((profile.touchPoints ?? 0) > 0 && Math.min(profile.screen.width, profile.screen.height) < 900);
|
|
121
|
+
let tier = backend !== 'webgpu' ? 't0' : touchFirst ? 't1' : 't2';
|
|
122
|
+
let source = 'caps';
|
|
123
|
+
for (const r of matched) {
|
|
124
|
+
if (r.action.maxTier && TIER_ORDER.indexOf(r.action.maxTier) < TIER_ORDER.indexOf(tier)) {
|
|
125
|
+
tier = r.action.maxTier;
|
|
126
|
+
source = 'filter';
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
return { tier, source, matched: matched.map((r) => r.id), disable, useWebGL2 };
|
|
130
|
+
}
|
|
131
|
+
//# sourceMappingURL=device-profile.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"device-profile.js","sourceRoot":"","sources":["../src/device-profile.ts"],"names":[],"mappings":"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"}
|
package/dist/device.d.ts
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/** Which API the device came from (DESIGN.md §14). */
|
|
2
|
+
export type Backend = 'webgpu' | 'webgpu-compat';
|
|
3
|
+
/** A configured GPU device and its canvas. */
|
|
4
|
+
export interface Gpu {
|
|
5
|
+
readonly adapter: GPUAdapter;
|
|
6
|
+
readonly device: GPUDevice;
|
|
7
|
+
readonly context: GPUCanvasContext;
|
|
8
|
+
readonly format: GPUTextureFormat;
|
|
9
|
+
readonly backend: Backend;
|
|
10
|
+
readonly adapterInfo: {
|
|
11
|
+
vendor: string;
|
|
12
|
+
architecture: string;
|
|
13
|
+
description: string;
|
|
14
|
+
};
|
|
15
|
+
/** Optional features that were available and requested. */
|
|
16
|
+
readonly features: ReadonlySet<string>;
|
|
17
|
+
/** Resolves when the device is lost (DESIGN.md §3: Recovering arrives with increment 6). */
|
|
18
|
+
readonly lost: Promise<GPUDeviceLostInfo>;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Acquires an adapter and device for `canvas`, preferring core WebGPU and retrying in
|
|
22
|
+
* compatibility mode (DESIGN.md §14).
|
|
23
|
+
*
|
|
24
|
+
* Limits are **requested explicitly**: an `adapter.limits` value is only the maximum the
|
|
25
|
+
* adapter *could* grant, and the device gets the spec defaults unless asked. Instanced
|
|
26
|
+
* drawing needs large storage buffers, so those are requested up to the adapter's maximum.
|
|
27
|
+
*
|
|
28
|
+
* @example
|
|
29
|
+
* const gpu = await acquireGpu(document.querySelector('canvas')!);
|
|
30
|
+
*/
|
|
31
|
+
export declare function acquireGpu(canvas: HTMLCanvasElement | OffscreenCanvas, options?: {
|
|
32
|
+
powerPreference?: GPUPowerPreference;
|
|
33
|
+
compat?: boolean;
|
|
34
|
+
/**
|
|
35
|
+
* Called with the chosen adapter before a device or the canvas context exists; throwing
|
|
36
|
+
* here leaves the canvas free for another backend (the device filter's WebGL2 rows).
|
|
37
|
+
*/
|
|
38
|
+
accept?: (adapter: GPUAdapter, backend: Backend) => void | Promise<void>;
|
|
39
|
+
}): Promise<Gpu>;
|
package/dist/device.js
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { YaliError } from '@yali-engine/core';
|
|
2
|
+
/** Optional features Yali requests when the adapter has them. */
|
|
3
|
+
const WANTED_FEATURES = [
|
|
4
|
+
'timestamp-query',
|
|
5
|
+
'shader-f16',
|
|
6
|
+
'indirect-first-instance',
|
|
7
|
+
// Compressed textures (ADR-0060): the texture store samples the best family the device has.
|
|
8
|
+
'texture-compression-bc',
|
|
9
|
+
'texture-compression-astc',
|
|
10
|
+
'texture-compression-etc2',
|
|
11
|
+
];
|
|
12
|
+
/**
|
|
13
|
+
* Acquires an adapter and device for `canvas`, preferring core WebGPU and retrying in
|
|
14
|
+
* compatibility mode (DESIGN.md §14).
|
|
15
|
+
*
|
|
16
|
+
* Limits are **requested explicitly**: an `adapter.limits` value is only the maximum the
|
|
17
|
+
* adapter *could* grant, and the device gets the spec defaults unless asked. Instanced
|
|
18
|
+
* drawing needs large storage buffers, so those are requested up to the adapter's maximum.
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* const gpu = await acquireGpu(document.querySelector('canvas')!);
|
|
22
|
+
*/
|
|
23
|
+
export async function acquireGpu(canvas, options = {}) {
|
|
24
|
+
const gpu = navigator.gpu;
|
|
25
|
+
if (!gpu) {
|
|
26
|
+
throw new YaliError('WEBGPU_UNAVAILABLE', 'This browser has no WebGPU (navigator.gpu is undefined).', 'Use Chrome/Edge 113+, Safari 26+ or Firefox 141+ (Windows); renderPlugin falls back to WebGL2 unless backend is "webgpu" (ADR-0070).');
|
|
27
|
+
}
|
|
28
|
+
let backend = 'webgpu';
|
|
29
|
+
let adapter = options.compat ? null : await gpu.requestAdapter({ powerPreference: options.powerPreference ?? 'high-performance' });
|
|
30
|
+
if (!adapter) {
|
|
31
|
+
adapter = await gpu.requestAdapter({ featureLevel: 'compatibility' });
|
|
32
|
+
backend = 'webgpu-compat';
|
|
33
|
+
}
|
|
34
|
+
if (!adapter) {
|
|
35
|
+
throw new YaliError('WEBGPU_NO_ADAPTER', 'WebGPU is present but no adapter is available (blocklisted GPU or driver?).', 'Update the GPU driver, or check chrome://gpu for why WebGPU is disabled.');
|
|
36
|
+
}
|
|
37
|
+
await options.accept?.(adapter, backend);
|
|
38
|
+
const features = WANTED_FEATURES.filter((f) => adapter.features.has(f));
|
|
39
|
+
const limits = adapter.limits;
|
|
40
|
+
const requiredLimits = {
|
|
41
|
+
maxStorageBufferBindingSize: limits.maxStorageBufferBindingSize,
|
|
42
|
+
maxBufferSize: limits.maxBufferSize,
|
|
43
|
+
maxComputeWorkgroupsPerDimension: limits.maxComputeWorkgroupsPerDimension,
|
|
44
|
+
// The shadow atlas is cascades × size wide (ADR-0068); compatibility mode defaults to 4096.
|
|
45
|
+
maxTextureDimension2D: limits.maxTextureDimension2D,
|
|
46
|
+
// GPU cloth runs a cloth per workgroup, as wide as the device allows (ADR-0101).
|
|
47
|
+
maxComputeInvocationsPerWorkgroup: limits.maxComputeInvocationsPerWorkgroup,
|
|
48
|
+
maxComputeWorkgroupSizeX: limits.maxComputeWorkgroupSizeX,
|
|
49
|
+
// The cluster cull's page bits are a ninth storage buffer (ADR-0134); a device without one streams coarse first.
|
|
50
|
+
maxStorageBuffersPerShaderStage: limits.maxStorageBuffersPerShaderStage,
|
|
51
|
+
};
|
|
52
|
+
// Compatibility mode grants 0 storage buffers to the vertex and fragment stages unless
|
|
53
|
+
// asked: request what the adapter allows, and the renderer picks its path from the result.
|
|
54
|
+
for (const name of ['maxStorageBuffersInVertexStage', 'maxStorageBuffersInFragmentStage']) {
|
|
55
|
+
const v = limits[name];
|
|
56
|
+
if (typeof v === 'number')
|
|
57
|
+
requiredLimits[name] = v;
|
|
58
|
+
}
|
|
59
|
+
const device = await adapter.requestDevice({ requiredFeatures: features, requiredLimits });
|
|
60
|
+
const context = canvas.getContext('webgpu');
|
|
61
|
+
if (!context) {
|
|
62
|
+
throw new YaliError('WEBGPU_NO_CONTEXT', 'canvas.getContext("webgpu") returned null.', 'Use a canvas that has no other context.');
|
|
63
|
+
}
|
|
64
|
+
const format = gpu.getPreferredCanvasFormat();
|
|
65
|
+
// COPY_SRC lets `render.capture()` read back exactly what was drawn (golden images, MCP).
|
|
66
|
+
context.configure({ device, format, alphaMode: 'opaque', usage: GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.COPY_SRC });
|
|
67
|
+
const info = adapter.info;
|
|
68
|
+
return {
|
|
69
|
+
adapter,
|
|
70
|
+
device,
|
|
71
|
+
context,
|
|
72
|
+
format,
|
|
73
|
+
backend,
|
|
74
|
+
adapterInfo: { vendor: info.vendor, architecture: info.architecture, description: info.description },
|
|
75
|
+
features: new Set(features),
|
|
76
|
+
lost: device.lost,
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
//# sourceMappingURL=device.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"device.js","sourceRoot":"","sources":["../src/device.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import type { PipelineCache } from './pipeline-cache.ts';
|
|
2
|
+
/** A procedural sky (M2 increment 5): every colour linear, the sun a radiance. */
|
|
3
|
+
export interface SkyOptions {
|
|
4
|
+
/** Direction towards the sun (normalized here). */
|
|
5
|
+
readonly sun?: readonly [number, number, number];
|
|
6
|
+
readonly zenith?: readonly [number, number, number];
|
|
7
|
+
readonly horizon?: readonly [number, number, number];
|
|
8
|
+
readonly ground?: readonly [number, number, number];
|
|
9
|
+
/** Radiance of the sun disk in the sky (what reflections see). */
|
|
10
|
+
readonly sunDisk?: readonly [number, number, number];
|
|
11
|
+
/** Angular radius of the disk, degrees. */
|
|
12
|
+
readonly sunAngle?: number;
|
|
13
|
+
}
|
|
14
|
+
/** The default sky. */
|
|
15
|
+
export declare const DEFAULT_SKY: Required<SkyOptions>;
|
|
16
|
+
/** Texels per cube face at the base level, and its mip count. */
|
|
17
|
+
export declare const ENV_SIZE = 128;
|
|
18
|
+
export declare const ENV_MIPS = 6;
|
|
19
|
+
/**
|
|
20
|
+
* The environment for image-based lighting (M2 increment 5): a procedural sky rendered into a
|
|
21
|
+
* cube map, each lower mip GGX-prefiltered for its roughness (`shaders/env/sky.wesl`), and the
|
|
22
|
+
* same sky projected onto nine spherical-harmonic coefficients for diffuse irradiance (CPU).
|
|
23
|
+
* Built once at start and again when `setSky` changes it; render passes only, so compatibility
|
|
24
|
+
* mode can run it. The draw reads `prefiltered` and `sh9`.
|
|
25
|
+
*/
|
|
26
|
+
export declare class Environment {
|
|
27
|
+
#private;
|
|
28
|
+
readonly device: GPUDevice;
|
|
29
|
+
/** Cube map the draw samples by roughness (mip m: roughness m / (mips − 1)). */
|
|
30
|
+
readonly prefiltered: GPUTexture;
|
|
31
|
+
readonly mips = 6;
|
|
32
|
+
/** Irradiance / π as SH9, 9 × vec4 (rgb, 0): the draw's diffuse ambient for a normal n. */
|
|
33
|
+
readonly sh9: Float32Array<ArrayBuffer>;
|
|
34
|
+
/** Bumped when the cube or SH changed. */
|
|
35
|
+
version: number;
|
|
36
|
+
constructor(device: GPUDevice);
|
|
37
|
+
init(cache: PipelineCache): Promise<void>;
|
|
38
|
+
/** Changes the sky (unset fields keep their values) and rebuilds the cube and SH. */
|
|
39
|
+
setSky(sky: SkyOptions): void;
|
|
40
|
+
/** The current sky. */
|
|
41
|
+
get sky(): Readonly<Required<SkyOptions>>;
|
|
42
|
+
/** Renders the sky into every mip of the base cube, then prefilters; projects SH9. */
|
|
43
|
+
build(): void;
|
|
44
|
+
destroy(): void;
|
|
45
|
+
}
|
|
46
|
+
/** `sky` with `changes`' set fields applied. */
|
|
47
|
+
export declare function mergeSky(sky: Required<SkyOptions>, changes: SkyOptions): Required<SkyOptions>;
|
|
48
|
+
/** The sky pass's `SkyParams` (24 floats) for cube face `f` of mip `m`, at `out[o]`: every backend packs it so. */
|
|
49
|
+
export declare function packSkyParams(out: Float32Array, o: number, s: Required<SkyOptions>, m: number, f: number): void;
|
|
50
|
+
/** The SH9 irradiance of sky `s` (`Environment.sh9`), into `out`. */
|
|
51
|
+
export declare function skySh9(out: Float32Array, s: Required<SkyOptions>): void;
|
|
52
|
+
/** `sky` of shaders/env/sky.wesl, in TS (for the SH projection). */
|
|
53
|
+
export declare function skyRadiance(s: Required<SkyOptions>, sun: readonly number[], d: readonly number[]): [number, number, number];
|
|
54
|
+
/**
|
|
55
|
+
* Projects `radiance` onto real SH up to l = 2 and convolves with the clamped cosine
|
|
56
|
+
* (Ramamoorthi & Hanrahan 2001), divided by π: `out` evaluated at n is the Lambert
|
|
57
|
+
* diffuse radiance for albedo 1. Coefficient order: Y00, Y1-1 (y), Y10 (z), Y11 (x), Y2-2 (xy),
|
|
58
|
+
* Y2-1 (yz), Y20, Y21 (xz), Y22 (x² − y²): `sh_irradiance` in draw.wesl reads the same.
|
|
59
|
+
*/
|
|
60
|
+
export declare function projectSh9(out: Float32Array, radiance: (d: readonly number[]) => readonly number[]): void;
|