@multiplekex/shallot 0.1.12 → 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.
- package/package.json +3 -4
- package/src/core/builder.ts +71 -32
- package/src/core/component.ts +25 -11
- package/src/core/index.ts +14 -13
- package/src/core/math.ts +135 -0
- package/src/core/runtime.ts +0 -1
- package/src/core/state.ts +9 -68
- package/src/core/xml.ts +381 -265
- package/src/editor/format.ts +5 -0
- package/src/editor/index.ts +101 -0
- package/src/extras/arrows/index.ts +28 -69
- package/src/extras/gradient/index.ts +36 -52
- package/src/extras/lines/index.ts +51 -122
- package/src/extras/orbit/index.ts +40 -15
- package/src/extras/text/font.ts +546 -0
- package/src/extras/text/index.ts +158 -204
- package/src/extras/text/sdf.ts +429 -0
- package/src/standard/activity/index.ts +172 -0
- package/src/standard/compute/graph.ts +23 -23
- package/src/standard/compute/index.ts +76 -61
- package/src/standard/defaults.ts +8 -5
- package/src/standard/index.ts +1 -0
- package/src/standard/input/index.ts +30 -19
- package/src/standard/loading/index.ts +18 -13
- package/src/standard/render/bvh/blas.ts +752 -0
- package/src/standard/render/bvh/radix.ts +476 -0
- package/src/standard/render/bvh/structs.ts +167 -0
- package/src/standard/render/bvh/tlas.ts +886 -0
- package/src/standard/render/bvh/traverse.ts +467 -0
- package/src/standard/render/camera.ts +302 -27
- package/src/standard/render/data.ts +93 -0
- package/src/standard/render/depth.ts +117 -0
- package/src/standard/render/forward/index.ts +259 -0
- package/src/standard/render/forward/raster.ts +228 -0
- package/src/standard/render/index.ts +443 -70
- package/src/standard/render/indirect.ts +40 -0
- package/src/standard/render/instance.ts +214 -0
- package/src/standard/render/intersection.ts +72 -0
- package/src/standard/render/light.ts +16 -16
- package/src/standard/render/mesh/index.ts +67 -75
- package/src/standard/render/mesh/unified.ts +96 -0
- package/src/standard/render/{transparent.ts → overlay.ts} +14 -15
- package/src/standard/render/pass.ts +10 -4
- package/src/standard/render/postprocess.ts +142 -64
- package/src/standard/render/ray.ts +61 -0
- package/src/standard/render/scene.ts +38 -164
- package/src/standard/render/shaders.ts +484 -0
- package/src/standard/render/surface/compile.ts +3 -10
- package/src/standard/render/surface/index.ts +60 -30
- package/src/standard/render/surface/noise.ts +45 -0
- package/src/standard/render/surface/structs.ts +60 -19
- package/src/standard/render/surface/wgsl.ts +573 -0
- package/src/standard/render/triangle.ts +84 -0
- package/src/standard/transforms/index.ts +4 -6
- package/src/standard/tween/index.ts +10 -1
- package/src/standard/tween/sequence.ts +24 -16
- package/src/standard/tween/tween.ts +67 -16
- package/src/core/types.ts +0 -37
- package/src/standard/compute/inspect.ts +0 -201
- package/src/standard/compute/pass.ts +0 -23
- package/src/standard/compute/timing.ts +0 -139
- package/src/standard/render/forward.ts +0 -273
|
@@ -0,0 +1,476 @@
|
|
|
1
|
+
import type { ComputeNode, ExecutionContext } from "../../compute";
|
|
2
|
+
|
|
3
|
+
const WG_X = 16;
|
|
4
|
+
const WG_Y = 16;
|
|
5
|
+
const WG_SIZE = WG_X * WG_Y;
|
|
6
|
+
const ITEMS_PER_WG = 2 * WG_SIZE;
|
|
7
|
+
|
|
8
|
+
const blockSumShader = /* wgsl */ `
|
|
9
|
+
@group(0) @binding(0) var<storage, read> input: array<u32>;
|
|
10
|
+
@group(0) @binding(1) var<storage, read_write> localSums: array<u32>;
|
|
11
|
+
@group(0) @binding(2) var<storage, read_write> blockSums: array<u32>;
|
|
12
|
+
|
|
13
|
+
override WG_COUNT: u32;
|
|
14
|
+
override BIT: u32;
|
|
15
|
+
override COUNT: u32;
|
|
16
|
+
|
|
17
|
+
var<workgroup> wgData: array<u32, 2 * (${WG_SIZE} + 1)>;
|
|
18
|
+
|
|
19
|
+
@compute @workgroup_size(${WG_X}, ${WG_Y}, 1)
|
|
20
|
+
fn main(
|
|
21
|
+
@builtin(workgroup_id) wid: vec3<u32>,
|
|
22
|
+
@builtin(num_workgroups) wdim: vec3<u32>,
|
|
23
|
+
@builtin(local_invocation_index) tid: u32,
|
|
24
|
+
) {
|
|
25
|
+
let workgroup = wid.x + wid.y * wdim.x;
|
|
26
|
+
let base = workgroup * ${WG_SIZE}u;
|
|
27
|
+
let gid = base + tid;
|
|
28
|
+
|
|
29
|
+
let val = select(input[gid], 0u, gid >= COUNT);
|
|
30
|
+
let bits = (val >> BIT) & 0x3;
|
|
31
|
+
|
|
32
|
+
var sums = array<u32, 4>(0, 0, 0, 0);
|
|
33
|
+
var lastThread = 0xffffffffu;
|
|
34
|
+
|
|
35
|
+
if (workgroup < WG_COUNT) {
|
|
36
|
+
lastThread = min(${WG_SIZE}u, COUNT - base) - 1;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
let stride = ${WG_SIZE}u + 1;
|
|
40
|
+
var swap = 0u;
|
|
41
|
+
var inOff = tid;
|
|
42
|
+
var outOff = tid + stride;
|
|
43
|
+
|
|
44
|
+
for (var b = 0u; b < 4; b++) {
|
|
45
|
+
let mask = select(0u, 1u, bits == b);
|
|
46
|
+
wgData[inOff + 1] = mask;
|
|
47
|
+
workgroupBarrier();
|
|
48
|
+
|
|
49
|
+
var sum = 0u;
|
|
50
|
+
for (var off = 1u; off < ${WG_SIZE}u; off *= 2) {
|
|
51
|
+
if (tid >= off) {
|
|
52
|
+
sum = wgData[inOff] + wgData[inOff - off];
|
|
53
|
+
} else {
|
|
54
|
+
sum = wgData[inOff];
|
|
55
|
+
}
|
|
56
|
+
wgData[outOff] = sum;
|
|
57
|
+
outOff = inOff;
|
|
58
|
+
swap = stride - swap;
|
|
59
|
+
inOff = tid + swap;
|
|
60
|
+
workgroupBarrier();
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
sums[b] = sum;
|
|
64
|
+
|
|
65
|
+
if (tid == lastThread) {
|
|
66
|
+
blockSums[b * WG_COUNT + workgroup] = sum + mask;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
outOff = inOff;
|
|
70
|
+
swap = stride - swap;
|
|
71
|
+
inOff = tid + swap;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
if (gid < COUNT) {
|
|
75
|
+
localSums[gid] = sums[bits];
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
`;
|
|
79
|
+
|
|
80
|
+
const reorderShader = /* wgsl */ `
|
|
81
|
+
@group(0) @binding(0) var<storage, read> inKeys: array<u32>;
|
|
82
|
+
@group(0) @binding(1) var<storage, read_write> outKeys: array<u32>;
|
|
83
|
+
@group(0) @binding(2) var<storage, read> localSums: array<u32>;
|
|
84
|
+
@group(0) @binding(3) var<storage, read> blockSums: array<u32>;
|
|
85
|
+
@group(0) @binding(4) var<storage, read> inVals: array<u32>;
|
|
86
|
+
@group(0) @binding(5) var<storage, read_write> outVals: array<u32>;
|
|
87
|
+
|
|
88
|
+
override WG_COUNT: u32;
|
|
89
|
+
override BIT: u32;
|
|
90
|
+
override COUNT: u32;
|
|
91
|
+
|
|
92
|
+
@compute @workgroup_size(${WG_X}, ${WG_Y}, 1)
|
|
93
|
+
fn main(
|
|
94
|
+
@builtin(workgroup_id) wid: vec3<u32>,
|
|
95
|
+
@builtin(num_workgroups) wdim: vec3<u32>,
|
|
96
|
+
@builtin(local_invocation_index) tid: u32,
|
|
97
|
+
) {
|
|
98
|
+
let workgroup = wid.x + wid.y * wdim.x;
|
|
99
|
+
let gid = workgroup * ${WG_SIZE}u + tid;
|
|
100
|
+
|
|
101
|
+
if (gid >= COUNT) { return; }
|
|
102
|
+
|
|
103
|
+
let k = inKeys[gid];
|
|
104
|
+
let v = inVals[gid];
|
|
105
|
+
let bits = (k >> BIT) & 0x3;
|
|
106
|
+
let dst = blockSums[bits * WG_COUNT + workgroup] + localSums[gid];
|
|
107
|
+
|
|
108
|
+
outKeys[dst] = k;
|
|
109
|
+
outVals[dst] = v;
|
|
110
|
+
}
|
|
111
|
+
`;
|
|
112
|
+
|
|
113
|
+
const prefixSumShader = /* wgsl */ `
|
|
114
|
+
@group(0) @binding(0) var<storage, read_write> data: array<u32>;
|
|
115
|
+
@group(0) @binding(1) var<storage, read_write> blockSums: array<u32>;
|
|
116
|
+
|
|
117
|
+
override COUNT: u32;
|
|
118
|
+
|
|
119
|
+
var<workgroup> temp: array<u32, ${ITEMS_PER_WG * 2}>;
|
|
120
|
+
|
|
121
|
+
@compute @workgroup_size(${WG_X}, ${WG_Y}, 1)
|
|
122
|
+
fn scan(
|
|
123
|
+
@builtin(workgroup_id) wid: vec3<u32>,
|
|
124
|
+
@builtin(num_workgroups) wdim: vec3<u32>,
|
|
125
|
+
@builtin(local_invocation_index) tid: u32,
|
|
126
|
+
) {
|
|
127
|
+
let workgroup = wid.x + wid.y * wdim.x;
|
|
128
|
+
let base = workgroup * ${WG_SIZE}u;
|
|
129
|
+
let gid = base + tid;
|
|
130
|
+
let eid = gid * 2;
|
|
131
|
+
|
|
132
|
+
temp[tid * 2] = select(data[eid], 0u, eid >= COUNT);
|
|
133
|
+
temp[tid * 2 + 1] = select(data[eid + 1], 0u, eid + 1 >= COUNT);
|
|
134
|
+
|
|
135
|
+
var offset = 1u;
|
|
136
|
+
for (var d = ${ITEMS_PER_WG}u >> 1; d > 0; d >>= 1) {
|
|
137
|
+
workgroupBarrier();
|
|
138
|
+
if (tid < d) {
|
|
139
|
+
let ai = offset * (tid * 2 + 1) - 1;
|
|
140
|
+
let bi = offset * (tid * 2 + 2) - 1;
|
|
141
|
+
temp[bi] += temp[ai];
|
|
142
|
+
}
|
|
143
|
+
offset *= 2;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
if (tid == 0) {
|
|
147
|
+
blockSums[workgroup] = temp[${ITEMS_PER_WG}u - 1];
|
|
148
|
+
temp[${ITEMS_PER_WG}u - 1] = 0;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
for (var d = 1u; d < ${ITEMS_PER_WG}u; d *= 2) {
|
|
152
|
+
offset >>= 1;
|
|
153
|
+
workgroupBarrier();
|
|
154
|
+
if (tid < d) {
|
|
155
|
+
let ai = offset * (tid * 2 + 1) - 1;
|
|
156
|
+
let bi = offset * (tid * 2 + 2) - 1;
|
|
157
|
+
let t = temp[ai];
|
|
158
|
+
temp[ai] = temp[bi];
|
|
159
|
+
temp[bi] += t;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
workgroupBarrier();
|
|
163
|
+
|
|
164
|
+
if (eid < COUNT) { data[eid] = temp[tid * 2]; }
|
|
165
|
+
if (eid + 1 < COUNT) { data[eid + 1] = temp[tid * 2 + 1]; }
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
@compute @workgroup_size(${WG_X}, ${WG_Y}, 1)
|
|
169
|
+
fn addBlocks(
|
|
170
|
+
@builtin(workgroup_id) wid: vec3<u32>,
|
|
171
|
+
@builtin(num_workgroups) wdim: vec3<u32>,
|
|
172
|
+
@builtin(local_invocation_index) tid: u32,
|
|
173
|
+
) {
|
|
174
|
+
let workgroup = wid.x + wid.y * wdim.x;
|
|
175
|
+
let eid = (workgroup * ${WG_SIZE}u + tid) * 2;
|
|
176
|
+
|
|
177
|
+
if (eid >= COUNT) { return; }
|
|
178
|
+
|
|
179
|
+
let sum = blockSums[workgroup];
|
|
180
|
+
data[eid] += sum;
|
|
181
|
+
if (eid + 1 < COUNT) { data[eid + 1] += sum; }
|
|
182
|
+
}
|
|
183
|
+
`;
|
|
184
|
+
|
|
185
|
+
function dispatchSize(device: GPUDevice, count: number): [number, number] {
|
|
186
|
+
const max = device.limits.maxComputeWorkgroupsPerDimension;
|
|
187
|
+
if (count <= max) return [count, 1];
|
|
188
|
+
const x = Math.ceil(Math.sqrt(count));
|
|
189
|
+
return [x, Math.ceil(count / x)];
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
interface PrefixPass {
|
|
193
|
+
pipeline: GPUComputePipeline;
|
|
194
|
+
bindGroup: GPUBindGroup;
|
|
195
|
+
dispatch: [number, number];
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
class PrefixSum {
|
|
199
|
+
private constructor(private passes: PrefixPass[]) {}
|
|
200
|
+
|
|
201
|
+
static async create(device: GPUDevice, data: GPUBuffer, count: number): Promise<PrefixSum> {
|
|
202
|
+
const passes: PrefixPass[] = [];
|
|
203
|
+
const module = device.createShaderModule({ code: prefixSumShader });
|
|
204
|
+
await PrefixSum.build(device, module, data, count, passes);
|
|
205
|
+
return new PrefixSum(passes);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
private static async build(
|
|
209
|
+
device: GPUDevice,
|
|
210
|
+
module: GPUShaderModule,
|
|
211
|
+
data: GPUBuffer,
|
|
212
|
+
count: number,
|
|
213
|
+
passes: PrefixPass[]
|
|
214
|
+
): Promise<void> {
|
|
215
|
+
const wgCount = Math.ceil(count / ITEMS_PER_WG);
|
|
216
|
+
const dispatch = dispatchSize(device, wgCount);
|
|
217
|
+
|
|
218
|
+
const blockSums = device.createBuffer({
|
|
219
|
+
size: Math.max(wgCount * 4, 4),
|
|
220
|
+
usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_SRC | GPUBufferUsage.COPY_DST,
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
const layout = device.createBindGroupLayout({
|
|
224
|
+
entries: [
|
|
225
|
+
{ binding: 0, visibility: GPUShaderStage.COMPUTE, buffer: { type: "storage" } },
|
|
226
|
+
{ binding: 1, visibility: GPUShaderStage.COMPUTE, buffer: { type: "storage" } },
|
|
227
|
+
],
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
const bindGroup = device.createBindGroup({
|
|
231
|
+
layout,
|
|
232
|
+
entries: [
|
|
233
|
+
{ binding: 0, resource: { buffer: data } },
|
|
234
|
+
{ binding: 1, resource: { buffer: blockSums } },
|
|
235
|
+
],
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
const pipelineLayout = device.createPipelineLayout({ bindGroupLayouts: [layout] });
|
|
239
|
+
|
|
240
|
+
passes.push({
|
|
241
|
+
pipeline: await device.createComputePipelineAsync({
|
|
242
|
+
layout: pipelineLayout,
|
|
243
|
+
compute: { module, entryPoint: "scan", constants: { COUNT: count } },
|
|
244
|
+
}),
|
|
245
|
+
bindGroup,
|
|
246
|
+
dispatch,
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
if (wgCount > 1) {
|
|
250
|
+
await PrefixSum.build(device, module, blockSums, wgCount, passes);
|
|
251
|
+
|
|
252
|
+
passes.push({
|
|
253
|
+
pipeline: await device.createComputePipelineAsync({
|
|
254
|
+
layout: pipelineLayout,
|
|
255
|
+
compute: { module, entryPoint: "addBlocks", constants: { COUNT: count } },
|
|
256
|
+
}),
|
|
257
|
+
bindGroup,
|
|
258
|
+
dispatch,
|
|
259
|
+
});
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
dispatch(pass: GPUComputePassEncoder): void {
|
|
264
|
+
for (const p of this.passes) {
|
|
265
|
+
pass.setPipeline(p.pipeline);
|
|
266
|
+
pass.setBindGroup(0, p.bindGroup);
|
|
267
|
+
pass.dispatchWorkgroups(p.dispatch[0], p.dispatch[1], 1);
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
interface RadixPass {
|
|
273
|
+
blockSum: { pipeline: GPUComputePipeline; bindGroup: GPUBindGroup };
|
|
274
|
+
reorder: { pipeline: GPUComputePipeline; bindGroup: GPUBindGroup };
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
class RadixSort {
|
|
278
|
+
private constructor(
|
|
279
|
+
private passes: RadixPass[],
|
|
280
|
+
private prefixSum: PrefixSum,
|
|
281
|
+
private workgroups: [number, number]
|
|
282
|
+
) {}
|
|
283
|
+
|
|
284
|
+
static async create(
|
|
285
|
+
device: GPUDevice,
|
|
286
|
+
keys: GPUBuffer,
|
|
287
|
+
values: GPUBuffer,
|
|
288
|
+
count: number
|
|
289
|
+
): Promise<RadixSort> {
|
|
290
|
+
const wgCount = Math.ceil(count / WG_SIZE);
|
|
291
|
+
const workgroups = dispatchSize(device, wgCount);
|
|
292
|
+
|
|
293
|
+
const tmpKeys = device.createBuffer({
|
|
294
|
+
size: count * 4,
|
|
295
|
+
usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_SRC | GPUBufferUsage.COPY_DST,
|
|
296
|
+
});
|
|
297
|
+
const tmpVals = device.createBuffer({
|
|
298
|
+
size: count * 4,
|
|
299
|
+
usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_SRC | GPUBufferUsage.COPY_DST,
|
|
300
|
+
});
|
|
301
|
+
const localSums = device.createBuffer({
|
|
302
|
+
size: count * 4,
|
|
303
|
+
usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_SRC | GPUBufferUsage.COPY_DST,
|
|
304
|
+
});
|
|
305
|
+
const blockSums = device.createBuffer({
|
|
306
|
+
size: 4 * wgCount * 4,
|
|
307
|
+
usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_SRC | GPUBufferUsage.COPY_DST,
|
|
308
|
+
});
|
|
309
|
+
|
|
310
|
+
const prefixSum = await PrefixSum.create(device, blockSums, 4 * wgCount);
|
|
311
|
+
|
|
312
|
+
const blockSumModule = device.createShaderModule({ code: blockSumShader });
|
|
313
|
+
const reorderModule = device.createShaderModule({ code: reorderShader });
|
|
314
|
+
|
|
315
|
+
const blockSumLayout = device.createBindGroupLayout({
|
|
316
|
+
entries: [
|
|
317
|
+
{
|
|
318
|
+
binding: 0,
|
|
319
|
+
visibility: GPUShaderStage.COMPUTE,
|
|
320
|
+
buffer: { type: "read-only-storage" },
|
|
321
|
+
},
|
|
322
|
+
{ binding: 1, visibility: GPUShaderStage.COMPUTE, buffer: { type: "storage" } },
|
|
323
|
+
{ binding: 2, visibility: GPUShaderStage.COMPUTE, buffer: { type: "storage" } },
|
|
324
|
+
],
|
|
325
|
+
});
|
|
326
|
+
|
|
327
|
+
const reorderLayout = device.createBindGroupLayout({
|
|
328
|
+
entries: [
|
|
329
|
+
{
|
|
330
|
+
binding: 0,
|
|
331
|
+
visibility: GPUShaderStage.COMPUTE,
|
|
332
|
+
buffer: { type: "read-only-storage" },
|
|
333
|
+
},
|
|
334
|
+
{ binding: 1, visibility: GPUShaderStage.COMPUTE, buffer: { type: "storage" } },
|
|
335
|
+
{
|
|
336
|
+
binding: 2,
|
|
337
|
+
visibility: GPUShaderStage.COMPUTE,
|
|
338
|
+
buffer: { type: "read-only-storage" },
|
|
339
|
+
},
|
|
340
|
+
{
|
|
341
|
+
binding: 3,
|
|
342
|
+
visibility: GPUShaderStage.COMPUTE,
|
|
343
|
+
buffer: { type: "read-only-storage" },
|
|
344
|
+
},
|
|
345
|
+
{
|
|
346
|
+
binding: 4,
|
|
347
|
+
visibility: GPUShaderStage.COMPUTE,
|
|
348
|
+
buffer: { type: "read-only-storage" },
|
|
349
|
+
},
|
|
350
|
+
{ binding: 5, visibility: GPUShaderStage.COMPUTE, buffer: { type: "storage" } },
|
|
351
|
+
],
|
|
352
|
+
});
|
|
353
|
+
|
|
354
|
+
const pipelinePromises: Promise<{
|
|
355
|
+
blockSum: GPUComputePipeline;
|
|
356
|
+
reorder: GPUComputePipeline;
|
|
357
|
+
}>[] = [];
|
|
358
|
+
|
|
359
|
+
for (let bit = 0; bit < 32; bit += 2) {
|
|
360
|
+
pipelinePromises.push(
|
|
361
|
+
(async () => {
|
|
362
|
+
const [blockSumPipeline, reorderPipeline] = await Promise.all([
|
|
363
|
+
device.createComputePipelineAsync({
|
|
364
|
+
layout: device.createPipelineLayout({
|
|
365
|
+
bindGroupLayouts: [blockSumLayout],
|
|
366
|
+
}),
|
|
367
|
+
compute: {
|
|
368
|
+
module: blockSumModule,
|
|
369
|
+
entryPoint: "main",
|
|
370
|
+
constants: { WG_COUNT: wgCount, BIT: bit, COUNT: count },
|
|
371
|
+
},
|
|
372
|
+
}),
|
|
373
|
+
device.createComputePipelineAsync({
|
|
374
|
+
layout: device.createPipelineLayout({
|
|
375
|
+
bindGroupLayouts: [reorderLayout],
|
|
376
|
+
}),
|
|
377
|
+
compute: {
|
|
378
|
+
module: reorderModule,
|
|
379
|
+
entryPoint: "main",
|
|
380
|
+
constants: { WG_COUNT: wgCount, BIT: bit, COUNT: count },
|
|
381
|
+
},
|
|
382
|
+
}),
|
|
383
|
+
]);
|
|
384
|
+
return { blockSum: blockSumPipeline, reorder: reorderPipeline };
|
|
385
|
+
})()
|
|
386
|
+
);
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
const pipelines = await Promise.all(pipelinePromises);
|
|
390
|
+
const passes: RadixPass[] = [];
|
|
391
|
+
|
|
392
|
+
for (let i = 0; i < 16; i++) {
|
|
393
|
+
const bit = i * 2;
|
|
394
|
+
const even = bit % 4 === 0;
|
|
395
|
+
const inK = even ? keys : tmpKeys;
|
|
396
|
+
const inV = even ? values : tmpVals;
|
|
397
|
+
const outK = even ? tmpKeys : keys;
|
|
398
|
+
const outV = even ? tmpVals : values;
|
|
399
|
+
|
|
400
|
+
passes.push({
|
|
401
|
+
blockSum: {
|
|
402
|
+
pipeline: pipelines[i].blockSum,
|
|
403
|
+
bindGroup: device.createBindGroup({
|
|
404
|
+
layout: blockSumLayout,
|
|
405
|
+
entries: [
|
|
406
|
+
{ binding: 0, resource: { buffer: inK } },
|
|
407
|
+
{ binding: 1, resource: { buffer: localSums } },
|
|
408
|
+
{ binding: 2, resource: { buffer: blockSums } },
|
|
409
|
+
],
|
|
410
|
+
}),
|
|
411
|
+
},
|
|
412
|
+
reorder: {
|
|
413
|
+
pipeline: pipelines[i].reorder,
|
|
414
|
+
bindGroup: device.createBindGroup({
|
|
415
|
+
layout: reorderLayout,
|
|
416
|
+
entries: [
|
|
417
|
+
{ binding: 0, resource: { buffer: inK } },
|
|
418
|
+
{ binding: 1, resource: { buffer: outK } },
|
|
419
|
+
{ binding: 2, resource: { buffer: localSums } },
|
|
420
|
+
{ binding: 3, resource: { buffer: blockSums } },
|
|
421
|
+
{ binding: 4, resource: { buffer: inV } },
|
|
422
|
+
{ binding: 5, resource: { buffer: outV } },
|
|
423
|
+
],
|
|
424
|
+
}),
|
|
425
|
+
},
|
|
426
|
+
});
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
return new RadixSort(passes, prefixSum, workgroups);
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
dispatch(pass: GPUComputePassEncoder): void {
|
|
433
|
+
const [x, y] = this.workgroups;
|
|
434
|
+
for (const p of this.passes) {
|
|
435
|
+
pass.setPipeline(p.blockSum.pipeline);
|
|
436
|
+
pass.setBindGroup(0, p.blockSum.bindGroup);
|
|
437
|
+
pass.dispatchWorkgroups(x, y, 1);
|
|
438
|
+
|
|
439
|
+
this.prefixSum.dispatch(pass);
|
|
440
|
+
|
|
441
|
+
pass.setPipeline(p.reorder.pipeline);
|
|
442
|
+
pass.setBindGroup(0, p.reorder.bindGroup);
|
|
443
|
+
pass.dispatchWorkgroups(x, y, 1);
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
export interface RadixSortConfig {
|
|
449
|
+
keys: GPUBuffer;
|
|
450
|
+
values: GPUBuffer;
|
|
451
|
+
count: number;
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
export function createRadixSort(device: GPUDevice, config: RadixSortConfig): Promise<RadixSort> {
|
|
455
|
+
return RadixSort.create(device, config.keys, config.values, config.count);
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
export function createRadixSortNode(config: RadixSortConfig): ComputeNode {
|
|
459
|
+
let sort: RadixSort | null = null;
|
|
460
|
+
|
|
461
|
+
return {
|
|
462
|
+
id: "radix-sort",
|
|
463
|
+
inputs: [],
|
|
464
|
+
outputs: [],
|
|
465
|
+
|
|
466
|
+
async prepare(device: GPUDevice) {
|
|
467
|
+
sort = await createRadixSort(device, config);
|
|
468
|
+
},
|
|
469
|
+
|
|
470
|
+
execute(ctx: ExecutionContext) {
|
|
471
|
+
const pass = ctx.encoder.beginComputePass();
|
|
472
|
+
sort!.dispatch(pass);
|
|
473
|
+
pass.end();
|
|
474
|
+
},
|
|
475
|
+
};
|
|
476
|
+
}
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
export interface Vec3 {
|
|
2
|
+
x: number;
|
|
3
|
+
y: number;
|
|
4
|
+
z: number;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export interface AABB {
|
|
8
|
+
min: Vec3;
|
|
9
|
+
max: Vec3;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface Triangle {
|
|
13
|
+
v0: Vec3;
|
|
14
|
+
e1: Vec3;
|
|
15
|
+
e2: Vec3;
|
|
16
|
+
n0: Vec3;
|
|
17
|
+
n1: Vec3;
|
|
18
|
+
n2: Vec3;
|
|
19
|
+
entityId: number;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export interface BVHNode {
|
|
23
|
+
min: Vec3;
|
|
24
|
+
max: Vec3;
|
|
25
|
+
leftChild: number;
|
|
26
|
+
rightChild: number;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface Ray {
|
|
30
|
+
origin: Vec3;
|
|
31
|
+
direction: Vec3;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export interface MortonPair {
|
|
35
|
+
code: number;
|
|
36
|
+
triangleId: number;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export interface HitResult {
|
|
40
|
+
hit: boolean;
|
|
41
|
+
t: number;
|
|
42
|
+
entityId: number;
|
|
43
|
+
u: number;
|
|
44
|
+
v: number;
|
|
45
|
+
normal: Vec3;
|
|
46
|
+
worldPos: Vec3;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export const LEAF_FLAG = 0x80000000;
|
|
50
|
+
|
|
51
|
+
export function isLeaf(child: number): boolean {
|
|
52
|
+
return (child & LEAF_FLAG) !== 0;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export function leafIndex(child: number): number {
|
|
56
|
+
return child & ~LEAF_FLAG;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export const LEAF_FLAG_WGSL = /* wgsl */ `const LEAF_FLAG: u32 = 0x80000000u;`;
|
|
60
|
+
|
|
61
|
+
export const TRIANGLE_STRUCT_WGSL = /* wgsl */ `
|
|
62
|
+
struct Triangle {
|
|
63
|
+
v0: vec3<f32>,
|
|
64
|
+
entityId: u32,
|
|
65
|
+
e1: vec3<f32>,
|
|
66
|
+
_pad0: u32,
|
|
67
|
+
e2: vec3<f32>,
|
|
68
|
+
_pad1: u32,
|
|
69
|
+
n0_enc: u32,
|
|
70
|
+
n1_enc: u32,
|
|
71
|
+
n2_enc: u32,
|
|
72
|
+
_pad2: u32,
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
fn octEncode(n: vec3<f32>) -> u32 {
|
|
76
|
+
let absSum = abs(n.x) + abs(n.y) + abs(n.z);
|
|
77
|
+
var vx = n.x / absSum;
|
|
78
|
+
var vy = n.y / absSum;
|
|
79
|
+
let vz = n.z / absSum;
|
|
80
|
+
if (vz < 0.0) {
|
|
81
|
+
let signX = select(-1.0, 1.0, vx >= 0.0);
|
|
82
|
+
let signY = select(-1.0, 1.0, vy >= 0.0);
|
|
83
|
+
let newVx = (1.0 - abs(vy)) * signX;
|
|
84
|
+
let newVy = (1.0 - abs(vx)) * signY;
|
|
85
|
+
vx = newVx;
|
|
86
|
+
vy = newVy;
|
|
87
|
+
}
|
|
88
|
+
let x = u32(clamp((vx * 0.5 + 0.5) * 65535.0, 0.0, 65535.0));
|
|
89
|
+
let y = u32(clamp((vy * 0.5 + 0.5) * 65535.0, 0.0, 65535.0));
|
|
90
|
+
return (y << 16u) | x;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
fn octDecode(enc: u32) -> vec3<f32> {
|
|
94
|
+
let x = f32(enc & 0xFFFFu) / 65535.0 * 2.0 - 1.0;
|
|
95
|
+
let y = f32(enc >> 16u) / 65535.0 * 2.0 - 1.0;
|
|
96
|
+
let z = 1.0 - abs(x) - abs(y);
|
|
97
|
+
var n: vec3<f32>;
|
|
98
|
+
if (z < 0.0) {
|
|
99
|
+
let signX = select(-1.0, 1.0, x >= 0.0);
|
|
100
|
+
let signY = select(-1.0, 1.0, y >= 0.0);
|
|
101
|
+
n = vec3<f32>((1.0 - abs(y)) * signX, (1.0 - abs(x)) * signY, z);
|
|
102
|
+
} else {
|
|
103
|
+
n = vec3<f32>(x, y, z);
|
|
104
|
+
}
|
|
105
|
+
return normalize(n);
|
|
106
|
+
}`;
|
|
107
|
+
|
|
108
|
+
export const TREE_NODE_STRUCT_WGSL = /* wgsl */ `
|
|
109
|
+
struct TreeNode {
|
|
110
|
+
minX: f32,
|
|
111
|
+
minY: f32,
|
|
112
|
+
minZ: f32,
|
|
113
|
+
leftChild: u32,
|
|
114
|
+
maxX: f32,
|
|
115
|
+
maxY: f32,
|
|
116
|
+
maxZ: f32,
|
|
117
|
+
rightChild: u32,
|
|
118
|
+
}`;
|
|
119
|
+
|
|
120
|
+
export const BVH_NODE_STRUCT_WGSL = /* wgsl */ `
|
|
121
|
+
struct BVHNode {
|
|
122
|
+
c0_minX: f32, c0_minY: f32, c0_minZ: f32, child0: u32,
|
|
123
|
+
c0_maxX: f32, c0_maxY: f32, c0_maxZ: f32, _pad0: u32,
|
|
124
|
+
c1_minX: f32, c1_minY: f32, c1_minZ: f32, child1: u32,
|
|
125
|
+
c1_maxX: f32, c1_maxY: f32, c1_maxZ: f32, _pad1: u32,
|
|
126
|
+
c2_minX: f32, c2_minY: f32, c2_minZ: f32, child2: u32,
|
|
127
|
+
c2_maxX: f32, c2_maxY: f32, c2_maxZ: f32, _pad2: u32,
|
|
128
|
+
c3_minX: f32, c3_minY: f32, c3_minZ: f32, child3: u32,
|
|
129
|
+
c3_maxX: f32, c3_maxY: f32, c3_maxZ: f32, _pad3: u32,
|
|
130
|
+
}`;
|
|
131
|
+
|
|
132
|
+
export const TREE_NODE_SIZE = 32;
|
|
133
|
+
export const BVH_NODE_SIZE = 128;
|
|
134
|
+
export const BLAS_NODE_SIZE = 32;
|
|
135
|
+
export const BLAS_TRIANGLE_SIZE = 64;
|
|
136
|
+
export const INVALID_NODE = 0xffffffff;
|
|
137
|
+
|
|
138
|
+
export const BLAS_NODE_STRUCT_WGSL = /* wgsl */ `
|
|
139
|
+
struct BLASNode {
|
|
140
|
+
minX: f32, minY: f32, minZ: f32, leftChild: u32,
|
|
141
|
+
maxX: f32, maxY: f32, maxZ: f32, rightChild: u32,
|
|
142
|
+
}`;
|
|
143
|
+
|
|
144
|
+
export const BLAS_TRIANGLE_STRUCT_WGSL = /* wgsl */ `
|
|
145
|
+
struct BLASTriangle {
|
|
146
|
+
v0: vec3<f32>, _pad0: u32,
|
|
147
|
+
e1: vec3<f32>, _pad1: u32,
|
|
148
|
+
e2: vec3<f32>, _pad2: u32,
|
|
149
|
+
n0_enc: u32, n1_enc: u32, n2_enc: u32, _pad3: u32,
|
|
150
|
+
}`;
|
|
151
|
+
|
|
152
|
+
export const RAY_STRUCT_WGSL = /* wgsl */ `
|
|
153
|
+
struct Ray {
|
|
154
|
+
origin: vec3<f32>,
|
|
155
|
+
direction: vec3<f32>,
|
|
156
|
+
}`;
|
|
157
|
+
|
|
158
|
+
export const HIT_RESULT_STRUCT_WGSL = /* wgsl */ `
|
|
159
|
+
struct HitResult {
|
|
160
|
+
hit: bool,
|
|
161
|
+
t: f32,
|
|
162
|
+
entityId: u32,
|
|
163
|
+
u: f32,
|
|
164
|
+
v: f32,
|
|
165
|
+
normal: vec3<f32>,
|
|
166
|
+
worldPos: vec3<f32>,
|
|
167
|
+
}`;
|