three-gpu-pathtracer 0.0.26 → 0.0.28
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/README.md +4 -2
- package/build/index.module.js +23 -5
- package/build/index.module.js.map +1 -1
- package/build/index.umd.cjs +22 -4
- package/build/index.umd.cjs.map +1 -1
- package/package.json +8 -8
- package/src/core/PathTracingSceneGenerator.js +1 -1
- package/src/core/utils/sceneUpdateUtils.js +2 -1
- package/src/uniforms/EquirectHdrInfoUniform.js +4 -2
- package/src/uniforms/LightsInfoUniformStruct.js +16 -1
- package/src/webgpu/API.md +167 -19
- package/src/webgpu/AtlasTexture.js +32 -12
- package/src/webgpu/EquirectHdrInfoNode.js +97 -46
- package/src/webgpu/LightsInfoNode.js +142 -4
- package/src/webgpu/MegaKernelPathTracer.js +11 -3
- package/src/webgpu/PathTracerBackend.js +174 -21
- package/src/webgpu/TurquinTexture.js +2 -0
- package/src/webgpu/WaveFrontPathTracer.js +169 -71
- package/src/webgpu/WebGPUPathTracer.js +637 -51
- package/src/webgpu/compute/ComputeKernel.js +18 -0
- package/src/webgpu/compute/PathTracerMegaKernel.js +253 -71
- package/src/webgpu/compute/TallySampleCountsKernel.js +10 -10
- package/src/webgpu/compute/UnpackAuxKernel.js +48 -0
- package/src/webgpu/compute/ZeroOutKernel.js +3 -1
- package/src/webgpu/compute/targets.js +55 -0
- package/src/webgpu/compute/wavefront/LogicKernel.js +136 -55
- package/src/webgpu/compute/wavefront/MaterialKernel.js +153 -66
- package/src/webgpu/compute/wavefront/MediumKernel.js +154 -0
- package/src/webgpu/compute/wavefront/PopulatePixelIndicesKernel.js +5 -5
- package/src/webgpu/compute/wavefront/QueueLengthToDispatchKernel.js +5 -5
- package/src/webgpu/compute/wavefront/QueueMediumWalksKernel.js +52 -0
- package/src/webgpu/compute/wavefront/ResetSlotsKernel.js +6 -6
- package/src/webgpu/compute/wavefront/TraceRayKernel.js +11 -9
- package/src/webgpu/compute/wavefront/TraceShadowRayKernel.js +18 -23
- package/src/webgpu/compute/wavefront/structs.js +63 -35
- package/src/webgpu/constants.js +2 -2
- package/src/webgpu/denoise/OIDNDenoiser.js +81 -78
- package/src/webgpu/index.d.ts +79 -6
- package/src/webgpu/materials/GltfCompliantMaterial.js +20 -6
- package/src/webgpu/materials/debug/AuxDebugMaterial.js +54 -0
- package/src/webgpu/nodes/PathtracerBVHComputeData.js +109 -17
- package/src/webgpu/nodes/aux.wgsl.js +61 -0
- package/src/webgpu/nodes/eon.wgsl.js +1 -1
- package/src/webgpu/nodes/lights.wgsl.js +21 -1
- package/src/webgpu/nodes/material.wgsl.js +2 -1
- package/src/webgpu/nodes/medium.wgsl.js +165 -0
- package/src/webgpu/nodes/random.wgsl.js +2 -0
- package/src/webgpu/nodes/sampling.wgsl.js +44 -3
- package/src/webgpu/nodes/structs.wgsl.js +8 -1
- package/src/webgpu/upscale/FSRUpscaler.js +3 -2
|
@@ -4,6 +4,8 @@ import { LogicKernel } from './compute/wavefront/LogicKernel.js';
|
|
|
4
4
|
import { MaterialKernel } from './compute/wavefront/MaterialKernel.js';
|
|
5
5
|
import { TraceRayKernel } from './compute/wavefront/TraceRayKernel.js';
|
|
6
6
|
import { TraceShadowRayKernel } from './compute/wavefront/TraceShadowRayKernel.js';
|
|
7
|
+
import { MediumKernel } from './compute/wavefront/MediumKernel.js';
|
|
8
|
+
import { QueueMediumWalksKernel } from './compute/wavefront/QueueMediumWalksKernel.js';
|
|
7
9
|
import { QueueLengthToDispatchKernel } from './compute/wavefront/QueueLengthToDispatchKernel.js';
|
|
8
10
|
import { ResetSlotsKernel } from './compute/wavefront/ResetSlotsKernel.js';
|
|
9
11
|
import { ZeroOutBufferKernel } from './compute/ZeroOutBufferKernel.js';
|
|
@@ -11,7 +13,11 @@ import { CopyBufferKernel } from './compute/CopyBufferKernel.js';
|
|
|
11
13
|
import { EquirectHdrInfoNode } from './EquirectHdrInfoNode.js';
|
|
12
14
|
import { EquirectBackgroundInfo } from './EquirectBackgroundInfo.js';
|
|
13
15
|
import { LightsInfoNode } from './LightsInfoNode.js';
|
|
14
|
-
import {
|
|
16
|
+
import {
|
|
17
|
+
rayDataStruct, traceQueuedRayStruct, intersectionResultStruct, shadowTraceResultStruct,
|
|
18
|
+
RAY_QUEUE_LENGTH, SHADOW_RAY_QUEUE_LENGTH, QUEUE_COUNTER_COUNT, PIXEL_QUEUE_HEADER,
|
|
19
|
+
MEDIUM_QUEUE_LENGTH, MEDIUM_QUEUE_HEADER,
|
|
20
|
+
} from './compute/wavefront/structs.js';
|
|
15
21
|
import { PathTracerBackend } from './PathTracerBackend.js';
|
|
16
22
|
import { FILTER_GLOSSY_DISABLED } from './nodes/material.wgsl.js';
|
|
17
23
|
import {
|
|
@@ -44,6 +50,10 @@ export class WaveFrontPathTracer extends PathTracerBackend {
|
|
|
44
50
|
|
|
45
51
|
// options
|
|
46
52
|
this.seed = 0;
|
|
53
|
+
|
|
54
|
+
// scatter events a medium walk takes per pass
|
|
55
|
+
this.mediumStepsPerPass = 16;
|
|
56
|
+
|
|
47
57
|
this.envInfo = new EquirectHdrInfoNode();
|
|
48
58
|
this.backgroundInfo = new EquirectBackgroundInfo();
|
|
49
59
|
this.lightsInfo = new LightsInfoNode();
|
|
@@ -56,14 +66,20 @@ export class WaveFrontPathTracer extends PathTracerBackend {
|
|
|
56
66
|
// persistent per-path state, one slot per in-flight path
|
|
57
67
|
this.rayDataStorage = null;
|
|
58
68
|
|
|
59
|
-
// append-only trace queues,
|
|
69
|
+
// append-only trace queues, with their lengths in the counters buffer
|
|
60
70
|
this.rayQueue = null;
|
|
61
71
|
this.shadowRayQueue = null;
|
|
72
|
+
this.queueCounters = new StorageBufferAttribute( new Uint32Array( QUEUE_COUNTER_COUNT ), 1 );
|
|
73
|
+
this.queueCounters.name = 'Queue Counters';
|
|
62
74
|
|
|
63
|
-
// per-queue-slot trace results, indexed by the ray's position in its queue
|
|
75
|
+
// per-queue-slot trace results, indexed by the ray's position in its queue. The ray results also
|
|
76
|
+
// hold the hits of medium walks, filled from the back
|
|
64
77
|
this.rayIntersectionsStorage = null;
|
|
65
78
|
this.shadowRayIntersectionsStorage = null;
|
|
66
79
|
|
|
80
|
+
// the slots walking a medium this pass
|
|
81
|
+
this.mediumQueue = null;
|
|
82
|
+
|
|
67
83
|
// pixel indices waiting for a free path slot, sized to the resolution
|
|
68
84
|
this.pixelQueue = null;
|
|
69
85
|
|
|
@@ -78,8 +94,11 @@ export class WaveFrontPathTracer extends PathTracerBackend {
|
|
|
78
94
|
this.materialKernel = new MaterialKernel().setWorkgroupSize( 64, 1, 1 );
|
|
79
95
|
this.traceRayKernel = new TraceRayKernel().setWorkgroupSize( 64, 1, 1 );
|
|
80
96
|
this.traceShadowRayKernel = new TraceShadowRayKernel().setWorkgroupSize( 64, 1, 1 );
|
|
81
|
-
this.
|
|
82
|
-
this.
|
|
97
|
+
this.mediumKernel = new MediumKernel().setWorkgroupSize( 64, 1, 1 );
|
|
98
|
+
this.queueMediumWalksKernel = new QueueMediumWalksKernel().setWorkgroupSize( 64, 1, 1 );
|
|
99
|
+
this.rayDispatchConverter = new QueueLengthToDispatchKernel( RAY_QUEUE_LENGTH ).setWorkgroupSize( 1, 1, 1 );
|
|
100
|
+
this.shadowDispatchConverter = new QueueLengthToDispatchKernel( SHADOW_RAY_QUEUE_LENGTH ).setWorkgroupSize( 1, 1, 1 );
|
|
101
|
+
this.mediumDispatchConverter = new QueueLengthToDispatchKernel( MEDIUM_QUEUE_LENGTH ).setWorkgroupSize( 1, 1, 1 );
|
|
83
102
|
this.primeSampleCountersKernel = new PrimeSampleCountersKernel().setWorkgroupSize( 1, 1, 1 );
|
|
84
103
|
this.tallySampleCountsKernel = new TallySampleCountsKernel().setWorkgroupSize( 8, 8, 1 );
|
|
85
104
|
|
|
@@ -95,9 +114,6 @@ export class WaveFrontPathTracer extends PathTracerBackend {
|
|
|
95
114
|
// clear kernels
|
|
96
115
|
this.zeroDispatchKernel = new ZeroOutBufferKernel().setWorkgroupSize( 1, 1, 1 );
|
|
97
116
|
|
|
98
|
-
// later
|
|
99
|
-
this.volumeKernel = null;
|
|
100
|
-
|
|
101
117
|
}
|
|
102
118
|
|
|
103
119
|
resetSeed() {
|
|
@@ -115,15 +131,18 @@ export class WaveFrontPathTracer extends PathTracerBackend {
|
|
|
115
131
|
|
|
116
132
|
setBVHData( bvhData ) {
|
|
117
133
|
|
|
118
|
-
this.materialKernel.bvhData = bvhData;
|
|
134
|
+
this.materialKernel.context.bvhData = bvhData;
|
|
119
135
|
this.materialKernel.needsUpdate = true;
|
|
120
136
|
|
|
121
|
-
this.traceRayKernel.bvhData = bvhData;
|
|
137
|
+
this.traceRayKernel.context.bvhData = bvhData;
|
|
122
138
|
this.traceRayKernel.needsUpdate = true;
|
|
123
139
|
|
|
124
|
-
this.traceShadowRayKernel.bvhData = bvhData;
|
|
140
|
+
this.traceShadowRayKernel.context.bvhData = bvhData;
|
|
125
141
|
this.traceShadowRayKernel.needsUpdate = true;
|
|
126
142
|
|
|
143
|
+
this.mediumKernel.context.bvhData = bvhData;
|
|
144
|
+
this.mediumKernel.needsUpdate = true;
|
|
145
|
+
|
|
127
146
|
this.reset();
|
|
128
147
|
|
|
129
148
|
}
|
|
@@ -152,6 +171,9 @@ export class WaveFrontPathTracer extends PathTracerBackend {
|
|
|
152
171
|
this.traceShadowRayKernel.context.random = random;
|
|
153
172
|
this.traceShadowRayKernel.needsUpdate = true;
|
|
154
173
|
|
|
174
|
+
this.mediumKernel.context.random = random;
|
|
175
|
+
this.mediumKernel.needsUpdate = true;
|
|
176
|
+
|
|
155
177
|
this.reset();
|
|
156
178
|
|
|
157
179
|
}
|
|
@@ -248,14 +270,33 @@ export class WaveFrontPathTracer extends PathTracerBackend {
|
|
|
248
270
|
this.rayDataStorage?.dispose();
|
|
249
271
|
this.rayQueue?.dispose();
|
|
250
272
|
this.shadowRayQueue?.dispose();
|
|
273
|
+
this.queueCounters.dispose();
|
|
251
274
|
this.rayIntersectionsStorage?.dispose();
|
|
252
275
|
this.shadowRayIntersectionsStorage?.dispose();
|
|
276
|
+
this.mediumQueue?.dispose();
|
|
253
277
|
this.sampleCountersStorage.dispose();
|
|
254
278
|
this.pixelQueue?.dispose();
|
|
255
279
|
|
|
256
280
|
// TODO: the dispatch converters own their indirect dispatch buffers, they should dispose of them themselves
|
|
257
281
|
this.rayDispatchConverter.outputDispatch.dispose();
|
|
258
282
|
this.shadowDispatchConverter.outputDispatch.dispose();
|
|
283
|
+
this.mediumDispatchConverter.outputDispatch.dispose();
|
|
284
|
+
|
|
285
|
+
this.populatePixelIndicesKernel.dispose();
|
|
286
|
+
this.logicKernel.dispose();
|
|
287
|
+
this.materialKernel.dispose();
|
|
288
|
+
this.traceRayKernel.dispose();
|
|
289
|
+
this.traceShadowRayKernel.dispose();
|
|
290
|
+
this.mediumKernel.dispose();
|
|
291
|
+
this.queueMediumWalksKernel.dispose();
|
|
292
|
+
this.rayDispatchConverter.dispose();
|
|
293
|
+
this.shadowDispatchConverter.dispose();
|
|
294
|
+
this.mediumDispatchConverter.dispose();
|
|
295
|
+
this.primeSampleCountersKernel.dispose();
|
|
296
|
+
this.tallySampleCountsKernel.dispose();
|
|
297
|
+
this.resetSlotsKernel.dispose();
|
|
298
|
+
this.copyRayDataKernel.dispose();
|
|
299
|
+
this.zeroDispatchKernel.dispose();
|
|
259
300
|
|
|
260
301
|
}
|
|
261
302
|
|
|
@@ -263,67 +304,73 @@ export class WaveFrontPathTracer extends PathTracerBackend {
|
|
|
263
304
|
// retired by a shrink can always return theirs.
|
|
264
305
|
_updatePixelQueue( width, height ) {
|
|
265
306
|
|
|
266
|
-
const size =
|
|
307
|
+
const size = PIXEL_QUEUE_HEADER + Math.max( width * height, 1 );
|
|
267
308
|
if ( ! this.pixelQueue || this.pixelQueue.array.length < size ) {
|
|
268
309
|
|
|
269
310
|
this.pixelQueue?.dispose();
|
|
270
311
|
this.pixelQueue = new StorageBufferAttribute( new Float32Array( size ), size );
|
|
271
312
|
this.pixelQueue.name = 'Pixel Queue';
|
|
272
313
|
|
|
273
|
-
//
|
|
314
|
+
// three rebinds a swapped buffer, so the kernels keep their compiled pipelines
|
|
274
315
|
this.materialKernel.pixelQueue = this.pixelQueue;
|
|
275
|
-
this.materialKernel.needsUpdate = true;
|
|
276
316
|
this.populatePixelIndicesKernel.pixelQueue = this.pixelQueue;
|
|
277
|
-
this.populatePixelIndicesKernel.needsUpdate = true;
|
|
278
317
|
this.resetSlotsKernel.pixelQueue = this.pixelQueue;
|
|
279
|
-
this.resetSlotsKernel.needsUpdate = true;
|
|
280
318
|
|
|
281
319
|
}
|
|
282
320
|
|
|
283
321
|
}
|
|
284
322
|
|
|
285
323
|
// Reallocates the per slot buffers to "count" slots. Slots that survive carry their ray data
|
|
286
|
-
// over, dropped slots return their pixel to the queue, and added slots take one from it.
|
|
287
|
-
//
|
|
288
|
-
|
|
324
|
+
// over, dropped slots return their pixel to the queue, and added slots take one from it. Only
|
|
325
|
+
// the slots in use, up to the pixel count, hold pixels. The trace queues and results only live
|
|
326
|
+
// within a frame, so they start empty.
|
|
327
|
+
_resizePool( count, pixelCount ) {
|
|
289
328
|
|
|
290
|
-
const {
|
|
329
|
+
const { resetSlotsKernel, copyRayDataKernel } = this;
|
|
291
330
|
const previousCount = this.rayCount;
|
|
331
|
+
const previousActive = Math.min( previousCount, pixelCount );
|
|
332
|
+
const active = Math.min( count, pixelCount );
|
|
292
333
|
|
|
293
334
|
resetSlotsKernel.pixelQueue = this.pixelQueue;
|
|
294
|
-
if (
|
|
335
|
+
if ( active < previousActive ) {
|
|
295
336
|
|
|
296
337
|
resetSlotsKernel.rayDataStorage = this.rayDataStorage;
|
|
297
|
-
resetSlotsKernel.start =
|
|
298
|
-
resetSlotsKernel.end =
|
|
338
|
+
resetSlotsKernel.start = active;
|
|
339
|
+
resetSlotsKernel.end = previousActive;
|
|
299
340
|
resetSlotsKernel.addingSlots = false;
|
|
300
|
-
|
|
341
|
+
this.dispatch( resetSlotsKernel, resetSlotsKernel.getDispatchSize( previousActive - active, 1, 1 ) );
|
|
301
342
|
|
|
302
343
|
}
|
|
303
344
|
|
|
304
345
|
const rayDataStorage = new StorageBufferAttribute( count, rayDataStruct.getLength() );
|
|
305
346
|
rayDataStorage.name = 'Ray Data';
|
|
306
347
|
|
|
307
|
-
const
|
|
308
|
-
const rayQueue = new StorageBufferAttribute( new Float32Array( queueSize ), queueSize );
|
|
348
|
+
const rayQueue = new StorageBufferAttribute( count, traceQueuedRayStruct.getLength() );
|
|
309
349
|
rayQueue.name = 'Ray Queue';
|
|
310
350
|
|
|
311
|
-
const shadowRayQueue = new StorageBufferAttribute(
|
|
351
|
+
const shadowRayQueue = new StorageBufferAttribute( count, traceQueuedRayStruct.getLength() );
|
|
312
352
|
shadowRayQueue.name = 'Shadow Ray Queue';
|
|
313
353
|
|
|
354
|
+
// Traced rays fill the results from the front at their queue index, and MediumKernel fills them from the
|
|
355
|
+
// back with the hits of medium walks. In a round each path slot either queues one ray or walks a medium,
|
|
356
|
+
// never both, so the two ranges together never exceed the slot count and can't overlap
|
|
314
357
|
const rayIntersectionsStorage = new StorageBufferAttribute( count, intersectionResultStruct.getLength() );
|
|
315
358
|
rayIntersectionsStorage.name = 'Ray Intersections';
|
|
316
359
|
|
|
317
|
-
const shadowRayIntersectionsStorage = new StorageBufferAttribute( count,
|
|
360
|
+
const shadowRayIntersectionsStorage = new StorageBufferAttribute( count, shadowTraceResultStruct.getLength() );
|
|
318
361
|
shadowRayIntersectionsStorage.name = 'Shadow Ray Intersections';
|
|
319
362
|
|
|
363
|
+
const mediumQueueSize = MEDIUM_QUEUE_HEADER + count;
|
|
364
|
+
const mediumQueue = new StorageBufferAttribute( new Uint32Array( mediumQueueSize ), mediumQueueSize );
|
|
365
|
+
mediumQueue.name = 'Medium Queue';
|
|
366
|
+
|
|
320
367
|
const copyCount = Math.min( count, previousCount );
|
|
321
368
|
if ( copyCount > 0 ) {
|
|
322
369
|
|
|
323
370
|
copyRayDataKernel.source = this.rayDataStorage;
|
|
324
371
|
copyRayDataKernel.target = rayDataStorage;
|
|
325
372
|
copyRayDataKernel.count = copyCount;
|
|
326
|
-
|
|
373
|
+
this.dispatch( copyRayDataKernel, copyRayDataKernel.getDispatchSize( copyCount, 1, 1 ) );
|
|
327
374
|
|
|
328
375
|
}
|
|
329
376
|
|
|
@@ -332,48 +379,56 @@ export class WaveFrontPathTracer extends PathTracerBackend {
|
|
|
332
379
|
this.shadowRayQueue?.dispose();
|
|
333
380
|
this.rayIntersectionsStorage?.dispose();
|
|
334
381
|
this.shadowRayIntersectionsStorage?.dispose();
|
|
382
|
+
this.mediumQueue?.dispose();
|
|
335
383
|
|
|
336
384
|
this.rayDataStorage = rayDataStorage;
|
|
337
385
|
this.rayQueue = rayQueue;
|
|
338
386
|
this.shadowRayQueue = shadowRayQueue;
|
|
339
387
|
this.rayIntersectionsStorage = rayIntersectionsStorage;
|
|
340
388
|
this.shadowRayIntersectionsStorage = shadowRayIntersectionsStorage;
|
|
389
|
+
this.mediumQueue = mediumQueue;
|
|
341
390
|
this.rayCount = count;
|
|
342
391
|
|
|
343
|
-
|
|
344
|
-
this.logicKernel.needsUpdate = true;
|
|
345
|
-
this.materialKernel.needsUpdate = true;
|
|
346
|
-
this.traceRayKernel.needsUpdate = true;
|
|
347
|
-
this.traceShadowRayKernel.needsUpdate = true;
|
|
348
|
-
this.populatePixelIndicesKernel.needsUpdate = true;
|
|
349
|
-
this.resetSlotsKernel.needsUpdate = true;
|
|
350
|
-
|
|
351
|
-
if ( count > previousCount ) {
|
|
392
|
+
if ( active > previousActive ) {
|
|
352
393
|
|
|
353
394
|
resetSlotsKernel.rayDataStorage = rayDataStorage;
|
|
354
|
-
resetSlotsKernel.start =
|
|
355
|
-
resetSlotsKernel.end =
|
|
395
|
+
resetSlotsKernel.start = previousActive;
|
|
396
|
+
resetSlotsKernel.end = active;
|
|
356
397
|
resetSlotsKernel.addingSlots = true;
|
|
357
|
-
|
|
398
|
+
this.dispatch( resetSlotsKernel, resetSlotsKernel.getDispatchSize( active - previousActive, 1, 1 ) );
|
|
358
399
|
|
|
359
400
|
}
|
|
360
401
|
|
|
361
402
|
}
|
|
362
403
|
|
|
363
|
-
// The slot count the current budget asks for, capped by the pool limit
|
|
364
|
-
_getRequestedSlotCount(
|
|
404
|
+
// The slot count the current budget asks for, capped by the pool limit
|
|
405
|
+
_getRequestedSlotCount() {
|
|
406
|
+
|
|
407
|
+
return Math.min( MAX_RAY_DATA_COUNT, Math.max( 1, Math.floor( this.frameBudget ) ) );
|
|
408
|
+
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
// a preview runs at least this many iterations so its samples finish within the frame
|
|
412
|
+
_getPreviewIterations() {
|
|
413
|
+
|
|
414
|
+
return Math.max( 1, Math.min( this.maxBounces, LOW_RES_ITERATIONS ) );
|
|
415
|
+
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
// a preview steps every path several times, so it covers that many fewer pixels than the budget
|
|
419
|
+
getPreviewPixelBudget() {
|
|
365
420
|
|
|
366
|
-
return Math.min(
|
|
421
|
+
return Math.min( this.frameBudget / this._getPreviewIterations(), MAX_RAY_DATA_COUNT );
|
|
367
422
|
|
|
368
423
|
}
|
|
369
424
|
|
|
370
425
|
// Resizes the pool when the budget changes. Paths in flight past a smaller budget are dropped.
|
|
371
426
|
_applyBudget( pixelCount ) {
|
|
372
427
|
|
|
373
|
-
const requested = this._getRequestedSlotCount(
|
|
428
|
+
const requested = this._getRequestedSlotCount();
|
|
374
429
|
if ( requested !== this.rayCount ) {
|
|
375
430
|
|
|
376
|
-
this._resizePool( requested );
|
|
431
|
+
this._resizePool( requested, pixelCount );
|
|
377
432
|
|
|
378
433
|
}
|
|
379
434
|
|
|
@@ -389,8 +444,11 @@ export class WaveFrontPathTracer extends PathTracerBackend {
|
|
|
389
444
|
materialKernel,
|
|
390
445
|
traceRayKernel,
|
|
391
446
|
traceShadowRayKernel,
|
|
447
|
+
mediumKernel,
|
|
448
|
+
queueMediumWalksKernel,
|
|
392
449
|
rayDispatchConverter,
|
|
393
450
|
shadowDispatchConverter,
|
|
451
|
+
mediumDispatchConverter,
|
|
394
452
|
zeroDispatchKernel,
|
|
395
453
|
populatePixelIndicesKernel,
|
|
396
454
|
} = this;
|
|
@@ -407,19 +465,31 @@ export class WaveFrontPathTracer extends PathTracerBackend {
|
|
|
407
465
|
const pixelCount = targetDimensions.x * targetDimensions.y;
|
|
408
466
|
this._applyBudget( pixelCount );
|
|
409
467
|
|
|
410
|
-
//
|
|
411
|
-
|
|
412
|
-
|
|
468
|
+
// compile the kernels before their first dispatch
|
|
469
|
+
yield* this.compileKernels( [
|
|
470
|
+
logicKernel,
|
|
471
|
+
materialKernel,
|
|
472
|
+
traceRayKernel,
|
|
473
|
+
traceShadowRayKernel,
|
|
474
|
+
mediumKernel,
|
|
475
|
+
queueMediumWalksKernel,
|
|
476
|
+
rayDispatchConverter,
|
|
477
|
+
shadowDispatchConverter,
|
|
478
|
+
mediumDispatchConverter,
|
|
479
|
+
zeroDispatchKernel,
|
|
480
|
+
populatePixelIndicesKernel,
|
|
481
|
+
] );
|
|
413
482
|
|
|
414
|
-
|
|
415
|
-
|
|
483
|
+
// reset the trace queues — only their lengths need zeroing
|
|
484
|
+
zeroDispatchKernel.target = this.queueCounters;
|
|
485
|
+
this.dispatch( zeroDispatchKernel, [ QUEUE_COUNTER_COUNT ] );
|
|
416
486
|
|
|
417
|
-
// assign every path slot a pixel and park the overflow pixels in the pixel queue
|
|
487
|
+
// assign every path slot in use a pixel and park the overflow pixels in the pixel queue
|
|
418
488
|
populatePixelIndicesKernel.rayDataStorage = this.rayDataStorage;
|
|
419
489
|
populatePixelIndicesKernel.pixelQueue = this.pixelQueue;
|
|
420
|
-
populatePixelIndicesKernel.frameBudget = this.rayCount;
|
|
490
|
+
populatePixelIndicesKernel.frameBudget = Math.min( this.rayCount, pixelCount );
|
|
421
491
|
populatePixelIndicesKernel.targetDimensions.copy( targetDimensions );
|
|
422
|
-
|
|
492
|
+
this.dispatch( populatePixelIndicesKernel, populatePixelIndicesKernel.getDispatchSize( targetDimensions.x, targetDimensions.y, 1 ) );
|
|
423
493
|
|
|
424
494
|
// advance the sequence once per task so each render pass starts from fresh
|
|
425
495
|
// noise unless "stableNoise" has reset it
|
|
@@ -427,9 +497,10 @@ export class WaveFrontPathTracer extends PathTracerBackend {
|
|
|
427
497
|
|
|
428
498
|
while ( true ) {
|
|
429
499
|
|
|
430
|
-
//
|
|
431
|
-
|
|
432
|
-
const
|
|
500
|
+
// the iterations that spend the budget over the slots in use
|
|
501
|
+
const activeSlots = Math.min( this._getRequestedSlotCount(), pixelCount );
|
|
502
|
+
const minIterations = this.lowResMode ? this._getPreviewIterations() : 1;
|
|
503
|
+
const iter = Math.max( minIterations, Math.round( this.frameBudget / activeSlots ) );
|
|
433
504
|
|
|
434
505
|
for ( let i = 0; i < iter; i ++ ) {
|
|
435
506
|
|
|
@@ -442,6 +513,8 @@ export class WaveFrontPathTracer extends PathTracerBackend {
|
|
|
442
513
|
logicKernel.prevOutputTarget = this.prevOutputTarget;
|
|
443
514
|
logicKernel.outputTarget = this.outputTarget;
|
|
444
515
|
logicKernel.sampleCountTarget = this.sampleCountTarget;
|
|
516
|
+
logicKernel.auxTarget = this.auxTarget ?? this._auxPlaceholder;
|
|
517
|
+
logicKernel.accumulateAux = this.auxTarget ? 1 : 0;
|
|
445
518
|
|
|
446
519
|
// Step 1: resolve last frame's trace results — accumulate NEE / emission / env, terminate
|
|
447
520
|
// finished paths into the output, and pick the next NEE light for each live path
|
|
@@ -449,8 +522,8 @@ export class WaveFrontPathTracer extends PathTracerBackend {
|
|
|
449
522
|
logicKernel.rayIntersectionsStorage = this.rayIntersectionsStorage;
|
|
450
523
|
logicKernel.shadowRayIntersectionsStorage = this.shadowRayIntersectionsStorage;
|
|
451
524
|
logicKernel.maxBounces = this.maxBounces;
|
|
452
|
-
logicKernel.rayCount = this.rayCount;
|
|
453
|
-
|
|
525
|
+
logicKernel.rayCount = Math.min( this.rayCount, pixelCount );
|
|
526
|
+
this.dispatch( logicKernel, logicKernel.getDispatchSize( logicKernel.rayCount, 1, 1 ) );
|
|
454
527
|
|
|
455
528
|
// the trace results are consumed, so only the slot state has to survive a resize
|
|
456
529
|
this._applyBudget( pixelCount );
|
|
@@ -459,23 +532,28 @@ export class WaveFrontPathTracer extends PathTracerBackend {
|
|
|
459
532
|
rayDataStorage,
|
|
460
533
|
rayQueue,
|
|
461
534
|
shadowRayQueue,
|
|
535
|
+
queueCounters,
|
|
462
536
|
rayIntersectionsStorage,
|
|
463
537
|
shadowRayIntersectionsStorage,
|
|
464
|
-
|
|
538
|
+
mediumQueue,
|
|
465
539
|
} = this;
|
|
466
540
|
|
|
541
|
+
// slots past the pixel count never run
|
|
542
|
+
const rayCount = Math.min( this.rayCount, pixelCount );
|
|
543
|
+
|
|
467
544
|
// Step 2: reset the trace queues for this frame's population
|
|
468
|
-
zeroDispatchKernel.target =
|
|
469
|
-
|
|
545
|
+
zeroDispatchKernel.target = queueCounters;
|
|
546
|
+
this.dispatch( zeroDispatchKernel, [ QUEUE_COUNTER_COUNT ] );
|
|
470
547
|
|
|
471
|
-
zeroDispatchKernel.target =
|
|
472
|
-
|
|
548
|
+
zeroDispatchKernel.target = mediumQueue;
|
|
549
|
+
this.dispatch( zeroDispatchKernel, [ MEDIUM_QUEUE_HEADER ] );
|
|
473
550
|
|
|
474
551
|
// Step 3: evaluate materials — spawn camera rays for freed slots, sample the bsdf, and
|
|
475
552
|
// enqueue this frame's bounce + shadow rays
|
|
476
553
|
materialKernel.rayDataStorage = rayDataStorage;
|
|
477
554
|
materialKernel.rayQueue = rayQueue;
|
|
478
555
|
materialKernel.shadowRayQueue = shadowRayQueue;
|
|
556
|
+
materialKernel.queueCounters = queueCounters;
|
|
479
557
|
materialKernel.pixelQueue = this.pixelQueue;
|
|
480
558
|
materialKernel.sampleCountTarget = this.sampleCountTarget;
|
|
481
559
|
materialKernel.seed = this.seed;
|
|
@@ -484,22 +562,42 @@ export class WaveFrontPathTracer extends PathTracerBackend {
|
|
|
484
562
|
materialKernel.maxTransparentBounces = maxTransparentBounces;
|
|
485
563
|
materialKernel.maxBounces = this.maxBounces;
|
|
486
564
|
materialKernel.targetDimensions.copy( targetDimensions );
|
|
487
|
-
|
|
565
|
+
this.dispatch( materialKernel, materialKernel.getDispatchSize( rayCount, 1, 1 ) );
|
|
488
566
|
|
|
489
567
|
// Step 4: convert the queue lengths into indirect dispatch sizes and trace
|
|
490
|
-
rayDispatchConverter.
|
|
491
|
-
|
|
568
|
+
rayDispatchConverter.queueCounters = queueCounters;
|
|
569
|
+
this.dispatch( rayDispatchConverter, [ 1, 1, 1 ] );
|
|
492
570
|
|
|
493
571
|
traceRayKernel.rayQueue = rayQueue;
|
|
572
|
+
traceRayKernel.queueCounters = queueCounters;
|
|
494
573
|
traceRayKernel.rayIntersectionsStorage = rayIntersectionsStorage;
|
|
495
|
-
|
|
574
|
+
this.dispatch( traceRayKernel, rayDispatchConverter.outputDispatch );
|
|
496
575
|
|
|
497
|
-
shadowDispatchConverter.
|
|
498
|
-
|
|
576
|
+
shadowDispatchConverter.queueCounters = queueCounters;
|
|
577
|
+
this.dispatch( shadowDispatchConverter, [ 1, 1, 1 ] );
|
|
499
578
|
|
|
500
579
|
traceShadowRayKernel.shadowRayQueue = shadowRayQueue;
|
|
580
|
+
traceShadowRayKernel.queueCounters = queueCounters;
|
|
501
581
|
traceShadowRayKernel.shadowRayIntersectionsStorage = shadowRayIntersectionsStorage;
|
|
502
|
-
|
|
582
|
+
this.dispatch( traceShadowRayKernel, shadowDispatchConverter.outputDispatch );
|
|
583
|
+
|
|
584
|
+
// Step 5: walk the paths inside media for a capped number of steps
|
|
585
|
+
queueMediumWalksKernel.rayDataStorage = rayDataStorage;
|
|
586
|
+
queueMediumWalksKernel.mediumQueue = mediumQueue;
|
|
587
|
+
queueMediumWalksKernel.rayCount = rayCount;
|
|
588
|
+
this.dispatch( queueMediumWalksKernel, queueMediumWalksKernel.getDispatchSize( rayCount, 1, 1 ) );
|
|
589
|
+
|
|
590
|
+
// the medium queue's length leads it, where the converter reads it as a counter
|
|
591
|
+
mediumDispatchConverter.queueCounters = mediumQueue;
|
|
592
|
+
this.dispatch( mediumDispatchConverter, [ 1, 1, 1 ] );
|
|
593
|
+
|
|
594
|
+
mediumKernel.rayDataStorage = rayDataStorage;
|
|
595
|
+
mediumKernel.mediumQueue = mediumQueue;
|
|
596
|
+
mediumKernel.rayIntersectionsStorage = rayIntersectionsStorage;
|
|
597
|
+
mediumKernel.rayCount = rayCount;
|
|
598
|
+
mediumKernel.maxSubsurfaceBounces = this.maxSubsurfaceBounces;
|
|
599
|
+
mediumKernel.stepsPerPass = this.mediumStepsPerPass;
|
|
600
|
+
this.dispatch( mediumKernel, mediumDispatchConverter.outputDispatch );
|
|
503
601
|
|
|
504
602
|
}
|
|
505
603
|
|