@plasius/gpu-renderer 0.2.56 → 0.2.58
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/CHANGELOG.md +34 -0
- package/README.md +57 -0
- package/dist/index.cjs +6972 -6478
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +6972 -6478
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/gpu-allocation-budget.js +92 -0
- package/src/index.d.ts +34 -0
- package/src/wavefront-bind-groups.js +2 -0
- package/src/wavefront-compute.js +40 -10
- package/src/wavefront-config.js +22 -2
- package/src/wavefront-instance-shader.js +88 -0
- package/src/wavefront-instance-texture.js +43 -0
- package/src/wavefront-instances.js +107 -0
- package/src/wavefront-mesh-sources.js +2 -2
- package/src/wavefront-pipelines.js +6 -5
- package/src/wavefront-runtime-support.js +8 -6
- package/src/wavefront-shader-bvh.js +4 -2
- package/src/wavefront-shader-kernels.js +1 -1
- package/src/wavefront-shader-layout.js +1 -0
- package/src/wavefront-shaders.js +5 -1
- package/src/wavefront-upload.js +12 -0
|
@@ -159,7 +159,7 @@ function chooseSplitAxis(triangles) {
|
|
|
159
159
|
return extent[1] >= extent[2] ? 1 : 2;
|
|
160
160
|
}
|
|
161
161
|
|
|
162
|
-
function
|
|
162
|
+
export function buildWavefrontBoundsBvh(triangles, maxLeafTriangles = 4) {
|
|
163
163
|
if (triangles.length === 0) {
|
|
164
164
|
return Object.freeze({ nodes: Object.freeze([]), triangles: Object.freeze([]) });
|
|
165
165
|
}
|
|
@@ -217,7 +217,7 @@ export function createWavefrontMeshAcceleration(meshes = [], gpuMaterialSource =
|
|
|
217
217
|
const source = Array.isArray(meshes) ? meshes : [meshes];
|
|
218
218
|
const resolvedMaterialSource = gpuMaterialSource ?? createWavefrontGpuMaterialSource(source);
|
|
219
219
|
const triangles = createMeshTriangleRecords(source, resolvedMaterialSource);
|
|
220
|
-
return
|
|
220
|
+
return buildWavefrontBoundsBvh(triangles);
|
|
221
221
|
}
|
|
222
222
|
|
|
223
223
|
export function estimateMeshSourceShape(meshes) {
|
|
@@ -3,10 +3,10 @@ import {
|
|
|
3
3
|
createComputePipeline,
|
|
4
4
|
createRenderPipeline,
|
|
5
5
|
} from "./wavefront-runtime-support.js";
|
|
6
|
-
import { PRESENT_WGSL,
|
|
6
|
+
import { PRESENT_WGSL, createWavefrontPathTracingComputeShaderSource } from "./wavefront-shaders.js";
|
|
7
7
|
import { CONFIG_BUFFER_BYTES } from "./wavefront-core.js";
|
|
8
8
|
|
|
9
|
-
export function createWavefrontBindGroupLayouts(device, constants) {
|
|
9
|
+
export function createWavefrontBindGroupLayouts(device, constants, instanced = false) {
|
|
10
10
|
const trace = device.createBindGroupLayout({
|
|
11
11
|
label: "plasius.wavefront.traceBindGroupLayout",
|
|
12
12
|
entries: [
|
|
@@ -59,6 +59,7 @@ export function createWavefrontBindGroupLayouts(device, constants) {
|
|
|
59
59
|
{ binding: 42, visibility: constants.shader.COMPUTE, texture: { sampleType: "float" } },
|
|
60
60
|
{ binding: 43, visibility: constants.shader.COMPUTE, texture: { sampleType: "float" } },
|
|
61
61
|
{ binding: 44, visibility: constants.shader.COMPUTE, texture: { sampleType: "float" } },
|
|
62
|
+
...(instanced ? [{binding:47, visibility:constants.shader.COMPUTE, texture:{sampleType:"uint"}}] : []),
|
|
62
63
|
],
|
|
63
64
|
});
|
|
64
65
|
const acceleration = device.createBindGroupLayout({
|
|
@@ -258,12 +259,12 @@ export async function createWavefrontPresentPipeline(device, presentBindGroupLay
|
|
|
258
259
|
});
|
|
259
260
|
}
|
|
260
261
|
|
|
261
|
-
export async function createWavefrontPipelineResources({ device, constants, format }) {
|
|
262
|
-
const bindGroupLayouts = createWavefrontBindGroupLayouts(device, constants);
|
|
262
|
+
export async function createWavefrontPipelineResources({ device, constants, format, instanced = false }) {
|
|
263
|
+
const bindGroupLayouts = createWavefrontBindGroupLayouts(device, constants, instanced);
|
|
263
264
|
const pipelineLayouts = createWavefrontPipelineLayouts(device, bindGroupLayouts);
|
|
264
265
|
const computeShader = device.createShaderModule({
|
|
265
266
|
label: "plasius.wavefront.computeShader",
|
|
266
|
-
code:
|
|
267
|
+
code: createWavefrontPathTracingComputeShaderSource({instanced}),
|
|
267
268
|
});
|
|
268
269
|
await assertShaderModuleCompiles(computeShader, "plasius.wavefront.computeShader");
|
|
269
270
|
|
|
@@ -161,31 +161,33 @@ export function createWavefrontDeviceDescriptor(adapter, options = {}, memory =
|
|
|
161
161
|
...descriptorRequiredLimits,
|
|
162
162
|
...(options.requiredLimits ?? {}),
|
|
163
163
|
};
|
|
164
|
+
const traceStorageBindings = TRACE_STORAGE_BUFFER_BINDINGS;
|
|
165
|
+
const traceTextureBindings = TRACE_SAMPLED_TEXTURE_BINDINGS + (options.instances !== undefined ? 1 : 0);
|
|
164
166
|
const exposedStorageBufferLimit = Number(adapter?.limits?.maxStorageBuffersPerShaderStage);
|
|
165
167
|
if (Number.isFinite(exposedStorageBufferLimit)) {
|
|
166
|
-
if (exposedStorageBufferLimit <
|
|
168
|
+
if (exposedStorageBufferLimit < traceStorageBindings) {
|
|
167
169
|
throw new Error(
|
|
168
|
-
`Wavefront mesh tracing requires maxStorageBuffersPerShaderStage>=${
|
|
170
|
+
`Wavefront mesh tracing requires maxStorageBuffersPerShaderStage>=${traceStorageBindings}, ` +
|
|
169
171
|
`but this adapter exposes ${exposedStorageBufferLimit}.`
|
|
170
172
|
);
|
|
171
173
|
}
|
|
172
174
|
requiredLimits.maxStorageBuffersPerShaderStage = Math.max(
|
|
173
175
|
Number(requiredLimits.maxStorageBuffersPerShaderStage ?? 0),
|
|
174
|
-
|
|
176
|
+
traceStorageBindings
|
|
175
177
|
);
|
|
176
178
|
}
|
|
177
179
|
|
|
178
180
|
const exposedSampledTextureLimit = Number(adapter?.limits?.maxSampledTexturesPerShaderStage);
|
|
179
181
|
if (Number.isFinite(exposedSampledTextureLimit)) {
|
|
180
|
-
if (exposedSampledTextureLimit <
|
|
182
|
+
if (exposedSampledTextureLimit < traceTextureBindings) {
|
|
181
183
|
throw new Error(
|
|
182
|
-
`Wavefront material tracing requires maxSampledTexturesPerShaderStage>=${
|
|
184
|
+
`Wavefront material tracing requires maxSampledTexturesPerShaderStage>=${traceTextureBindings}, ` +
|
|
183
185
|
`but this adapter exposes ${exposedSampledTextureLimit}.`
|
|
184
186
|
);
|
|
185
187
|
}
|
|
186
188
|
requiredLimits.maxSampledTexturesPerShaderStage = Math.max(
|
|
187
189
|
Number(requiredLimits.maxSampledTexturesPerShaderStage ?? 0),
|
|
188
|
-
|
|
190
|
+
traceTextureBindings
|
|
189
191
|
);
|
|
190
192
|
}
|
|
191
193
|
|
|
@@ -459,6 +459,7 @@ fn no_candidate() -> Candidate {
|
|
|
459
459
|
1u,
|
|
460
460
|
0xffffffffu,
|
|
461
461
|
0xffffffffu,
|
|
462
|
+
0xffffffffu,
|
|
462
463
|
0u,
|
|
463
464
|
0u
|
|
464
465
|
);
|
|
@@ -485,6 +486,7 @@ fn surface_candidate(
|
|
|
485
486
|
uv,
|
|
486
487
|
frontFace,
|
|
487
488
|
triangleIndex,
|
|
489
|
+
0xffffffffu,
|
|
488
490
|
primitiveId,
|
|
489
491
|
materialRefId,
|
|
490
492
|
mediumRefId
|
|
@@ -546,7 +548,7 @@ fn intersect_triangle(ray: RayRecord, triangle: TriangleRecord, triangleIndex: u
|
|
|
546
548
|
);
|
|
547
549
|
}
|
|
548
550
|
|
|
549
|
-
fn
|
|
551
|
+
fn intersect_geometry_bvh(ray: RayRecord, initialNearest: f32, root: u32) -> Candidate {
|
|
550
552
|
var nearest = initialNearest;
|
|
551
553
|
var best = no_candidate();
|
|
552
554
|
if (config.bvhNodeCount == 0u || config.triangleCount == 0u) {
|
|
@@ -555,7 +557,7 @@ fn intersect_bvh(ray: RayRecord, initialNearest: f32) -> Candidate {
|
|
|
555
557
|
|
|
556
558
|
var stack = array<u32, 64>();
|
|
557
559
|
var stackSize = 1u;
|
|
558
|
-
stack[0] =
|
|
560
|
+
stack[0] = root;
|
|
559
561
|
|
|
560
562
|
loop {
|
|
561
563
|
if (stackSize == 0u) {
|
|
@@ -245,7 +245,7 @@ fn intersectActiveQueue(@builtin(global_invocation_id) globalId: vec3<u32>) {
|
|
|
245
245
|
if (meshCandidate.hit == 1u && meshCandidate.distance < nearest) {
|
|
246
246
|
nearest = meshCandidate.distance;
|
|
247
247
|
candidate = meshCandidate;
|
|
248
|
-
hitTriangle =
|
|
248
|
+
hitTriangle = triangle_for_candidate(meshCandidate);
|
|
249
249
|
}
|
|
250
250
|
|
|
251
251
|
if (candidate.hit == 0u) {
|
package/src/wavefront-shaders.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { WAVEFRONT_INSTANCE_SHADER_WGSL, WAVEFRONT_UNINSTANCED_SHADER_WGSL } from './wavefront-instance-shader.js';
|
|
1
2
|
import { WAVEFRONT_COMPUTE_WORKGROUP_SIZE } from "./wavefront-workgroup.js";
|
|
2
3
|
import { WAVEFRONT_SHADER_BVH_WGSL } from "./wavefront-shader-bvh.js";
|
|
3
4
|
import { WAVEFRONT_SHADER_KERNELS_WGSL } from "./wavefront-shader-kernels.js";
|
|
@@ -22,6 +23,7 @@ export const WAVEFRONT_COMPUTE_WGSL = [
|
|
|
22
23
|
WAVEFRONT_SHADER_MATERIALS_WGSL,
|
|
23
24
|
WAVEFRONT_SHADER_LIGHTING_WGSL,
|
|
24
25
|
WAVEFRONT_SHADER_BVH_WGSL,
|
|
26
|
+
WAVEFRONT_UNINSTANCED_SHADER_WGSL,
|
|
25
27
|
WAVEFRONT_SHADER_KERNELS_WGSL,
|
|
26
28
|
].join("\n");
|
|
27
29
|
|
|
@@ -34,5 +36,7 @@ export function createWavefrontPathTracingComputeShaderSource(options = {}) {
|
|
|
34
36
|
if (workgroupSize !== WAVEFRONT_COMPUTE_WORKGROUP_SIZE) {
|
|
35
37
|
throw new Error(`wavefront mesh compute currently requires workgroupSize=${WAVEFRONT_COMPUTE_WORKGROUP_SIZE}`);
|
|
36
38
|
}
|
|
37
|
-
return
|
|
39
|
+
return options.instanced === true
|
|
40
|
+
? WAVEFRONT_COMPUTE_WGSL.replace(WAVEFRONT_UNINSTANCED_SHADER_WGSL, WAVEFRONT_INSTANCE_SHADER_WGSL)
|
|
41
|
+
: WAVEFRONT_COMPUTE_WGSL;
|
|
38
42
|
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { waitForSubmittedGpuWork } from './wavefront-gpu-synchronization.js';
|
|
2
|
+
|
|
3
|
+
// Large writeBuffer calls need temporary driver staging storage in addition to
|
|
4
|
+
// the destination. Complete each bounded chunk before requesting the next one.
|
|
5
|
+
export async function uploadWavefrontBuffer(device, buffer, data, {chunkBytes=8*1024*1024}={}) {
|
|
6
|
+
if(!Number.isSafeInteger(chunkBytes)||chunkBytes<4||chunkBytes%4!==0) throw new Error('GPU upload chunk size must be a positive multiple of four.');
|
|
7
|
+
if(data.byteLength<=chunkBytes){device.queue.writeBuffer(buffer,0,data);return;}
|
|
8
|
+
for(let offset=0;offset<data.byteLength;offset+=chunkBytes){
|
|
9
|
+
device.queue.writeBuffer(buffer,offset,data,offset,Math.min(chunkBytes,data.byteLength-offset));
|
|
10
|
+
await waitForSubmittedGpuWork(device,{timeoutMs:10000,maxWaitMs:10000,allowTimeout:false});
|
|
11
|
+
}
|
|
12
|
+
}
|