@aethervtt/shard-render 0.0.1 → 0.2.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.
Files changed (170) hide show
  1. package/dist/ablation.d.ts +74 -0
  2. package/dist/ablation.js +117 -0
  3. package/dist/assets.d.ts +3 -0
  4. package/dist/assets.js +43 -2
  5. package/dist/atmosphere-nodes.d.ts +9 -0
  6. package/dist/atmosphere-nodes.js +157 -30
  7. package/dist/atmosphere-plugin.js +8 -2
  8. package/dist/atmosphere-shaders.js +17 -5
  9. package/dist/baseline/atmosphere.d.ts +16 -0
  10. package/dist/baseline/atmosphere.js +301 -0
  11. package/dist/baseline/data-texture.d.ts +36 -0
  12. package/dist/baseline/data-texture.js +181 -0
  13. package/dist/baseline/environment.d.ts +14 -0
  14. package/dist/baseline/environment.js +368 -0
  15. package/dist/baseline/exposure.d.ts +25 -0
  16. package/dist/baseline/exposure.js +133 -0
  17. package/dist/baseline/lights.d.ts +33 -0
  18. package/dist/baseline/lights.js +133 -0
  19. package/dist/baseline/textures.d.ts +97 -0
  20. package/dist/baseline/textures.js +203 -0
  21. package/dist/camera.d.ts +2 -0
  22. package/dist/camera.js +4 -0
  23. package/dist/crowd.d.ts +21 -0
  24. package/dist/crowd.js +121 -0
  25. package/dist/culling-shaders.js +21 -2
  26. package/dist/culling.d.ts +11 -2
  27. package/dist/culling.js +44 -18
  28. package/dist/cutaway-plugin.d.ts +48 -0
  29. package/dist/cutaway-plugin.js +364 -0
  30. package/dist/cutaway.d.ts +27 -0
  31. package/dist/cutaway.js +49 -0
  32. package/dist/data-store.d.ts +69 -0
  33. package/dist/data-store.js +105 -0
  34. package/dist/debug-shaders.js +17 -3
  35. package/dist/debug-views.js +4 -2
  36. package/dist/decals-plugin.d.ts +5 -0
  37. package/dist/decals-plugin.js +54 -0
  38. package/dist/decals.d.ts +69 -0
  39. package/dist/decals.js +326 -0
  40. package/dist/deferred-shaders.js +2 -0
  41. package/dist/deferred.js +28 -4
  42. package/dist/deform.d.ts +5 -4
  43. package/dist/deform.js +4 -9
  44. package/dist/describe-plugin.d.ts +4 -2
  45. package/dist/describe-plugin.js +75 -3
  46. package/dist/display-nodes.js +37 -13
  47. package/dist/dynamic-resolution.d.ts +2 -0
  48. package/dist/dynamic-resolution.js +6 -1
  49. package/dist/environment-plugin.js +17 -2
  50. package/dist/environment-state.d.ts +7 -3
  51. package/dist/environment-state.js +20 -9
  52. package/dist/environment.js +7 -1
  53. package/dist/features.d.ts +49 -0
  54. package/dist/features.js +106 -0
  55. package/dist/foliage-budget.d.ts +64 -0
  56. package/dist/foliage-budget.js +108 -0
  57. package/dist/foliage-path.d.ts +19 -0
  58. package/dist/foliage-path.js +7 -0
  59. package/dist/foliage-shaders.d.ts +11 -0
  60. package/dist/foliage-shaders.js +504 -0
  61. package/dist/foliage.d.ts +194 -0
  62. package/dist/foliage.js +1027 -0
  63. package/dist/forward.d.ts +38 -2
  64. package/dist/forward.js +608 -186
  65. package/dist/fxaa.js +12 -2
  66. package/dist/gizmos-plugin.js +7 -0
  67. package/dist/gizmos.d.ts +3 -3
  68. package/dist/gizmos.js +11 -13
  69. package/dist/gpu-assets.d.ts +58 -4
  70. package/dist/gpu-assets.js +191 -8
  71. package/dist/graph.d.ts +45 -0
  72. package/dist/graph.js +169 -19
  73. package/dist/health.d.ts +58 -0
  74. package/dist/health.js +112 -0
  75. package/dist/index.d.ts +40 -11
  76. package/dist/index.js +343 -9
  77. package/dist/instances.d.ts +119 -8
  78. package/dist/instances.js +387 -78
  79. package/dist/interior-plugin.d.ts +121 -0
  80. package/dist/interior-plugin.js +465 -0
  81. package/dist/interior.d.ts +24 -0
  82. package/dist/interior.js +13 -0
  83. package/dist/layers.d.ts +35 -0
  84. package/dist/layers.js +49 -0
  85. package/dist/lens-plugin.d.ts +43 -0
  86. package/dist/lens-plugin.js +304 -0
  87. package/dist/lens.d.ts +41 -0
  88. package/dist/lens.js +99 -0
  89. package/dist/lights.d.ts +50 -5
  90. package/dist/lights.js +101 -22
  91. package/dist/material-pipelines.d.ts +37 -4
  92. package/dist/material-pipelines.js +102 -5
  93. package/dist/materials.d.ts +23 -0
  94. package/dist/materials.js +44 -1
  95. package/dist/outline-plugin.d.ts +48 -0
  96. package/dist/outline-plugin.js +529 -0
  97. package/dist/outline.d.ts +15 -0
  98. package/dist/outline.js +42 -0
  99. package/dist/overlay-registry.d.ts +3 -1
  100. package/dist/overlay-registry.js +3 -1
  101. package/dist/overlays.js +6 -2
  102. package/dist/perf-overlay.d.ts +9 -0
  103. package/dist/perf-overlay.js +125 -0
  104. package/dist/picking.d.ts +17 -0
  105. package/dist/picking.js +40 -11
  106. package/dist/pixel-perfect-plugin.js +10 -2
  107. package/dist/plugin.d.ts +69 -7
  108. package/dist/plugin.js +223 -25
  109. package/dist/post-common.d.ts +22 -2
  110. package/dist/post-common.js +82 -2
  111. package/dist/post-nodes.d.ts +7 -1
  112. package/dist/post-nodes.js +235 -111
  113. package/dist/post-plugin.js +5 -3
  114. package/dist/post-scene.d.ts +130 -0
  115. package/dist/post-scene.js +164 -0
  116. package/dist/post-shaders.js +85 -62
  117. package/dist/post.js +11 -1
  118. package/dist/projection.d.ts +24 -0
  119. package/dist/projection.js +87 -0
  120. package/dist/readback.d.ts +1 -0
  121. package/dist/readback.js +1 -0
  122. package/dist/render-scale.js +1 -0
  123. package/dist/ribbons-plugin.d.ts +5 -0
  124. package/dist/ribbons-plugin.js +59 -0
  125. package/dist/ribbons.d.ts +44 -0
  126. package/dist/ribbons.js +288 -0
  127. package/dist/scenario.d.ts +70 -0
  128. package/dist/scenario.js +125 -0
  129. package/dist/screen-effects.d.ts +97 -0
  130. package/dist/screen-effects.js +192 -0
  131. package/dist/shaders.d.ts +13 -0
  132. package/dist/shaders.js +189 -34
  133. package/dist/shadow-catcher.d.ts +9 -0
  134. package/dist/shadow-catcher.js +133 -0
  135. package/dist/shadows.d.ts +27 -2
  136. package/dist/shadows.js +100 -20
  137. package/dist/standard-fields.d.ts +1 -0
  138. package/dist/standard-fields.js +5 -0
  139. package/dist/standard.js +18 -0
  140. package/dist/stats.d.ts +45 -1
  141. package/dist/stats.js +91 -3
  142. package/dist/surface-model.d.ts +57 -0
  143. package/dist/surface-model.js +289 -0
  144. package/dist/surface-shaders.d.ts +3 -0
  145. package/dist/surface-shaders.js +160 -0
  146. package/dist/surface.d.ts +40 -0
  147. package/dist/surface.js +86 -0
  148. package/dist/target.d.ts +1 -13
  149. package/dist/target.js +1 -26
  150. package/dist/testing.d.ts +11 -0
  151. package/dist/testing.js +39 -1
  152. package/dist/tier.d.ts +13 -0
  153. package/dist/tier.js +14 -0
  154. package/dist/timer.d.ts +18 -1
  155. package/dist/timer.js +73 -20
  156. package/dist/variants.d.ts +37 -0
  157. package/dist/variants.js +104 -0
  158. package/dist/vfx-draw.d.ts +37 -0
  159. package/dist/vfx-draw.js +291 -0
  160. package/dist/vfx-state.d.ts +42 -0
  161. package/dist/vfx-state.js +81 -0
  162. package/dist/vfx.d.ts +69 -0
  163. package/dist/vfx.js +103 -0
  164. package/dist/view-visibility-plugin.d.ts +61 -0
  165. package/dist/view-visibility-plugin.js +267 -0
  166. package/dist/view-visibility.d.ts +28 -0
  167. package/dist/view-visibility.js +46 -0
  168. package/dist/view.d.ts +29 -1
  169. package/dist/view.js +57 -34
  170. package/package.json +23 -11
