@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
@@ -0,0 +1,301 @@
1
+ import { GpuBuffer } from "@aethervtt/shard-gpu";
2
+ import { fragmentKernel } from "@aethervtt/shard-shader/fragment-kernel";
3
+ import { MULTISCATTER_SIZE, TRANSMITTANCE_H, TRANSMITTANCE_W } from "../atmosphere-model.js";
4
+ import {
5
+ AtmosphereGpuResource,
6
+ LUT_LAYERS,
7
+ packParams
8
+ } from "../atmosphere-nodes.js";
9
+ import { ATMOSPHERE_SHADERS } from "../atmosphere-shaders.js";
10
+ import { Shaders } from "../plugin.js";
11
+ import { baselineBakers } from "./environment.js";
12
+ const SLOT = 256;
13
+ const fragment = (root) => fragmentKernel(ATMOSPHERE_SHADERS[root], "main", root).source;
14
+ const BASELINE_ATMOSPHERE_SHADERS = {
15
+ "shard::atmosphere::transmittance_lut::fragment": fragment(
16
+ "shard::atmosphere::transmittance_lut"
17
+ ),
18
+ "shard::atmosphere::multiscatter_lut::fragment": fragment("shard::atmosphere::multiscatter_lut"),
19
+ "shard::atmosphere::sky_view::fragment": fragment("shard::atmosphere::sky_view"),
20
+ "shard::atmosphere::bake::fragment": fragment("shard::atmosphere::bake"),
21
+ "shard::atmosphere::baseline::aerial": `
22
+ import shard::atmosphere::{ AtmosphereView, KCD, integrate, froxel_distance, ray_at };
23
+
24
+ @group(0) @binding(0) var<uniform> atmo: AtmosphereView;
25
+ @group(0) @binding(1) var tlut: texture_2d_array<f32>;
26
+ @group(0) @binding(2) var mlut: texture_2d_array<f32>;
27
+ @group(0) @binding(3) var lut_sampler: sampler;
28
+ /** The froxel grid's size, and the slice this pass renders. */
29
+ @group(0) @binding(4) var<uniform> shard_target: vec4u;
30
+
31
+ struct Froxel {
32
+ @location(0) scatter: vec4f,
33
+ @location(1) transmittance: vec4f,
34
+ }
35
+
36
+ /**
37
+ * One slice of the aerial-perspective froxels: in-scattering (kcd/m\xB2) and transmittance from the
38
+ * camera to the slice's far end, along the froxel column's ray.
39
+ */
40
+ @fragment fn main(@builtin(position) p: vec4f) -> Froxel {
41
+ let size = shard_target.xyz;
42
+ let a = atmo.atmospheres[u32(atmo.info.y)];
43
+ let uv = (floor(p.xy) + 0.5) / vec2f(size.xy);
44
+ let d = ray_at(atmo, uv);
45
+ let o = a.origin.xyz;
46
+ var L = vec3f(0.0);
47
+ var T = vec3f(1.0);
48
+ var t_prev = 0.0;
49
+ for (var k = 0u; k <= shard_target.w; k++) {
50
+ let t = froxel_distance(atmo.froxels, (f32(k) + 1.0) / f32(size.z));
51
+ // Two steps per slice, from where the last one ended.
52
+ let seg = integrate(tlut, mlut, lut_sampler, a, atmo.suns[0], atmo.suns[1], u32(atmo.info.z), o + d * t_prev, d, t - t_prev, 2u, false);
53
+ L += T * seg.luminance;
54
+ T *= seg.transmittance;
55
+ t_prev = t;
56
+ }
57
+ var out: Froxel;
58
+ out.scatter = vec4f(L / KCD, 1.0);
59
+ out.transmittance = vec4f(T, 1.0);
60
+ return out;
61
+ }`
62
+ };
63
+ const ROOTS = {
64
+ transmittance: "shard::atmosphere::transmittance_lut::fragment",
65
+ multiscatter: "shard::atmosphere::multiscatter_lut::fragment",
66
+ skyView: "shard::atmosphere::sky_view::fragment",
67
+ aerial: "shard::atmosphere::baseline::aerial",
68
+ bake: "shard::atmosphere::bake::fragment"
69
+ };
70
+ const kernels = /* @__PURE__ */ new WeakMap();
71
+ function kernelsOf(gpu) {
72
+ let k = kernels.get(gpu);
73
+ if (k && k.generation === gpu.generation) return k;
74
+ const F = GPUShaderStage.FRAGMENT;
75
+ const uniform = (binding) => ({
76
+ binding,
77
+ visibility: F,
78
+ buffer: { type: "uniform" }
79
+ });
80
+ const target = (binding) => ({
81
+ binding,
82
+ visibility: F,
83
+ buffer: { type: "uniform", hasDynamicOffset: true }
84
+ });
85
+ const array = (binding) => ({
86
+ binding,
87
+ visibility: F,
88
+ texture: { sampleType: "float", viewDimension: "2d-array" }
89
+ });
90
+ const sampler = (binding) => ({
91
+ binding,
92
+ visibility: F,
93
+ sampler: { type: "filtering" }
94
+ });
95
+ const layout = (label, entries) => gpu.layouts.bindGroupLayout({ label: `atmosphere/baseline/${label}`, entries });
96
+ const view = [uniform(0), array(1), array(2), sampler(3), target(4)];
97
+ k = {
98
+ generation: gpu.generation,
99
+ layouts: {
100
+ transmittance: layout("transmittance", [uniform(0), target(1)]),
101
+ multiscatter: layout("multiscatter", [uniform(0), array(1), sampler(2), target(3)]),
102
+ skyView: layout("sky-view", view),
103
+ aerial: layout("aerial", view),
104
+ bake: layout("bake", view)
105
+ },
106
+ pipelines: {}
107
+ };
108
+ kernels.set(gpu, k);
109
+ return k;
110
+ }
111
+ function pipeline(ctx, k, name) {
112
+ const cached = k.pipelines[name];
113
+ if (cached) return cached;
114
+ const gpu = ctx.gpu;
115
+ const shaders = ctx.world.resource(Shaders);
116
+ const fs = shaders.module(gpu, { root: ROOTS[name] });
117
+ const vs = shaders.module(gpu, { root: "shard::fullscreen" });
118
+ if (!fs || !vs) {
119
+ gpu.pipelines.skipped++;
120
+ return void 0;
121
+ }
122
+ const made = gpu.pipelines.render({
123
+ label: `atmosphere/baseline/${name}`,
124
+ layout: gpu.layouts.pipelineLayout({
125
+ label: `atmosphere/baseline/${name}`,
126
+ bindGroupLayouts: [k.layouts[name]]
127
+ }),
128
+ vertex: { module: vs, entryPoint: "vs" },
129
+ fragment: {
130
+ module: fs,
131
+ entryPoint: "main",
132
+ targets: name === "aerial" ? [{ format: "rgba16float" }, { format: "rgba16float" }] : [{ format: "rgba16float" }]
133
+ }
134
+ });
135
+ if (made) k.pipelines[name] = made;
136
+ return made;
137
+ }
138
+ function targetBuffer(gpu, label, slots) {
139
+ const data = new Uint32Array(Math.max(1, slots.length) * SLOT / 4);
140
+ for (let i = 0; i < slots.length; i++) data.set(slots[i], i * SLOT / 4);
141
+ const buffer = new GpuBuffer(gpu, { label, usage: GPUBufferUsage.UNIFORM, size: data.byteLength });
142
+ buffer.write(data);
143
+ return buffer;
144
+ }
145
+ function draw(ctx, label, p, group, offset, attachments) {
146
+ const pass = ctx.encoder.beginRenderPass({ label, colorAttachments: attachments });
147
+ pass.setPipeline(p);
148
+ pass.setBindGroup(0, group, [offset]);
149
+ pass.draw(3);
150
+ pass.end();
151
+ }
152
+ function color(view, depthSlice) {
153
+ return { view, depthSlice, loadOp: "clear", storeOp: "store", clearValue: [0, 0, 0, 0] };
154
+ }
155
+ const paramScratch = new Float32Array(32);
156
+ function runLuts(ctx, s) {
157
+ const k = kernelsOf(ctx.gpu);
158
+ const t = pipeline(ctx, k, "transmittance");
159
+ const m = pipeline(ctx, k, "multiscatter");
160
+ if (!t || !m) return false;
161
+ const gpu = ctx.gpu;
162
+ const targets = targetBuffer(gpu, "atmosphere/baseline/luts", [
163
+ [TRANSMITTANCE_W, TRANSMITTANCE_H, 1, 0],
164
+ [MULTISCATTER_SIZE, MULTISCATTER_SIZE, 1, 0]
165
+ ]);
166
+ ctx.afterSubmit(() => targets.destroy());
167
+ const target = { buffer: targets.buffer, size: 16 };
168
+ for (let i = 0; i < LUT_LAYERS; i++) {
169
+ const rec = s.owners[i];
170
+ if (!rec || rec.layerVersion === rec.version) continue;
171
+ packParams(rec, paramScratch, 0, void 0, 0);
172
+ s.params[i].write(paramScratch);
173
+ const layer = (texture) => texture.createView({ dimension: "2d", baseArrayLayer: i, arrayLayerCount: 1 });
174
+ const params = { buffer: s.params[i].buffer };
175
+ draw(
176
+ ctx,
177
+ "atmosphere/transmittance",
178
+ t,
179
+ gpu.device.createBindGroup({
180
+ layout: k.layouts.transmittance,
181
+ entries: [
182
+ { binding: 0, resource: params },
183
+ { binding: 1, resource: target }
184
+ ]
185
+ }),
186
+ 0,
187
+ [color(layer(s.transmittance))]
188
+ );
189
+ draw(
190
+ ctx,
191
+ "atmosphere/multiscatter",
192
+ m,
193
+ gpu.device.createBindGroup({
194
+ layout: k.layouts.multiscatter,
195
+ entries: [
196
+ { binding: 0, resource: params },
197
+ { binding: 1, resource: s.transmittanceArray },
198
+ { binding: 2, resource: s.clamp },
199
+ { binding: 3, resource: target }
200
+ ]
201
+ }),
202
+ SLOT,
203
+ [color(layer(s.multiscatter))]
204
+ );
205
+ rec.layerVersion = rec.version;
206
+ s.lutComputes++;
207
+ }
208
+ return true;
209
+ }
210
+ function viewGroup(gpu, layout, s, uniform, targets) {
211
+ return gpu.device.createBindGroup({
212
+ layout,
213
+ entries: [
214
+ { binding: 0, resource: { buffer: uniform } },
215
+ { binding: 1, resource: s.transmittanceArray },
216
+ { binding: 2, resource: s.multiscatterArray },
217
+ { binding: 3, resource: s.clamp },
218
+ { binding: 4, resource: { buffer: targets, size: 16 } }
219
+ ]
220
+ });
221
+ }
222
+ const viewPasses = /* @__PURE__ */ new WeakMap();
223
+ function runView(ctx, s, ca, c) {
224
+ const k = kernelsOf(ctx.gpu);
225
+ const sky = pipeline(ctx, k, "skyView");
226
+ const aerial = pipeline(ctx, k, "aerial");
227
+ if (!sky || !aerial) return false;
228
+ const gpu = ctx.gpu;
229
+ const depth = c.scatter.depthOrArrayLayers;
230
+ let v = viewPasses.get(c);
231
+ if (!v || v.uniform !== c.uniform.version || v.skyView !== c.skyView || v.scatter !== c.scatter || v.transmittance !== c.transmittance || v.luts !== s.transmittance || v.generation !== gpu.generation) {
232
+ v?.targets.destroy();
233
+ const slots = [[c.skyView.width, c.skyView.height, 1, 0]];
234
+ for (let slice = 0; slice < depth; slice++) {
235
+ slots.push([c.scatter.width, c.scatter.height, depth, slice]);
236
+ }
237
+ const targets = targetBuffer(gpu, "atmosphere/baseline/view", slots);
238
+ v = {
239
+ uniform: c.uniform.version,
240
+ skyView: c.skyView,
241
+ scatter: c.scatter,
242
+ transmittance: c.transmittance,
243
+ luts: s.transmittance,
244
+ generation: gpu.generation,
245
+ targets,
246
+ sky: viewGroup(gpu, k.layouts.skyView, s, c.uniform.buffer, targets.buffer),
247
+ aerial: viewGroup(gpu, k.layouts.aerial, s, c.uniform.buffer, targets.buffer),
248
+ skyViewView: c.skyView.createView(),
249
+ scatterView: c.scatter.createView(),
250
+ transmittanceView: c.transmittance.createView()
251
+ };
252
+ viewPasses.set(c, v);
253
+ }
254
+ draw(ctx, "atmosphere/sky-view", sky, v.sky, 0, [color(v.skyViewView)]);
255
+ if (ca.aerialPerspective) {
256
+ for (let slice = 0; slice < depth; slice++) {
257
+ draw(ctx, "atmosphere/aerial", aerial, v.aerial, (1 + slice) * SLOT, [
258
+ color(v.scatterView, slice),
259
+ color(v.transmittanceView, slice)
260
+ ]);
261
+ }
262
+ }
263
+ return true;
264
+ }
265
+ const bakeTargets = /* @__PURE__ */ new WeakMap();
266
+ function bake(ctx, env, face, view) {
267
+ const ca = env.atmosphere;
268
+ const s = ctx.world.resource(AtmosphereGpuResource);
269
+ const primary = ca?.primary;
270
+ if (!ca || !primary || primary.layerVersion !== primary.version) return false;
271
+ const c = s.cameras.get(ca.camera);
272
+ if (!c?.packed) return false;
273
+ const k = kernelsOf(ctx.gpu);
274
+ const p = pipeline(ctx, k, "bake");
275
+ if (!p) return false;
276
+ const gpu = ctx.gpu;
277
+ let sizes = bakeTargets.get(gpu);
278
+ if (!sizes) {
279
+ sizes = /* @__PURE__ */ new Map();
280
+ bakeTargets.set(gpu, sizes);
281
+ }
282
+ let targets = sizes.get(env.size);
283
+ if (!targets || targets.generation !== gpu.generation) {
284
+ const faces = [0, 1, 2, 3, 4, 5].map((f) => [env.size, env.size, 1, f]);
285
+ const buffer = gpu.withOwner("gpu", () => targetBuffer(gpu, "atmosphere/baseline/bake", faces));
286
+ targets = { generation: gpu.generation, buffer };
287
+ sizes.set(env.size, targets);
288
+ }
289
+ const group = viewGroup(gpu, k.layouts.bake, s, c.uniform.buffer, targets.buffer.buffer);
290
+ draw(ctx, "atmosphere/bake", p, group, face * SLOT, [color(view)]);
291
+ return true;
292
+ }
293
+ function installAtmosphereBaseline() {
294
+ baselineBakers.set("atmosphere", bake);
295
+ }
296
+ export {
297
+ BASELINE_ATMOSPHERE_SHADERS,
298
+ installAtmosphereBaseline,
299
+ runLuts,
300
+ runView
301
+ };
@@ -0,0 +1,36 @@
1
+ import { type GpuContext } from '@aethervtt/shard-gpu';
2
+ /** Uploads what every data texture of `gpu` was written this frame. */
3
+ export declare function flushDataTextures(gpu: GpuContext): void;
4
+ /** Forgets `owner`'s data textures on `gpu`: the ledger destroys them, so they mustn't flush. */
5
+ export declare function releaseDataTextures(gpu: GpuContext, owner: string): void;
6
+ export declare class DataTexture {
7
+ readonly owner: string;
8
+ version: number;
9
+ private readonly gpu;
10
+ private readonly label;
11
+ private texture;
12
+ private view;
13
+ private mirror;
14
+ private mirrorWords;
15
+ size: number;
16
+ private rows;
17
+ private generation;
18
+ private dirtyStart;
19
+ private dirtyEnd;
20
+ private readonly destination;
21
+ private readonly layout;
22
+ private readonly extent;
23
+ constructor(gpu: GpuContext, label: string, size: number);
24
+ get currentVersion(): number;
25
+ ensureCapacity(bytes: number): boolean;
26
+ write(data: ArrayBufferView & ArrayLike<number>, byteOffset: number, start: number, count: number | undefined): void;
27
+ resource(): GPUBindingResource;
28
+ flush(): void;
29
+ destroy(): void;
30
+ private setMirror;
31
+ private upload;
32
+ /** A texture for the current size (plus the length texel), everything uploaded again. */
33
+ private recreate;
34
+ /** After device loss (or on first use) the texture is made again from the CPU copy. */
35
+ private checkGeneration;
36
+ }
@@ -0,0 +1,181 @@
1
+ import { DATA_TEXTURE_PREFIX } from "@aethervtt/shard-gpu";
2
+ import { DATA_TEXTURE_WIDTH as DATA_WIDTH } from "@aethervtt/shard-shader";
3
+ const textures = /* @__PURE__ */ new WeakMap();
4
+ const bytesOf = /* @__PURE__ */ new WeakMap();
5
+ const wordsOf = /* @__PURE__ */ new WeakMap();
6
+ function bytes(buffer) {
7
+ let view = bytesOf.get(buffer);
8
+ if (!view) {
9
+ view = new Uint8Array(buffer);
10
+ bytesOf.set(buffer, view);
11
+ }
12
+ return view;
13
+ }
14
+ function words(buffer) {
15
+ let view = wordsOf.get(buffer);
16
+ if (!view) {
17
+ view = new Uint32Array(buffer, 0, buffer.byteLength >> 2);
18
+ wordsOf.set(buffer, view);
19
+ }
20
+ return view;
21
+ }
22
+ function flushDataTextures(gpu) {
23
+ const list = textures.get(gpu);
24
+ if (!list) return;
25
+ for (let i = 0; i < list.length; i++) list[i].flush();
26
+ }
27
+ function releaseDataTextures(gpu, owner) {
28
+ const list = textures.get(gpu);
29
+ if (!list) return;
30
+ let n = 0;
31
+ for (let i = 0; i < list.length; i++) if (list[i].owner !== owner) list[n++] = list[i];
32
+ list.length = n;
33
+ }
34
+ class DataTexture {
35
+ owner;
36
+ version = 0;
37
+ gpu;
38
+ label;
39
+ texture;
40
+ view;
41
+ mirror;
42
+ mirrorWords;
43
+ size;
44
+ rows = 0;
45
+ generation = -1;
46
+ dirtyStart = Number.POSITIVE_INFINITY;
47
+ dirtyEnd = 0;
48
+ // Reused upload descriptors: flushing allocates nothing.
49
+ destination;
50
+ layout = { offset: 0, bytesPerRow: DATA_WIDTH * 16 };
51
+ extent = { width: 0, height: 0 };
52
+ constructor(gpu, label, size) {
53
+ this.gpu = gpu;
54
+ this.label = label;
55
+ this.owner = gpu.owner;
56
+ this.size = size;
57
+ this.mirror = new Uint8Array(size);
58
+ this.mirrorWords = new Uint32Array(this.mirror.buffer);
59
+ this.destination = { texture: void 0, origin: { x: 0, y: 0 } };
60
+ let list = textures.get(gpu);
61
+ if (!list) {
62
+ list = [];
63
+ textures.set(gpu, list);
64
+ }
65
+ list.push(this);
66
+ }
67
+ get currentVersion() {
68
+ this.checkGeneration();
69
+ return this.version;
70
+ }
71
+ ensureCapacity(bytes2) {
72
+ this.checkGeneration();
73
+ if (bytes2 <= this.size) return false;
74
+ let size = this.size;
75
+ while (size < bytes2) size *= 2;
76
+ const mirror = new Uint8Array(size);
77
+ mirror.set(this.mirror.subarray(0, this.size));
78
+ this.setMirror(mirror);
79
+ this.size = size;
80
+ this.recreate();
81
+ return true;
82
+ }
83
+ write(data, byteOffset, start, count) {
84
+ const bytesPerElement = data.BYTES_PER_ELEMENT;
85
+ const elements = count ?? data.length - start;
86
+ const length = elements * bytesPerElement;
87
+ this.ensureCapacity(byteOffset + length);
88
+ if (elements === 0) return;
89
+ this.checkGeneration();
90
+ const from = data.byteOffset + start * bytesPerElement;
91
+ if (((from | byteOffset | length) & 3) === 0) {
92
+ const src = words(data.buffer);
93
+ const dst = this.mirrorWords;
94
+ const s = from >> 2;
95
+ const d = byteOffset >> 2;
96
+ for (let i = 0, n = length >> 2; i < n; i++) dst[d + i] = src[s + i];
97
+ } else {
98
+ const src = bytes(data.buffer);
99
+ const dst = this.mirror;
100
+ for (let i = 0; i < length; i++) dst[byteOffset + i] = src[from + i];
101
+ }
102
+ if (byteOffset < this.dirtyStart) this.dirtyStart = byteOffset;
103
+ if (byteOffset + length > this.dirtyEnd) this.dirtyEnd = byteOffset + length;
104
+ }
105
+ resource() {
106
+ this.checkGeneration();
107
+ return this.view;
108
+ }
109
+ flush() {
110
+ if (this.dirtyEnd <= this.dirtyStart) return;
111
+ this.checkGeneration();
112
+ const first = Math.floor(this.dirtyStart / 16);
113
+ const last = Math.ceil(this.dirtyEnd / 16);
114
+ this.dirtyStart = Number.POSITIVE_INFINITY;
115
+ this.dirtyEnd = 0;
116
+ const row0 = Math.floor(first / DATA_WIDTH);
117
+ const row1 = Math.floor((last - 1) / DATA_WIDTH);
118
+ if (row0 === row1) {
119
+ this.upload(first % DATA_WIDTH, row0, last - first, 1);
120
+ return;
121
+ }
122
+ this.upload(first % DATA_WIDTH, row0, DATA_WIDTH - first % DATA_WIDTH, 1);
123
+ if (row1 > row0 + 1) this.upload(0, row0 + 1, DATA_WIDTH, row1 - row0 - 1);
124
+ this.upload(0, row1, last - row1 * DATA_WIDTH, 1);
125
+ }
126
+ destroy() {
127
+ this.texture?.destroy();
128
+ const list = textures.get(this.gpu);
129
+ const i = list ? list.indexOf(this) : -1;
130
+ if (i !== -1) list.splice(i, 1);
131
+ }
132
+ setMirror(mirror) {
133
+ this.mirror = mirror;
134
+ this.mirrorWords = new Uint32Array(mirror.buffer);
135
+ }
136
+ upload(x, y, width, height) {
137
+ this.destination.texture = this.texture;
138
+ this.destination.origin.x = x;
139
+ this.destination.origin.y = y;
140
+ this.layout.offset = (y * DATA_WIDTH + x) * 16;
141
+ this.extent.width = width;
142
+ this.extent.height = height;
143
+ this.gpu.device.queue.writeTexture(this.destination, this.mirror, this.layout, this.extent);
144
+ }
145
+ /** A texture for the current size (plus the length texel), everything uploaded again. */
146
+ recreate() {
147
+ this.texture?.destroy();
148
+ const texels = this.size / 16 + 1;
149
+ this.rows = Math.ceil(texels / DATA_WIDTH);
150
+ if (this.mirror.byteLength < this.rows * DATA_WIDTH * 16) {
151
+ const mirror = new Uint8Array(this.rows * DATA_WIDTH * 16);
152
+ mirror.set(this.mirror);
153
+ this.setMirror(mirror);
154
+ }
155
+ this.mirrorWords[(this.rows * DATA_WIDTH - 1) * 4] = this.size;
156
+ this.texture = this.gpu.withOwner(
157
+ this.owner,
158
+ () => this.gpu.device.createTexture({
159
+ label: `${DATA_TEXTURE_PREFIX}${this.label}`,
160
+ size: [DATA_WIDTH, this.rows],
161
+ format: "rgba32uint",
162
+ // COPY_SRC: tools and tests read a data texture back.
163
+ usage: GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_DST | GPUTextureUsage.COPY_SRC
164
+ })
165
+ );
166
+ this.view = this.texture.createView();
167
+ this.generation = this.gpu.generation;
168
+ this.version++;
169
+ this.dirtyStart = 0;
170
+ this.dirtyEnd = this.rows * DATA_WIDTH * 16;
171
+ }
172
+ /** After device loss (or on first use) the texture is made again from the CPU copy. */
173
+ checkGeneration() {
174
+ if (this.generation !== this.gpu.generation) this.recreate();
175
+ }
176
+ }
177
+ export {
178
+ DataTexture,
179
+ flushDataTextures,
180
+ releaseDataTextures
181
+ };
@@ -0,0 +1,14 @@
1
+ import type { Environment, EnvironmentStore } from '../environment-state.js';
2
+ import type { NodeContext } from '../graph.js';
3
+ export declare const BASELINE_ENVIRONMENT_SHADERS: Record<string, string>;
4
+ /**
5
+ * A pass that fills one face of an atmosphere environment's source (level 0) on baseline, or false
6
+ * when its inputs aren't ready. The atmosphere plugin registers `atmosphere`.
7
+ */
8
+ export type BaselineEnvironmentBaker = (ctx: NodeContext, env: Environment, face: number, target: GPUTextureView) => boolean;
9
+ export declare const baselineBakers: Map<"atmosphere" | "map", BaselineEnvironmentBaker>;
10
+ /**
11
+ * Prefilters every pending environment (and the BRDF LUT, once) with fragment passes. Runs once
12
+ * per frame, before any view draws; an environment whose input isn't ready stays pending.
13
+ */
14
+ export declare function runBaselineEnvironment(ctx: NodeContext, store: EnvironmentStore): void;