@multiplekex/shallot 0.2.5 → 0.3.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/package.json +1 -1
- package/src/core/component.ts +1 -1
- package/src/core/index.ts +1 -13
- package/src/core/math.ts +186 -0
- package/src/core/state.ts +1 -1
- package/src/core/xml.ts +56 -41
- package/src/extras/orbit/index.ts +1 -1
- package/src/extras/text/index.ts +10 -65
- package/src/extras/{water.ts → water/index.ts} +59 -4
- package/src/standard/raster/batch.ts +149 -0
- package/src/standard/raster/forward.ts +832 -0
- package/src/standard/raster/index.ts +146 -472
- package/src/standard/raster/shadow.ts +408 -0
- package/src/standard/raytracing/bvh/blas.ts +335 -87
- package/src/standard/raytracing/bvh/radix.ts +225 -228
- package/src/standard/raytracing/bvh/refit.ts +711 -0
- package/src/standard/raytracing/bvh/structs.ts +0 -55
- package/src/standard/raytracing/bvh/tlas.ts +153 -141
- package/src/standard/raytracing/bvh/traverse.ts +72 -64
- package/src/standard/raytracing/index.ts +233 -204
- package/src/standard/raytracing/instance.ts +30 -18
- package/src/standard/raytracing/ray.ts +1 -1
- package/src/standard/raytracing/shaders.ts +23 -40
- package/src/standard/render/camera.ts +10 -28
- package/src/standard/render/data.ts +1 -1
- package/src/standard/render/index.ts +68 -12
- package/src/standard/render/light.ts +2 -2
- package/src/standard/render/mesh.ts +404 -0
- package/src/standard/render/overlay.ts +5 -2
- package/src/standard/render/postprocess.ts +263 -267
- package/src/standard/render/surface/index.ts +81 -12
- package/src/standard/render/surface/shaders.ts +265 -11
- package/src/standard/render/surface/structs.ts +10 -0
- package/src/standard/tween/tween.ts +44 -115
- package/src/standard/render/mesh/box.ts +0 -20
- package/src/standard/render/mesh/index.ts +0 -315
- package/src/standard/render/mesh/plane.ts +0 -11
- package/src/standard/render/mesh/sphere.ts +0 -40
- package/src/standard/render/mesh/unified.ts +0 -96
- package/src/standard/render/surface/compile.ts +0 -65
- package/src/standard/render/surface/noise.ts +0 -58
|
@@ -1,31 +1,39 @@
|
|
|
1
1
|
import type { Plugin, State } from "../../core";
|
|
2
2
|
import { MAX_ENTITIES, resource } from "../../core";
|
|
3
|
+
import { setTraits } from "../../core/component";
|
|
3
4
|
import { Compute } from "../compute";
|
|
4
5
|
import type { ComputeNode, ExecutionContext } from "../compute";
|
|
5
6
|
import { WorldTransform } from "../transforms";
|
|
6
7
|
import { Activity } from "../activity";
|
|
7
|
-
import { Camera
|
|
8
|
-
import { Mesh,
|
|
9
|
-
import { getDefaultAllSurfaces } from "../render/surface";
|
|
8
|
+
import { Camera } from "../render/camera";
|
|
9
|
+
import { Mesh, getMesh, getMeshVersion } from "../render/mesh";
|
|
10
|
+
import { getDefaultAllSurfaces, Surface } from "../render/surface";
|
|
10
11
|
import type { SurfaceData } from "../render/surface";
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
14
|
-
RasterPlugin,
|
|
15
|
-
RasterResource,
|
|
16
|
-
createRasterPipeline,
|
|
17
|
-
createSkyPipeline,
|
|
18
|
-
executeRasterPass,
|
|
19
|
-
type RasterPassConfig,
|
|
20
|
-
} from "../raster";
|
|
12
|
+
import { Render, registerShadowSettings } from "../render";
|
|
13
|
+
import { RasterPlugin, RasterResource } from "../raster";
|
|
21
14
|
import { createBLASAtlas, type BLASAtlas } from "./bvh/blas";
|
|
22
15
|
import { createTLASBuffers, createTLASNode, type TLASBuffers } from "./bvh/tlas";
|
|
23
|
-
import {
|
|
16
|
+
import { createBLASRefitNode } from "./bvh/refit";
|
|
24
17
|
import { createInstanceNode } from "./instance";
|
|
25
18
|
import { createDepthConvertNode } from "./depth";
|
|
26
19
|
import { compileUberShader } from "./shaders";
|
|
27
20
|
|
|
28
|
-
export
|
|
21
|
+
export const Raytracing = {};
|
|
22
|
+
export const Dynamic = {};
|
|
23
|
+
|
|
24
|
+
export const RaytracingShadowSettings = {
|
|
25
|
+
softness: [] as number[],
|
|
26
|
+
samples: [] as number[],
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
setTraits(RaytracingShadowSettings, {
|
|
30
|
+
defaults: () => ({
|
|
31
|
+
softness: 0,
|
|
32
|
+
samples: 1,
|
|
33
|
+
}),
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
export { compileUberShader, compileRTSurface } from "./shaders";
|
|
29
37
|
export { createDepthConvertNode } from "./depth";
|
|
30
38
|
export { createInstanceNode } from "./instance";
|
|
31
39
|
export { generateRay } from "./ray";
|
|
@@ -41,205 +49,184 @@ interface RTState {
|
|
|
41
49
|
instanceCount: GPUBuffer;
|
|
42
50
|
tlas: TLASBuffers;
|
|
43
51
|
blasAtlas: BLASAtlas;
|
|
52
|
+
blasVersion: number;
|
|
53
|
+
rendered: boolean;
|
|
44
54
|
}
|
|
45
55
|
|
|
46
56
|
export const RTResource = resource<RTState>("raytracing");
|
|
47
57
|
|
|
48
|
-
|
|
58
|
+
interface RTRenderConfig {
|
|
49
59
|
scene: GPUBuffer;
|
|
50
60
|
sky: GPUBuffer;
|
|
51
61
|
data: GPUBuffer;
|
|
52
|
-
matrices: GPUBuffer;
|
|
53
|
-
sizes: GPUBuffer;
|
|
54
|
-
indirect: GPUBuffer;
|
|
55
|
-
getSurfaces: () => SurfaceData[];
|
|
56
|
-
getRaytracing: () => boolean;
|
|
57
|
-
getClearColor: () => { r: number; g: number; b: number };
|
|
58
|
-
getSky: () => boolean;
|
|
59
|
-
acquire?: (message?: string) => (() => void) | undefined;
|
|
60
|
-
batches: () => (Batch | null)[];
|
|
61
62
|
tlasNodes: GPUBuffer;
|
|
62
63
|
tlasInstanceIds: GPUBuffer;
|
|
63
|
-
blasNodes: GPUBuffer;
|
|
64
|
-
blasTriIds: GPUBuffer;
|
|
65
|
-
blasTriangles: GPUBuffer;
|
|
66
|
-
blasMeta: GPUBuffer;
|
|
67
64
|
instanceInverses: GPUBuffer;
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
65
|
+
getBlasAtlas: () => BLASAtlas;
|
|
66
|
+
getSurfaces: () => SurfaceData[];
|
|
67
|
+
getRaytracing: () => boolean;
|
|
68
|
+
setRendered: (value: boolean) => void;
|
|
69
|
+
acquire?: (message?: string) => (() => void) | undefined;
|
|
70
|
+
}
|
|
73
71
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
72
|
+
interface RTRenderGPU {
|
|
73
|
+
pipeline: GPUComputePipeline | null;
|
|
74
|
+
compiling: boolean;
|
|
75
|
+
bindGroup0: GPUBindGroup | null;
|
|
76
|
+
bindGroup1: GPUBindGroup | null;
|
|
77
|
+
cachedWidth: number;
|
|
78
|
+
cachedHeight: number;
|
|
79
|
+
cachedBlasAtlas: BLASAtlas | null;
|
|
80
|
+
}
|
|
78
81
|
|
|
79
|
-
|
|
80
|
-
|
|
82
|
+
async function compileRTPipeline(
|
|
83
|
+
device: GPUDevice,
|
|
84
|
+
surfaces: SurfaceData[]
|
|
85
|
+
): Promise<GPUComputePipeline> {
|
|
86
|
+
const code = compileUberShader(surfaces);
|
|
87
|
+
const module = device.createShaderModule({ code });
|
|
88
|
+
return device.createComputePipelineAsync({
|
|
89
|
+
layout: "auto",
|
|
90
|
+
compute: { module, entryPoint: "main" },
|
|
91
|
+
});
|
|
92
|
+
}
|
|
81
93
|
|
|
82
|
-
|
|
83
|
-
|
|
94
|
+
function triggerCompile(gpu: RTRenderGPU, device: GPUDevice, config: RTRenderConfig): void {
|
|
95
|
+
gpu.compiling = true;
|
|
96
|
+
const release = config.acquire?.("compiling shaders");
|
|
97
|
+
const surfaces = config.getSurfaces();
|
|
98
|
+
compileRTPipeline(device, surfaces)
|
|
99
|
+
.then((p) => {
|
|
100
|
+
gpu.pipeline = p;
|
|
101
|
+
})
|
|
102
|
+
.finally(() => {
|
|
103
|
+
gpu.compiling = false;
|
|
104
|
+
release?.();
|
|
105
|
+
});
|
|
106
|
+
}
|
|
84
107
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
const eidView = ctx.getTextureView("eid");
|
|
108
|
+
function executeRTRender(gpu: RTRenderGPU, ctx: ExecutionContext, config: RTRenderConfig): void {
|
|
109
|
+
const wantRT = config.getRaytracing();
|
|
88
110
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
111
|
+
if (!wantRT) {
|
|
112
|
+
config.setRendered(false);
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
92
115
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
const width = colorTexture.width;
|
|
97
|
-
const height = colorTexture.height;
|
|
98
|
-
|
|
99
|
-
if (width !== cachedWidth || height !== cachedHeight) {
|
|
100
|
-
bindGroup0 = device.createBindGroup({
|
|
101
|
-
layout: rtPipeline.getBindGroupLayout(0),
|
|
102
|
-
entries: [
|
|
103
|
-
{ binding: 0, resource: { buffer: config.scene } },
|
|
104
|
-
{ binding: 1, resource: { buffer: config.data } },
|
|
105
|
-
{ binding: 2, resource: colorView },
|
|
106
|
-
{ binding: 3, resource: depthView },
|
|
107
|
-
{ binding: 4, resource: eidView },
|
|
108
|
-
{ binding: 5, resource: { buffer: config.sky } },
|
|
109
|
-
],
|
|
110
|
-
});
|
|
116
|
+
if (!gpu.pipeline && !gpu.compiling) {
|
|
117
|
+
triggerCompile(gpu, ctx.device, config);
|
|
118
|
+
}
|
|
111
119
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
{ binding: 1, resource: { buffer: config.tlasInstanceIds } },
|
|
117
|
-
{ binding: 2, resource: { buffer: config.blasNodes } },
|
|
118
|
-
{ binding: 3, resource: { buffer: config.blasTriIds } },
|
|
119
|
-
{ binding: 4, resource: { buffer: config.blasTriangles } },
|
|
120
|
-
{ binding: 5, resource: { buffer: config.blasMeta } },
|
|
121
|
-
{ binding: 6, resource: { buffer: config.instanceInverses } },
|
|
122
|
-
],
|
|
123
|
-
});
|
|
120
|
+
if (!gpu.pipeline) {
|
|
121
|
+
config.setRendered(false);
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
124
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
125
|
+
const { device, encoder } = ctx;
|
|
126
|
+
|
|
127
|
+
const colorView = ctx.getTextureView("color");
|
|
128
|
+
const depthView = ctx.getTextureView("depth");
|
|
129
|
+
const eidView = ctx.getTextureView("eid");
|
|
128
130
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
pass.setBindGroup(1, bindGroup1!);
|
|
133
|
-
pass.dispatchWorkgroups(Math.ceil(width / 8), Math.ceil(height / 8));
|
|
134
|
-
pass.end();
|
|
131
|
+
if (!colorView || !depthView || !eidView) {
|
|
132
|
+
config.setRendered(false);
|
|
133
|
+
return;
|
|
135
134
|
}
|
|
136
135
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
const clearPass = ctx.encoder.beginRenderPass({
|
|
143
|
-
colorAttachments: [
|
|
144
|
-
{
|
|
145
|
-
view: depthView as GPUTextureView,
|
|
146
|
-
clearValue: { r: 0, g: 0, b: 0, a: 0 },
|
|
147
|
-
loadOp: "clear" as const,
|
|
148
|
-
storeOp: "store" as const,
|
|
149
|
-
},
|
|
150
|
-
],
|
|
151
|
-
});
|
|
152
|
-
clearPass.end();
|
|
153
|
-
}
|
|
136
|
+
const colorTexture = ctx.getTexture("color");
|
|
137
|
+
if (!colorTexture) {
|
|
138
|
+
config.setRendered(false);
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
154
141
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
142
|
+
const width = colorTexture.width;
|
|
143
|
+
const height = colorTexture.height;
|
|
144
|
+
|
|
145
|
+
const blas = config.getBlasAtlas();
|
|
146
|
+
|
|
147
|
+
const sizeChanged = width !== gpu.cachedWidth || height !== gpu.cachedHeight;
|
|
148
|
+
const blasChanged = blas !== gpu.cachedBlasAtlas;
|
|
149
|
+
|
|
150
|
+
if (sizeChanged) {
|
|
151
|
+
gpu.bindGroup0 = device.createBindGroup({
|
|
152
|
+
layout: gpu.pipeline.getBindGroupLayout(0),
|
|
153
|
+
entries: [
|
|
154
|
+
{ binding: 0, resource: { buffer: config.scene } },
|
|
155
|
+
{ binding: 1, resource: { buffer: config.data } },
|
|
156
|
+
{ binding: 2, resource: colorView },
|
|
157
|
+
{ binding: 3, resource: depthView },
|
|
158
|
+
{ binding: 4, resource: eidView },
|
|
159
|
+
{ binding: 5, resource: { buffer: config.sky } },
|
|
160
|
+
],
|
|
161
|
+
});
|
|
162
|
+
gpu.cachedWidth = width;
|
|
163
|
+
gpu.cachedHeight = height;
|
|
173
164
|
}
|
|
174
165
|
|
|
175
|
-
|
|
176
|
-
device
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
166
|
+
if (sizeChanged || blasChanged) {
|
|
167
|
+
gpu.bindGroup1 = device.createBindGroup({
|
|
168
|
+
layout: gpu.pipeline.getBindGroupLayout(1),
|
|
169
|
+
entries: [
|
|
170
|
+
{ binding: 0, resource: { buffer: config.tlasNodes } },
|
|
171
|
+
{ binding: 1, resource: { buffer: config.tlasInstanceIds } },
|
|
172
|
+
{ binding: 2, resource: { buffer: blas.nodesBuffer } },
|
|
173
|
+
{ binding: 3, resource: { buffer: blas.triIdsBuffer } },
|
|
174
|
+
{ binding: 4, resource: { buffer: blas.trianglesBuffer } },
|
|
175
|
+
{ binding: 5, resource: { buffer: blas.metaBuffer } },
|
|
176
|
+
{ binding: 6, resource: { buffer: config.instanceInverses } },
|
|
177
|
+
],
|
|
184
178
|
});
|
|
179
|
+
gpu.cachedBlasAtlas = blas;
|
|
185
180
|
}
|
|
186
181
|
|
|
182
|
+
const pass = encoder.beginComputePass();
|
|
183
|
+
pass.setPipeline(gpu.pipeline);
|
|
184
|
+
pass.setBindGroup(0, gpu.bindGroup0!);
|
|
185
|
+
pass.setBindGroup(1, gpu.bindGroup1!);
|
|
186
|
+
pass.dispatchWorkgroups(Math.ceil(width / 8), Math.ceil(height / 8));
|
|
187
|
+
pass.end();
|
|
188
|
+
|
|
189
|
+
config.setRendered(true);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
function createRTRenderNode(config: RTRenderConfig): ComputeNode {
|
|
193
|
+
const gpu: RTRenderGPU = {
|
|
194
|
+
pipeline: null,
|
|
195
|
+
compiling: false,
|
|
196
|
+
bindGroup0: null,
|
|
197
|
+
bindGroup1: null,
|
|
198
|
+
cachedWidth: 0,
|
|
199
|
+
cachedHeight: 0,
|
|
200
|
+
cachedBlasAtlas: null,
|
|
201
|
+
};
|
|
202
|
+
|
|
187
203
|
return {
|
|
188
|
-
id: "
|
|
204
|
+
id: "rt-render",
|
|
189
205
|
inputs: [{ id: "tlas-bvh-nodes", access: "read" as const }],
|
|
190
206
|
outputs: [
|
|
191
|
-
{ id: "
|
|
207
|
+
{ id: "rt-output", access: "write" },
|
|
192
208
|
{ id: "depth", access: "write" },
|
|
193
|
-
{ id: "eid", access: "write" },
|
|
194
209
|
],
|
|
195
210
|
|
|
196
211
|
async prepare(device: GPUDevice) {
|
|
197
|
-
const surfaces = config.getSurfaces();
|
|
198
|
-
rasterPipeline = await createRasterPipeline(device, surfaces, COLOR_FORMAT);
|
|
199
|
-
skyPipeline = await createSkyPipeline(device, COLOR_FORMAT);
|
|
200
|
-
|
|
201
212
|
if (config.getRaytracing()) {
|
|
202
|
-
|
|
203
|
-
const release = config.acquire?.("compiling shaders");
|
|
204
|
-
createRTPipeline(device, surfaces)
|
|
205
|
-
.then((p) => {
|
|
206
|
-
rtPipeline = p;
|
|
207
|
-
})
|
|
208
|
-
.finally(() => {
|
|
209
|
-
rtCompiling = false;
|
|
210
|
-
release?.();
|
|
211
|
-
});
|
|
213
|
+
triggerCompile(gpu, device, config);
|
|
212
214
|
}
|
|
213
215
|
},
|
|
214
216
|
|
|
215
217
|
execute(ctx: ExecutionContext) {
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
if (wantRT && !rtPipeline && !rtCompiling) {
|
|
219
|
-
rtCompiling = true;
|
|
220
|
-
const release = config.acquire?.("compiling shaders");
|
|
221
|
-
const surfaces = config.getSurfaces();
|
|
222
|
-
createRTPipeline(ctx.device, surfaces)
|
|
223
|
-
.then((p) => {
|
|
224
|
-
rtPipeline = p;
|
|
225
|
-
})
|
|
226
|
-
.finally(() => {
|
|
227
|
-
rtCompiling = false;
|
|
228
|
-
release?.();
|
|
229
|
-
});
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
if (wantRT && rtPipeline) {
|
|
233
|
-
executeRT(ctx);
|
|
234
|
-
} else {
|
|
235
|
-
executeRasterFallback(ctx);
|
|
236
|
-
}
|
|
218
|
+
executeRTRender(gpu, ctx, config);
|
|
237
219
|
},
|
|
238
220
|
};
|
|
239
221
|
}
|
|
240
222
|
|
|
241
223
|
export const RaytracingPlugin: Plugin = {
|
|
242
224
|
systems: [],
|
|
225
|
+
components: {
|
|
226
|
+
Raytracing,
|
|
227
|
+
RaytracingShadowSettings,
|
|
228
|
+
Dynamic,
|
|
229
|
+
},
|
|
243
230
|
dependencies: [RasterPlugin],
|
|
244
231
|
|
|
245
232
|
async initialize(state: State) {
|
|
@@ -247,6 +234,12 @@ export const RaytracingPlugin: Plugin = {
|
|
|
247
234
|
const render = Render.from(state);
|
|
248
235
|
if (!compute || !render) return;
|
|
249
236
|
|
|
237
|
+
registerShadowSettings({
|
|
238
|
+
softness: RaytracingShadowSettings.softness,
|
|
239
|
+
samples: RaytracingShadowSettings.samples,
|
|
240
|
+
isActive: (s, eid) => s.hasComponent(eid, RaytracingShadowSettings),
|
|
241
|
+
});
|
|
242
|
+
|
|
250
243
|
const { device } = compute;
|
|
251
244
|
|
|
252
245
|
const createPropertyBuffer = (size: number, label?: string) =>
|
|
@@ -268,6 +261,8 @@ export const RaytracingPlugin: Plugin = {
|
|
|
268
261
|
}),
|
|
269
262
|
tlas: createTLASBuffers(device),
|
|
270
263
|
blasAtlas,
|
|
264
|
+
blasVersion: getMeshVersion(),
|
|
265
|
+
rendered: false,
|
|
271
266
|
};
|
|
272
267
|
|
|
273
268
|
state.setResource(RTResource, rtState);
|
|
@@ -290,35 +285,75 @@ export const RaytracingPlugin: Plugin = {
|
|
|
290
285
|
outputs: [{ id: "instance-count", access: "write" }],
|
|
291
286
|
execute(ctx) {
|
|
292
287
|
if (!getRaytracing()) return;
|
|
288
|
+
|
|
289
|
+
const currentMeshVersion = getMeshVersion();
|
|
290
|
+
if (currentMeshVersion !== rtState.blasVersion) {
|
|
291
|
+
rtState.blasAtlas.nodesBuffer.destroy();
|
|
292
|
+
rtState.blasAtlas.triIdsBuffer.destroy();
|
|
293
|
+
rtState.blasAtlas.metaBuffer.destroy();
|
|
294
|
+
rtState.blasAtlas.trianglesBuffer.destroy();
|
|
295
|
+
rtState.blasAtlas.shapeAABBs.destroy();
|
|
296
|
+
rtState.blasAtlas.treeNodesBuffer.destroy();
|
|
297
|
+
rtState.blasAtlas.parentIndicesBuffer.destroy();
|
|
298
|
+
rtState.blasAtlas.baseTrianglesBuffer.destroy();
|
|
299
|
+
rtState.blasAtlas.boundsFlagsBuffer.destroy();
|
|
300
|
+
rtState.blasAtlas = createBLASAtlas(ctx.device, getMesh);
|
|
301
|
+
rtState.blasVersion = currentMeshVersion;
|
|
302
|
+
}
|
|
303
|
+
|
|
293
304
|
const meshEntities = state.query([Mesh, WorldTransform]);
|
|
294
|
-
let
|
|
305
|
+
let entityCount = 0;
|
|
295
306
|
for (const eid of meshEntities) {
|
|
296
|
-
entityIds[
|
|
297
|
-
|
|
307
|
+
entityIds[entityCount] = eid;
|
|
308
|
+
entityCount++;
|
|
298
309
|
}
|
|
299
310
|
ctx.device.queue.writeBuffer(
|
|
300
311
|
rtState.tlas.entityIds,
|
|
301
312
|
0,
|
|
302
313
|
entityIds,
|
|
303
314
|
0,
|
|
304
|
-
Math.max(
|
|
315
|
+
Math.max(entityCount, 1)
|
|
305
316
|
);
|
|
306
|
-
countBuf[0] =
|
|
317
|
+
countBuf[0] = entityCount;
|
|
307
318
|
ctx.device.queue.writeBuffer(rtState.instanceCount, 0, countBuf);
|
|
308
319
|
},
|
|
309
320
|
};
|
|
310
321
|
compute.graph.add(uploadNode);
|
|
311
322
|
|
|
323
|
+
const getDynamicShapes = () => {
|
|
324
|
+
const result = new Map<number, SurfaceData>();
|
|
325
|
+
const surfaces = getDefaultAllSurfaces();
|
|
326
|
+
for (const eid of state.query([Mesh, Dynamic])) {
|
|
327
|
+
const shapeId = Mesh.shape[eid];
|
|
328
|
+
const surfaceType = Surface.type[eid] ?? 0;
|
|
329
|
+
const data = surfaces[surfaceType];
|
|
330
|
+
if (data?.vertex && !result.has(shapeId)) {
|
|
331
|
+
result.set(shapeId, data);
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
return result;
|
|
335
|
+
};
|
|
336
|
+
|
|
337
|
+
const blasRefitNode = createBLASRefitNode({
|
|
338
|
+
getBlasAtlas: () => rtState.blasAtlas,
|
|
339
|
+
scene: render.scene,
|
|
340
|
+
sky: render.sky,
|
|
341
|
+
getDynamicShapes,
|
|
342
|
+
getRaytracing,
|
|
343
|
+
});
|
|
344
|
+
compute.graph.add(blasRefitNode);
|
|
345
|
+
|
|
312
346
|
const instanceNode = createInstanceNode({
|
|
313
347
|
matrices: render.matrices,
|
|
314
348
|
sizes: render.sizes,
|
|
315
349
|
shapes: render.shapes,
|
|
316
|
-
shapeAABBs: rtState.blasAtlas.shapeAABBs,
|
|
317
350
|
entityCount: render.entityCountBuffer,
|
|
318
351
|
instanceAABBs: rtState.instanceAABBs,
|
|
319
352
|
instanceInverses: rtState.instanceInverses,
|
|
353
|
+
getShapeAABBs: () => rtState.blasAtlas.shapeAABBs,
|
|
320
354
|
getEntityCount: () => render.entityCount,
|
|
321
355
|
getRaytracing,
|
|
356
|
+
inputs: [{ id: "blas-nodes", access: "read" }],
|
|
322
357
|
});
|
|
323
358
|
compute.graph.add(instanceNode);
|
|
324
359
|
|
|
@@ -338,43 +373,37 @@ export const RaytracingPlugin: Plugin = {
|
|
|
338
373
|
})
|
|
339
374
|
);
|
|
340
375
|
|
|
341
|
-
const getActiveClearColor = () => {
|
|
342
|
-
for (const eid of state.query([Camera])) {
|
|
343
|
-
if (Camera.active[eid]) return getClearColor(eid);
|
|
344
|
-
}
|
|
345
|
-
return { r: 0, g: 0, b: 0 };
|
|
346
|
-
};
|
|
347
|
-
|
|
348
|
-
const getSky = () => {
|
|
349
|
-
for (const eid of state.query([Camera])) {
|
|
350
|
-
if (Camera.active[eid]) return hasSkyComponent(state, eid);
|
|
351
|
-
}
|
|
352
|
-
return false;
|
|
353
|
-
};
|
|
354
|
-
|
|
355
376
|
const rasterState = RasterResource.from(state);
|
|
356
377
|
|
|
357
|
-
const
|
|
378
|
+
const rtRenderNode = createRTRenderNode({
|
|
358
379
|
scene: render.scene,
|
|
359
380
|
sky: render.sky,
|
|
360
381
|
data: render.data,
|
|
361
|
-
matrices: render.matrices,
|
|
362
|
-
sizes: render.sizes,
|
|
363
|
-
indirect: rasterState!.indirect,
|
|
364
|
-
getSurfaces: getDefaultAllSurfaces,
|
|
365
|
-
getRaytracing,
|
|
366
|
-
getClearColor: getActiveClearColor,
|
|
367
|
-
getSky,
|
|
368
|
-
acquire: (message) => Activity.from(state)?.acquire(message),
|
|
369
|
-
batches: () => rasterState?.batches ?? [],
|
|
370
382
|
tlasNodes: rtState.tlas.bvhNodes,
|
|
371
383
|
tlasInstanceIds: rtState.tlas.instanceIds,
|
|
372
|
-
blasNodes: rtState.blasAtlas.nodesBuffer,
|
|
373
|
-
blasTriIds: rtState.blasAtlas.triIdsBuffer,
|
|
374
|
-
blasTriangles: rtState.blasAtlas.trianglesBuffer,
|
|
375
|
-
blasMeta: rtState.blasAtlas.metaBuffer,
|
|
376
384
|
instanceInverses: rtState.instanceInverses,
|
|
385
|
+
getBlasAtlas: () => rtState.blasAtlas,
|
|
386
|
+
getSurfaces: getDefaultAllSurfaces,
|
|
387
|
+
getRaytracing,
|
|
388
|
+
setRendered: (v: boolean) => {
|
|
389
|
+
rtState.rendered = v;
|
|
390
|
+
if (rasterState) rasterState.rtRendered = v;
|
|
391
|
+
},
|
|
392
|
+
acquire: (message) => Activity.from(state)?.acquire(message),
|
|
377
393
|
});
|
|
378
|
-
compute.graph.
|
|
394
|
+
compute.graph.add(rtRenderNode);
|
|
395
|
+
|
|
396
|
+
const existingForward = compute.graph.nodes.get("forward");
|
|
397
|
+
if (existingForward) {
|
|
398
|
+
const wrappedForward: ComputeNode = {
|
|
399
|
+
id: "forward",
|
|
400
|
+
inputs: [...existingForward.inputs, { id: "rt-output", access: "read" as const }],
|
|
401
|
+
outputs: existingForward.outputs,
|
|
402
|
+
sync: existingForward.sync,
|
|
403
|
+
prepare: existingForward.prepare,
|
|
404
|
+
execute: existingForward.execute,
|
|
405
|
+
};
|
|
406
|
+
compute.graph.set("forward", wrappedForward);
|
|
407
|
+
}
|
|
379
408
|
},
|
|
380
409
|
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ComputeNode, ExecutionContext } from "../compute";
|
|
1
|
+
import type { ComputeNode, ExecutionContext, ResourceRef } from "../compute";
|
|
2
2
|
|
|
3
3
|
const WORKGROUP_SIZE = 64;
|
|
4
4
|
|
|
@@ -160,50 +160,62 @@ export interface InstanceConfig {
|
|
|
160
160
|
matrices: GPUBuffer;
|
|
161
161
|
sizes: GPUBuffer;
|
|
162
162
|
shapes: GPUBuffer;
|
|
163
|
-
shapeAABBs: GPUBuffer;
|
|
164
163
|
entityCount: GPUBuffer;
|
|
165
164
|
instanceAABBs: GPUBuffer;
|
|
166
165
|
instanceInverses: GPUBuffer;
|
|
166
|
+
getShapeAABBs: () => GPUBuffer;
|
|
167
167
|
getEntityCount: () => number;
|
|
168
|
-
getRaytracing
|
|
168
|
+
getRaytracing: () => boolean;
|
|
169
|
+
inputs?: ResourceRef[];
|
|
169
170
|
}
|
|
170
171
|
|
|
171
172
|
export function createInstanceNode(config: InstanceConfig): ComputeNode {
|
|
172
173
|
let pipeline: GPUComputePipeline | null = null;
|
|
173
174
|
let bindGroup: GPUBindGroup | null = null;
|
|
175
|
+
let cachedShapeAABBs: GPUBuffer | null = null;
|
|
176
|
+
|
|
177
|
+
function rebuildBindGroup(device: GPUDevice, shapeAABBs: GPUBuffer): void {
|
|
178
|
+
bindGroup = device.createBindGroup({
|
|
179
|
+
layout: pipeline!.getBindGroupLayout(0),
|
|
180
|
+
entries: [
|
|
181
|
+
{ binding: 0, resource: { buffer: config.matrices } },
|
|
182
|
+
{ binding: 1, resource: { buffer: config.sizes } },
|
|
183
|
+
{ binding: 2, resource: { buffer: config.shapes } },
|
|
184
|
+
{ binding: 3, resource: { buffer: shapeAABBs } },
|
|
185
|
+
{ binding: 4, resource: { buffer: config.entityCount } },
|
|
186
|
+
{ binding: 5, resource: { buffer: config.instanceAABBs } },
|
|
187
|
+
{ binding: 6, resource: { buffer: config.instanceInverses } },
|
|
188
|
+
],
|
|
189
|
+
});
|
|
190
|
+
cachedShapeAABBs = shapeAABBs;
|
|
191
|
+
}
|
|
174
192
|
|
|
175
193
|
return {
|
|
176
194
|
id: "instance",
|
|
177
|
-
inputs: [],
|
|
195
|
+
inputs: config.inputs ?? [],
|
|
178
196
|
outputs: [
|
|
179
197
|
{ id: "instance-aabbs", access: "write" },
|
|
180
198
|
{ id: "instance-inverses", access: "write" },
|
|
181
199
|
],
|
|
182
200
|
|
|
183
201
|
async prepare(device: GPUDevice) {
|
|
184
|
-
const module =
|
|
202
|
+
const module = device.createShaderModule({ code: shader });
|
|
185
203
|
|
|
186
204
|
pipeline = await device.createComputePipelineAsync({
|
|
187
205
|
layout: "auto",
|
|
188
206
|
compute: { module, entryPoint: "main" },
|
|
189
207
|
});
|
|
190
208
|
|
|
191
|
-
|
|
192
|
-
layout: pipeline.getBindGroupLayout(0),
|
|
193
|
-
entries: [
|
|
194
|
-
{ binding: 0, resource: { buffer: config.matrices } },
|
|
195
|
-
{ binding: 1, resource: { buffer: config.sizes } },
|
|
196
|
-
{ binding: 2, resource: { buffer: config.shapes } },
|
|
197
|
-
{ binding: 3, resource: { buffer: config.shapeAABBs } },
|
|
198
|
-
{ binding: 4, resource: { buffer: config.entityCount } },
|
|
199
|
-
{ binding: 5, resource: { buffer: config.instanceAABBs } },
|
|
200
|
-
{ binding: 6, resource: { buffer: config.instanceInverses } },
|
|
201
|
-
],
|
|
202
|
-
});
|
|
209
|
+
rebuildBindGroup(device, config.getShapeAABBs());
|
|
203
210
|
},
|
|
204
211
|
|
|
205
212
|
execute(ctx: ExecutionContext) {
|
|
206
|
-
if (
|
|
213
|
+
if (!config.getRaytracing()) return;
|
|
214
|
+
|
|
215
|
+
const currentAABBs = config.getShapeAABBs();
|
|
216
|
+
if (currentAABBs !== cachedShapeAABBs) {
|
|
217
|
+
rebuildBindGroup(ctx.device, currentAABBs);
|
|
218
|
+
}
|
|
207
219
|
|
|
208
220
|
const workgroups = Math.ceil(config.getEntityCount() / WORKGROUP_SIZE);
|
|
209
221
|
|