package/dist/graph.js CHANGED
@@ -1,4 +1,11 @@
1
- import { ShardError } from "@aethervtt/shard-core";
1
+ import {
2
+ defineSpan,
3
+ ProfilerResource,
4
+ ShardError,
5
+ TRACK
6
+ } from "@aethervtt/shard-core";
7
+ import { flushDataStores } from "./data-store.js";
8
+ import { unsupportedNodes } from "./features.js";
2
9
  import { TexturePool } from "./pool.js";
3
10
  import { BYTES_PER_TEXEL, toFloats } from "./readback.js";
4
11
  import { GpuTimer } from "./timer.js";
@@ -10,8 +17,14 @@ const RenderPhase = {
10
17
  Opaque: 300,
11
18
  Lighting: 350,
12
19
  Sky: 400,
20
+ /** Coplanar ground bands (0057): after the sky, which draws where no depth was written. */
21
+ Ground: 420,
13
22
  Sprites: 450,
14
23
  Transparent: 500,
24
+ /** Projected fog's composite (0058): after transparent objects, before the overlay band. */
25
+ Fog: 520,
26
+ /** Ground bands from the overlay band up, over fog (0058). */
27
+ Overlay3d: 530,
15
28
  Effects3d: 550,
16
29
  Resolve: 600,
17
30
  Post: 700,
@@ -122,17 +135,43 @@ class RenderGraph {
122
135
  /** Views rendered in the last frame, in order. */
123
136
  lastViews = [];
124
137
  viewData = /* @__PURE__ */ new Map();
138
+ /** Each node's `render/<node>` span and its CPU time this frame, summed over views (0074). */
139
+ nodeSlots = /* @__PURE__ */ new Map();
140
+ nodeSpans = [];
141
+ nodeMs = new Float64Array(16);
142
+ nodeRan = new Uint8Array(16);
143
+ /** Nodes disabled for measurement (0075): see `ablate`. */
144
+ ablatedNodes = /* @__PURE__ */ new Set();
125
145
  constructor(gpu) {
126
146
  this.gpu = gpu;
127
147
  this.pool = new TexturePool(gpu);
128
148
  this.timer = new GpuTimer(gpu);
129
149
  }
150
+ /** Rejects pending captures and destroys the timer's query set (the app is being disposed). */
151
+ dispose() {
152
+ const error = new ShardError("render/disposed", "The app was disposed before the capture ran");
153
+ for (const capture of this.captures.splice(0)) capture.reject(error);
154
+ this.timer.destroy();
155
+ }
130
156
  addNode(name, node) {
131
157
  if (this.nodes.has(name)) {
132
158
  throw new ShardError("render/duplicate-node", `Render graph node "${name}" already exists`);
133
159
  }
134
160
  this.nodes.set(name, node);
135
161
  this.resolutions.clear();
162
+ if (!this.nodeSlots.has(name)) {
163
+ const slot = this.nodeSpans.length;
164
+ this.nodeSlots.set(name, slot);
165
+ this.nodeSpans.push(defineSpan(`render/${name}`));
166
+ if (slot >= this.nodeMs.length) {
167
+ const ms = new Float64Array(this.nodeMs.length * 2);
168
+ ms.set(this.nodeMs);
169
+ this.nodeMs = ms;
170
+ const ran = new Uint8Array(this.nodeRan.length * 2);
171
+ ran.set(this.nodeRan);
172
+ this.nodeRan = ran;
173
+ }
174
+ }
136
175
  }
137
176
  removeNode(name) {
138
177
  this.nodes.delete(name);
@@ -141,6 +180,34 @@ class RenderGraph {
141
180
  hasNode(name) {
142
181
  return this.nodes.has(name);
143
182
  }
183
+ /** Every node's name, in the order they were added. */
184
+ nodeNames() {
185
+ return [...this.nodes.keys()];
186
+ }
187
+ /**
188
+ * Disables nodes for measurement (0075's ablation), replacing the previous set; `[]` restores
189
+ * them. A disabled render or compute node still begins and ends its pass, with its attachments'
190
+ * clears, so the textures it writes stay valid for what reads them; only its `run` (its draws and
191
+ * dispatches) is skipped. A raw node, which begins its own passes, is skipped whole. The graph
192
+ * resolves as before: what the node fed still runs, on whatever its textures hold. The image is
193
+ * wrong while nodes are disabled. Throws `render/unknown-node` for a name the graph lacks.
194
+ */
195
+ ablate(names) {
196
+ const next = [...names];
197
+ for (const name of next) {
198
+ if (!this.nodes.has(name)) {
199
+ throw new ShardError("render/unknown-node", `No render graph node "${name}"`, {
200
+ hint: `Nodes: ${this.nodeNames().join(", ")}.`
201
+ });
202
+ }
203
+ }
204
+ this.ablatedNodes.clear();
205
+ for (const name of next) this.ablatedNodes.add(name);
206
+ }
207
+ /** The nodes `ablate` disabled. */
208
+ ablated() {
209
+ return [...this.ablatedNodes];
210
+ }
144
211
  /** The data of a view rendered in the last frame. */
145
212
  lastViewData(view) {
146
213
  return this.viewData.get(view);
@@ -153,11 +220,12 @@ class RenderGraph {
153
220
  resolve() {
154
221
  return this.resolveFor(void 0);
155
222
  }
156
- resolveFor(view) {
223
+ /** `off`: nodes that don't run on this device (features the baseline tier doesn't support). */
224
+ resolveFor(view, off) {
157
225
  let key = "";
158
226
  const enabled = /* @__PURE__ */ new Map();
159
227
  for (const [name, node] of this.nodes) {
160
- const on = !view || !node.enabled || node.enabled(view);
228
+ const on = (!view || !node.enabled || node.enabled(view)) && !off?.has(name);
161
229
  key += on ? "1" : "0";
162
230
  if (on) enabled.set(name, node);
163
231
  }
@@ -181,6 +249,47 @@ class RenderGraph {
181
249
  }
182
250
  return resolved;
183
251
  }
252
+ /**
253
+ * Whether render node `order[k]` can draw in the pass `order[from]` began: the same color
254
+ * attachments, loaded rather than cleared, the same depth (written only if the pass writes it),
255
+ * and nothing it samples bound as an attachment.
256
+ */
257
+ continues(order, from, k, view) {
258
+ const a = this.nodes.get(order[from]);
259
+ const b = this.nodes.get(order[k]);
260
+ if (a.ownPass || b.ownPass) return false;
261
+ const aliases = view.aliases;
262
+ const ca = colorOf(a, view);
263
+ const cb = colorOf(b, view);
264
+ if (ca.length !== cb.length) return false;
265
+ for (let i = 0; i < ca.length; i++) {
266
+ const x = ca[i];
267
+ const y = cb[i];
268
+ if (y.clear !== void 0 || (x.mip ?? 0) !== (y.mip ?? 0)) return false;
269
+ if (canonicalOf(aliases, x.resource) !== canonicalOf(aliases, y.resource)) return false;
270
+ if (x.resolve === void 0 !== (y.resolve === void 0)) return false;
271
+ if (x.resolve && canonicalOf(aliases, x.resolve) !== canonicalOf(aliases, y.resolve))
272
+ return false;
273
+ }
274
+ const da = a.depth;
275
+ const db = b.depth;
276
+ if (!da !== !db) return false;
277
+ if (da && db) {
278
+ if (canonicalOf(aliases, da.resource) !== canonicalOf(aliases, db.resource)) return false;
279
+ if (db.clear !== void 0 || da.readOnly && !db.readOnly) return false;
280
+ }
281
+ const reads = b.reads ?? [];
282
+ for (let r = 0; r < reads.length; r++) {
283
+ const c = canonicalOf(aliases, reads[r]);
284
+ if (da && c === canonicalOf(aliases, da.resource)) return false;
285
+ for (let i = 0; i < ca.length; i++) {
286
+ const x = ca[i];
287
+ if (c === canonicalOf(aliases, x.resource)) return false;
288
+ if (x.resolve && c === canonicalOf(aliases, x.resolve)) return false;
289
+ }
290
+ }
291
+ return true;
292
+ }
184
293
  /** Whether a node after `k` loads or reads `resource` (so its contents must be stored). */
185
294
  loadedLater(order, k, resource, view) {
186
295
  const canonical = canonicalOf(view.aliases, resource);
@@ -233,8 +342,10 @@ class RenderGraph {
233
342
  this.lastViews = sorted.map((v) => v.name);
234
343
  this.viewData.clear();
235
344
  for (const v of sorted) this.viewData.set(v.name, v.data);
345
+ const off = this.gpu.tier === "baseline" ? unsupportedNodes(world) : void 0;
346
+ const profiler = world.tryResource(ProfilerResource);
236
347
  for (const view of sorted) {
237
- const resolved = this.resolveFor(view);
348
+ const resolved = this.resolveFor(view, off);
238
349
  const order = resolved.order;
239
350
  this.lastByView.set(view.name, resolved);
240
351
  const textures = /* @__PURE__ */ new Map();
@@ -264,25 +375,43 @@ class RenderGraph {
264
375
  const vh = view.height ?? view.target.height;
265
376
  const px = size === "view" ? [vw, vh] : Array.isArray(size) ? size : [Math.max(1, Math.ceil(vw / size.divide)), Math.max(1, Math.ceil(vh / size.divide))];
266
377
  const format = perView(desc.format, view);
378
+ const sampleCount = perView(desc.sampleCount ?? 1, view);
379
+ let usage = (desc.usage ?? GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.TEXTURE_BINDING) | GPUTextureUsage.COPY_SRC;
380
+ if (sampleCount > 1 && this.gpu.tier === "baseline")
381
+ usage &= ~GPUTextureUsage.TEXTURE_BINDING;
267
382
  t = this.pool.acquire({
268
383
  label: `${view.name}/${canonical}`,
269
384
  format: format === "view" ? view.target.format : format,
270
385
  size: px,
271
- sampleCount: perView(desc.sampleCount ?? 1, view),
386
+ sampleCount,
272
387
  mipLevelCount: perView(desc.mipLevelCount ?? 1, view),
273
- usage: (desc.usage ?? GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.TEXTURE_BINDING) | GPUTextureUsage.COPY_SRC
388
+ usage
274
389
  });
275
390
  textures.set(canonical, t);
276
391
  return t;
277
392
  };
393
+ const provide = (name, t) => {
394
+ textures.set(canonicalOf(view.aliases, name), t);
395
+ };
278
396
  const timestamps = (name) => this.timer.allocate(name);
279
397
  const afterSubmit = (fn) => void submitted.push(fn);
398
+ let open;
399
+ let openFrom = -1;
280
400
  for (let k = 0; k < order.length; k++) {
281
401
  const name = order[k];
282
402
  const node = this.nodes.get(name);
403
+ const ablated = this.ablatedNodes.size > 0 && this.ablatedNodes.has(name);
404
+ if (ablated && node.kind === "raw") continue;
405
+ const t0 = profiler !== void 0 ? profiler.now() : 0;
283
406
  let renderPass;
284
407
  let computePass;
285
- if (node.kind === "render") {
408
+ if (node.kind === "render" && open && this.continues(order, openFrom, k, view)) {
409
+ renderPass = open;
410
+ } else {
411
+ open?.end();
412
+ open = void 0;
413
+ }
414
+ if (node.kind === "render" && !renderPass) {
286
415
  const colors = colorOf(node, view);
287
416
  renderPass = encoder.beginRenderPass({
288
417
  label: `${view.name}/${name}`,
@@ -302,26 +431,38 @@ class RenderGraph {
302
431
  } : void 0,
303
432
  timestampWrites: this.timer.allocate(name)
304
433
  });
434
+ open = renderPass;
435
+ openFrom = k;
305
436
  } else if (node.kind === "compute") {
306
437
  computePass = encoder.beginComputePass({
307
438
  label: `${view.name}/${name}`,
308
439
  timestampWrites: this.timer.allocate(name)
309
440
  });
310
441
  }
311
- node.run({
312
- gpu: this.gpu,
313
- world,
314
- view,
315
- encoder,
316
- renderPass,
317
- computePass,
318
- texture,
319
- timestamps,
320
- afterSubmit
321
- });
322
- renderPass?.end();
442
+ if (!ablated) {
443
+ node.run({
444
+ gpu: this.gpu,
445
+ world,
446
+ view,
447
+ encoder,
448
+ renderPass,
449
+ computePass,
450
+ texture,
451
+ provide,
452
+ timestamps,
453
+ afterSubmit
454
+ });
455
+ }
323
456
  computePass?.end();
457
+ if (profiler !== void 0) {
458
+ const ms = profiler.now() - t0;
459
+ const slot = this.nodeSlots.get(name);
460
+ this.nodeMs[slot] = this.nodeMs[slot] + ms;
461
+ this.nodeRan[slot] = 1;
462
+ profiler.event(this.nodeSpans[slot], TRACK.main, t0, ms);
463
+ }
324
464
  }
465
+ open?.end();
325
466
  for (const capture of this.captures) {
326
467
  if (capture.view !== view.name) continue;
327
468
  let source;
@@ -356,8 +497,17 @@ class RenderGraph {
356
497
  readbacks.push({ capture, buffer, width, height, format, bytesPerRow });
357
498
  }
358
499
  }
500
+ if (profiler !== void 0) {
501
+ for (let slot = 0; slot < this.nodeSpans.length; slot++) {
502
+ if (!this.nodeRan[slot]) continue;
503
+ profiler.sample(this.nodeSpans[slot], this.nodeMs[slot]);
504
+ this.nodeMs[slot] = 0;
505
+ this.nodeRan[slot] = 0;
506
+ }
507
+ }
359
508
  this.captures = this.captures.filter((c) => !readbacks.some((r) => r.capture === c));
360
509
  this.timer.resolve(encoder);
510
+ flushDataStores(this.gpu);
361
511
  device.queue.submit([encoder.finish()]);
362
512
  this.timer.readback(world);
363
513
  for (const fn of submitted) fn();
@@ -0,0 +1,58 @@
1
+ import { type World } from '@aethervtt/shard-core';
2
+ import type { GpuStatus } from '@aethervtt/shard-gpu';
3
+ export type RenderHealthState = 'ok' | 'degraded' | 'lost' | 'failed';
4
+ export interface RenderHealthIssue {
5
+ /** `package/what-happened`, e.g. `assets/load-failed`, `render/material-fallback`. */
6
+ code: string;
7
+ message: string;
8
+ /** What it's about: an asset path, a material type, a feature. */
9
+ ref?: string;
10
+ /** When it was first seen, in ms since the epoch. */
11
+ since: number;
12
+ /**
13
+ * `degraded` issues make the state `degraded` (something draws a fallback or not at all); `info`
14
+ * ones are reported without changing it (a shader cache miss in a dev build).
15
+ */
16
+ severity: 'degraded' | 'info';
17
+ }
18
+ export interface RenderHealthValue {
19
+ /**
20
+ * `ok`; `degraded` while any fallback, failed pipeline or unsupported feature is in use; `lost`
21
+ * between a device loss and its recovery; `failed` when recovery gave up.
22
+ */
23
+ state: RenderHealthState;
24
+ issues: RenderHealthIssue[];
25
+ }
26
+ export declare const RenderHealth: import("@aethervtt/shard-core").ResourceDef<RenderHealthValue>;
27
+ export declare const RenderHealthChanged: import("@aethervtt/shard-core").EventDef<{
28
+ from: RenderHealthState;
29
+ to: RenderHealthState;
30
+ issues: RenderHealthIssue[];
31
+ }>;
32
+ type Raised = Omit<RenderHealthIssue, 'since'>;
33
+ /** Issues raised by code other than the renderer's own checks (unsupported features, 0064). */
34
+ export declare const RenderHealthReports: import("@aethervtt/shard-core").ResourceDef<{
35
+ issues: Map<string, Raised>;
36
+ version: number;
37
+ }>;
38
+ /** Raises an issue until `clearHealthIssue` (same code and ref). Raising it again does nothing. */
39
+ export declare function raiseHealthIssue(world: World, issue: Raised): void;
40
+ export declare function clearHealthIssue(world: World, code: string, ref?: string): void;
41
+ /** Material types drawing through the standard fallback (filled by the forward plugin). */
42
+ export declare const MaterialFallbacks: import("@aethervtt/shard-core").ResourceDef<{
43
+ count(): number;
44
+ list(): readonly {
45
+ type: string;
46
+ error: {
47
+ code: string;
48
+ message: string;
49
+ };
50
+ }[];
51
+ }>;
52
+ /** Where the device status comes from (the render plugin's GPU context). */
53
+ export interface HealthSources {
54
+ status(): GpuStatus;
55
+ }
56
+ /** Rebuilds RenderHealth when its inputs change, and sends RenderHealthChanged on transitions. */
57
+ export declare function healthSystem(sources: HealthSources): import("@aethervtt/shard-core").SystemDef<undefined>;
58
+ export {};
package/dist/health.js ADDED
@@ -0,0 +1,112 @@
1
+ import { AssetServerResource } from "@aethervtt/shard-assets";
2
+ import { defineEvent, defineResource, defineSystem } from "@aethervtt/shard-core";
3
+ const RenderHealth = defineResource("render/Health", {
4
+ description: "The renderer's health: ok, degraded (fallbacks in use), lost (device reset in progress) or failed, with the issues behind it.",
5
+ init: () => ({ state: "ok", issues: [] })
6
+ });
7
+ const RenderHealthChanged = defineEvent("render/HealthChanged", {
8
+ description: "RenderHealth moved to another state (ok, degraded, lost, failed)."
9
+ });
10
+ const RenderHealthReports = defineResource(
11
+ "render/HealthReports",
12
+ {
13
+ description: "Health issues raised by features and backends, by code and ref.",
14
+ init: () => ({ issues: /* @__PURE__ */ new Map(), version: 0 })
15
+ }
16
+ );
17
+ function raiseHealthIssue(world, issue) {
18
+ const reports = world.initResource(RenderHealthReports);
19
+ const key = `${issue.code}|${issue.ref ?? ""}`;
20
+ const had = reports.issues.get(key);
21
+ if (had && had.message === issue.message && had.severity === issue.severity) return;
22
+ reports.issues.set(key, issue);
23
+ reports.version++;
24
+ }
25
+ function clearHealthIssue(world, code, ref) {
26
+ const reports = world.tryResource(RenderHealthReports);
27
+ if (reports?.issues.delete(`${code}|${ref ?? ""}`)) reports.version++;
28
+ }
29
+ const MaterialFallbacks = defineResource("render/MaterialFallbacks", {
30
+ description: "Material types whose shader or pipeline failed, drawn with the standard one."
31
+ });
32
+ function healthSystem(sources) {
33
+ const seen = { status: "", fallbacks: -1, materials: -1, reports: -1 };
34
+ const since = /* @__PURE__ */ new Map();
35
+ return defineSystem({
36
+ name: "render/health",
37
+ description: "Keeps RenderHealth current: device status, fallbacks in use, raised issues.",
38
+ run: (_, world) => {
39
+ const status = sources.status();
40
+ const server = world.tryResource(AssetServerResource);
41
+ const fallbacks = server?.fallbackCount ?? 0;
42
+ const fallbackMaterials = world.tryResource(MaterialFallbacks);
43
+ const materials = fallbackMaterials?.count() ?? 0;
44
+ const reports = world.tryResource(RenderHealthReports)?.version ?? 0;
45
+ if (status === seen.status && fallbacks === seen.fallbacks && materials === seen.materials && reports === seen.reports)
46
+ return;
47
+ seen.status = status;
48
+ seen.fallbacks = fallbacks;
49
+ seen.materials = materials;
50
+ seen.reports = reports;
51
+ const now = Date.now();
52
+ const issues = [];
53
+ const add = (issue) => {
54
+ const key = `${issue.code}|${issue.ref ?? ""}`;
55
+ let at = since.get(key);
56
+ if (at === void 0) {
57
+ at = now;
58
+ since.set(key, at);
59
+ }
60
+ issues.push({ ...issue, since: at });
61
+ };
62
+ if (status === "lost") {
63
+ add({
64
+ code: "gpu/device-lost",
65
+ message: "The GPU device was lost; recovering",
66
+ severity: "degraded"
67
+ });
68
+ } else if (status === "failed") {
69
+ add({
70
+ code: "gpu/recovery-failed",
71
+ message: "The GPU device couldn't be replaced",
72
+ severity: "degraded"
73
+ });
74
+ }
75
+ for (const f of server?.failed() ?? []) {
76
+ add({ code: f.code, message: f.message, ref: f.path, severity: "degraded" });
77
+ }
78
+ for (const f of fallbackMaterials?.list() ?? []) {
79
+ add({
80
+ code: "render/material-fallback",
81
+ message: `Drawn with the standard material: ${f.error.message}`,
82
+ ref: f.type,
83
+ severity: "degraded"
84
+ });
85
+ }
86
+ for (const issue of world.tryResource(RenderHealthReports)?.issues.values() ?? []) add(issue);
87
+ for (const key of since.keys()) {
88
+ if (!issues.some((i) => `${i.code}|${i.ref ?? ""}` === key)) since.delete(key);
89
+ }
90
+ const state = status === "failed" ? "failed" : status === "lost" ? "lost" : issues.some((i) => i.severity === "degraded") ? "degraded" : "ok";
91
+ const health = world.initResource(RenderHealth);
92
+ const from = health.state;
93
+ health.state = state;
94
+ health.issues = issues;
95
+ world.touchResource(RenderHealth);
96
+ if (from !== state) {
97
+ const change = { from, to: state, issues };
98
+ world.send(RenderHealthChanged, change);
99
+ world.trigger(RenderHealthChanged, change);
100
+ }
101
+ }
102
+ });
103
+ }
104
+ export {
105
+ MaterialFallbacks,
106
+ RenderHealth,
107
+ RenderHealthChanged,
108
+ RenderHealthReports,
109
+ clearHealthIssue,
110
+ healthSystem,
111
+ raiseHealthIssue
112
+ };
package/dist/index.d.ts CHANGED
@@ -1,12 +1,18 @@
1
- export { AssetStore, MaterialAsset, MaterialAssetType, MaterialImporter, Materials, type MaterialValue, MeshAsset, Meshes, materialFields, materialFromJson, materialTypeOf, RenderTargets, STANDARD_TYPE, StandardMaterial, type StandardMaterialValue, validateMaterial, } from './assets.js';
1
+ export { type AblationDriver, type AblationOptions, type AblationResult, ablatePasses, type PassCost, PassCosts, type PassCostsData, runAblation, } from './ablation.js';
2
+ export { AssetStore, MaterialAsset, MaterialAssetType, MaterialImporter, Materials, type MaterialValue, MeshAsset, Meshes, materialFields, materialFromJson, materialTypeOf, missingMesh, RenderTargets, STANDARD_TYPE, StandardMaterial, type StandardMaterialValue, validateMaterial, } from './assets.js';
2
3
  export { Atmosphere, type AtmospherePreset, AtmospherePresets, type AtmosphereRecord, type AtmosphereSampleResult, AtmosphereSettings, AtmosphereStore, Atmospheres, type AtmosphereValue, atmosphereMethods, atmosphereModel, type CameraAtmosphere, describeAtmospheres, sampleAtmosphere, selectAtmospheres, sunTransmittanceAt, } from './atmosphere.js';
3
4
  export { type AtmosphereModel, createSkySample, integrateSky, multiscatterLut, transmittanceLut, transmittanceToTop, } from './atmosphere-model.js';
4
5
  export { AtmosphereGpu, AtmosphereGpuResource, LUT_LAYERS } from './atmosphere-nodes.js';
5
6
  export { atmospherePlugin } from './atmosphere-plugin.js';
6
7
  export { applyPhysicalCameras, Camera3d, type Camera3dValue, camera2d, Exposure, type ExposurePreset, ExposurePresets, ev100, ev100FromCamera, ev100FromIlluminance, exposureScale, type LightPreset, LightPresets, lux, PhysicalCamera, } from './camera.js';
7
8
  export { CLUSTER_COUNT, CLUSTER_X, CLUSTER_Y, CLUSTER_Z, ClusterBuffers, type ClusterStats, clusterAabb, clusterAabbs, clusterLightsCpu, clusterRange, MAX_LIGHTS_PER_CLUSTER, ViewLightList, } from './clusters.js';
8
- export { Culler, GpuCuller, readVisibleSlots, visibleSlots } from './culling.js';
9
+ export { Culler, cullGround, GpuCuller, readVisibleSlots, visibleSlots } from './culling.js';
10
+ export { Cutaway, CutawayPath, type CutawaySupport, CutawayView, MAX_REVEAL_POINTS, } from './cutaway.js';
11
+ export { CUTAWAY_SHADERS, CutawayCameras, type CutawayState, cutawayPlugin, describeCutaways, extractCutaways, } from './cutaway-plugin.js';
12
+ export { DATA_WIDTH, DataStore, type DataStoreOptions, dataEntry, flushDataStores, loadDataTextures, } from './data-store.js';
9
13
  export { captureShadowMap, DEBUG_VIEWS, type DebugView, describeCulling, describeLighting, readBuffer, readTextureLayer, setDebugView, } from './debug-views.js';
14
+ export { Decals, describeDecals, updateDecals } from './decals.js';
15
+ export { DecalGpu, decalsPlugin } from './decals-plugin.js';
10
16
  export { captureGBuffer, describeDeferred, GBUFFER_CHANNELS, type GBufferChannel, } from './deferred.js';
11
17
  export { deferredPlugin } from './deferred-plugin.js';
12
18
  export { DEFORM_WORDS, DeformStore, type MeshDeform } from './deform.js';
@@ -15,39 +21,62 @@ export { whiteBalance } from './display-nodes.js';
15
21
  export { dynamicResolutionPlugin, type FrameTimings, RenderScaleController, updateRenderScale, } from './dynamic-resolution.js';
16
22
  export { type CameraEnvironment, DefaultEnvironment, type DefaultEnvironmentValue, describeEnvironment, Environment, type EnvironmentBaker, EnvironmentMap, type EnvironmentMapValue, EnvironmentPresets, EnvironmentStore, Environments, environmentBakers, ProceduralSky, type ProceduralSkyValue, Skybox, } from './environment.js';
17
23
  export { environmentPlugin } from './environment-plugin.js';
18
- export { type ForwardPluginOptions, type ForwardState, ForwardStateResource, forwardCorePlugin, PICK_TARGETS, sceneColor, type ViewGpu, viewBindGroup, } from './forward.js';
24
+ export { addRenderFeatures, type BaselineStrategy, clearUnsupported, describeFeatures, featureOfNode, type RenderFeature, RenderFeatures, reportUnsupported, unsupportedNodes, } from './features.js';
25
+ export { type FoliageChunk, FoliageLayer, type FoliageLayerOptions, FoliageLayerSet, FoliageLayers, foliagePlugin, } from './foliage.js';
26
+ export { CoveredGpuTime, FOLIAGE_SPAN_KEY, FoliageBudget, type FoliageBudgetValue, FoliageController, } from './foliage-budget.js';
27
+ export { FoliagePath, type FoliageSupport } from './foliage-path.js';
28
+ export { FOLIAGE_SHADERS, MAX_FOLIAGE_MESHES } from './foliage-shaders.js';
29
+ export { type ForwardPluginOptions, type ForwardState, ForwardStateResource, forwardCorePlugin, forwardQueue, PASS_GROUND, PICK_TARGETS, sceneColor, VERTEX_BUFFERS, type ViewGpu, viewBindGroup, viewPixelScale, } from './forward.js';
19
30
  export { fxaaPlugin } from './fxaa.js';
20
- export { LABEL_FONT, labelWidth } from './gizmo-font.js';
31
+ export { LABEL_FONT, labelAtlas, labelWidth } from './gizmo-font.js';
21
32
  export { GIZMO_LINE_FLOATS, type GizmoOptions, GizmoStore, Gizmos, packGizmoColor, } from './gizmos.js';
22
33
  export { gizmosPlugin } from './gizmos-plugin.js';
23
34
  export { GpuAssets, GpuAssetsResource, type GpuMaterial, type GpuMesh, type GpuTexture, } from './gpu-assets.js';
24
35
  export { type CapturedBuffer, type CapturedImage, type ColorAttachment, type DepthAttachment, type NodeContext, type NodeDescriptor, RenderGraph, RenderPhase, type RenderView, type ResolvedGraph, type ResourceRef, resolveGraph, type TransientTexture, VIEW_TARGET, } from './graph.js';
25
- export { type Batch, createDrawList, DeformPath, type DrawItem, type DrawList, INSTANCE_BYTES, INSTANCE_FLOATS, InstanceData, InstanceFlags, InstanceSlot, InstanceStore, Instances, LOD_BIT, Lod, lodSize, Mesh3d, MeshMaterial, MorphWeights, NotShadowCaster, NotShadowReceiver, SkinnedMesh, selectLod, VisibilityRange, } from './instances.js';
36
+ export { clearHealthIssue, MaterialFallbacks, RenderHealth, RenderHealthChanged, type RenderHealthIssue, RenderHealthReports, type RenderHealthState, type RenderHealthValue, raiseHealthIssue, } from './health.js';
37
+ export { type Batch, CUTAWAY_BIT, createDrawList, DeformPath, type DrawItem, type DrawList, groundKey, INSTANCE_BYTES, INSTANCE_FLOATS, InstanceData, InstanceFlags, InstanceSlot, InstanceStore, Instances, LOD_BIT, Lod, lodSize, Mesh3d, MeshMaterial, MorphWeights, NotShadowCaster, NotShadowReceiver, recordFlags, ShadowWhenHidden, SkinnedMesh, selectLod, VisibilityRange, } from './instances.js';
38
+ export { INTERIOR_BINDINGS, INTERIOR_BLOCKED, INTERIOR_SKY, InteriorPath, type InteriorSupport, } from './interior.js';
39
+ export { COVER_SCALE, COVER_ZERO, describeInterior, INTERIOR_GROUND, INTERIOR_LEVELS, INTERIOR_SHADERS, InteriorLighting, InteriorLightingResource, interiorPlugin, LINK_X, LINK_Z, ROW_CLEAR, ROW_SCALE, ROW_ZERO, VISIBILITY_MAX, } from './interior-plugin.js';
26
40
  export { observeOriginShifts, shiftRenderHistory } from './large-world.js';
27
- export { AmbientLight, type AmbientLightValue, CascadeSettings, DirectionalLight, LIGHT_FLOATS, LightingSettings, type LightingSettingsValue, type LightRecord, LightStore, Lights, type LuminousPowerPreset, LuminousPowerPresets, lumens, PointLight, SpotLight, } from './lights.js';
41
+ export { GROUND_BANDS, type GroundBand, GroundLayer, RenderLayers } from './layers.js';
42
+ export { clearLensFields, expireLensFields, forwardLensFields, LENS_DEMAND, Lens, type LensField, LensFields, LensPath, MAX_LENS_FIELDS, publishLensField, } from './lens.js';
43
+ export { describeLens, extractLens, LENS_SHADERS, type LensView, LensViews, lensPlugin, MAX_LENS_PIXELS, } from './lens-plugin.js';
44
+ export { AmbientLight, type AmbientLightValue, CascadeSettings, DirectionalLight, extractLights, FALLOFFS, LIGHT_FLOATS, LIGHT_ROW_WORD, LightingSettings, type LightingSettingsValue, type LightRecord, LightStore, Lights, type LuminousPowerPreset, LuminousPowerPresets, lumens, PointLight, SHADOW_UPDATES, SpotLight, setLightRow, tabletopFalloff, } from './lights.js';
28
45
  export { MaterialNoise, type MaterialNoiseSupport } from './material-noise.js';
29
46
  export { registerMaterialModule, typeOrdinal } from './material-pipelines.js';
30
47
  export { allMaterialTypes, BLEND_MODES, type BlendMode, defineMaterial, findMaterialType, isTransparent, type MaterialNoiseSlot, MaterialType, type MaterialTypeOptions, materialModulePath, onMaterialTypeChange, } from './materials.js';
48
+ export { MAX_OUTLINE_STYLES, OUTLINE_OCCLUSION, Outline, OutlinePath, } from './outline.js';
49
+ export { describeOutlines, OUTLINE_SHADERS, type OutlineView, OutlineViews, outlinePlugin, queueOutlines, } from './outline-plugin.js';
31
50
  export { allOverlays, DebugOverlays, type DebugOverlaysValue, defineOverlay, gridsOverlay, isOverlayOn, OVERLAYS, type Overlay, type OverlayDef, type OverlayFilter, overlayNames, setOverlays, } from './overlays.js';
32
51
  export { descendantPaths, entityName, findModelRoot } from './paths.js';
33
- export { type PickBlocker, type PickDrawer, type PickHit, Picking, pick, primaryView, type RaycastOptions, raycast, } from './picking.js';
52
+ export { type PickBlocker, type PickDetail, type PickDetailer, type PickDrawer, type PickHit, Picking, pick, primaryView, type RaycastOptions, raycast, } from './picking.js';
34
53
  export { pickingPlugin } from './picking-plugin.js';
35
54
  export { PixelPerfect, type PixelPerfectLayout, PixelPerfectPath, PixelTargets, pixelPerfectLayout, } from './pixel-perfect.js';
36
55
  export { pixelPerfectPlugin } from './pixel-perfect-plugin.js';
37
- export { captureBuffer, captureView, describeRender, Gpu, GpuDeviceLost, Graph, RenderDescribers, type RenderPluginOptions, RenderSet, renderPlugin, Shaders, Views, Window, } from './plugin.js';
56
+ export { captureBuffer, captureView, describeRender, Gpu, GpuDeviceLost, Graph, gpuMemory, RenderDescribers, RenderOptions, type RenderOptionsValue, type RenderPluginOptions, RenderSet, renderOwner, renderPlugin, Shaders, Views, Window, } from './plugin.js';
38
57
  export { TexturePool } from './pool.js';
39
58
  export { ANTIALIASING_MODES, Antialiasing, AutoExposure, Bloom, ColorGrading, cocRadiusPixels, DepthOfField, Fog, METERING_MODES, MotionBlur, PostEffect, type PostSettings, SSAO_QUALITIES, Ssao, Vignette, } from './post.js';
40
59
  export { bloomLevels, describePost, ExposureMeters, type ExposureState, histogramEv, METER_READBACKS, POST_NODES, } from './post-nodes.js';
41
60
  export { postPlugin } from './post-plugin.js';
61
+ export { screenToPlane, screenToRay, worldToScreen } from './projection.js';
42
62
  export { describeRenderScale, RenderScale, type RenderScaleValue, SCALE_STEP, scaledSize, } from './render-scale.js';
63
+ export { describeRibbons, Ribbons, shiftRibbons, updateRibbons } from './ribbons.js';
64
+ export { RibbonGpu, ribbonsPlugin } from './ribbons-plugin.js';
65
+ export { clearScreenEffects, describeScreenEffects, expireScreenEffects, forwardScreenEffects, MAX_SCREEN_EFFECT_PARAMS, MAX_SCREEN_EFFECTS, onScreenEffect, publishScreenEffect, runScreenEffects, SCREEN_EFFECTS_DEMAND, type ScreenEffect, type ScreenEffectHandler, ScreenEffectHandlers, type ScreenEffectParams, ScreenEffects, } from './screen-effects.js';
43
66
  export { ENGINE_SHADERS, materialLayout, registerEngineShaders, registerShaders, ViewUniform, viewLayout, } from './shaders.js';
67
+ export { SHADOW_CATCHER_SHADERS, ShadowCatcher, shadowCatcherPlugin } from './shadow-catcher.js';
44
68
  export { Cascades, cascadeSplits, LocalShadows, ShadowsResource, sliceSphere, } from './shadows.js';
45
69
  export { MAX_JOINTS, type SkinAsset, SkinAssetType, Skins, skinArtifact } from './skin-asset.js';
46
70
  export { prepareDeforms } from './skinning.js';
47
71
  export { skinningPlugin } from './skinning-plugin.js';
48
72
  export { forwardPlugin } from './standard.js';
49
- export { GpuMemory, type GpuMemoryData, RenderCounters, type RenderCountersData, RenderStats, type ViewStats, } from './stats.js';
50
- export { OffscreenTarget, type OffscreenTargetOptions, type RenderTarget, WindowTarget, } from './target.js';
73
+ export { type FrameCounts, type FrameRecord, GpuMemory, type GpuMemoryData, RECENT_FRAMES, RenderCounters, type RenderCountersData, RenderStats, RenderStatsMap, type ViewStats, } from './stats.js';
74
+ export { OffscreenTarget, type OffscreenTargetOptions, type RenderTarget, } from './target.js';
75
+ export { bindingDimension, depthReadEntry } from './tier.js';
51
76
  export { GpuTimer } from './timer.js';
52
- export { type CameraData, Cameras, cameraOf, DEFAULT_CURVE, isScaled, RenderPath, shiftCameraHistory, TONEMAP_CURVES, type TonemapCurve, Tonemapping, ViewSettings, type ViewSettingsValue, viewAliases, } from './view.js';
77
+ export { addShaderVariantSource, type MaterialVariantEntry, materialVariants, type ShaderVariantManifest, type ShaderVariantSource, ShaderVariantSources, showToCamera, showVariants, } from './variants.js';
78
+ export { Beam, Decal, DecalReceiver, DecalsPath, RibbonsPath, Trail } from './vfx.js';
79
+ export { type CameraData, CameraMoved, Cameras, cameraOf, DEFAULT_CURVE, isScaled, RenderPath, shiftCameraHistory, TONEMAP_CURVES, type TonemapCurve, Tonemapping, ViewSettings, type ViewSettingsValue, viewAliases, } from './view.js';
80
+ export { type HiddenSet, HiddenSetsResource, NO_HIDDEN, observeViewVisibilityWithoutPlugin, VIEW_SHADOWS, ViewVisibility, } from './view-visibility.js';
81
+ export { describeViewVisibility, HiddenSets, resolveViewVisibility, viewVisibilityPlugin, } from './view-visibility-plugin.js';
53
82
  export { ComputedVisibility, computeVisibility, Visibility } from './visibility.js';