three-gpu-pathtracer 0.0.27 → 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 +0 -1
- package/build/index.module.js +5 -3
- package/build/index.module.js.map +1 -1
- package/build/index.umd.cjs +4 -2
- package/build/index.umd.cjs.map +1 -1
- package/package.json +7 -7
- package/src/core/PathTracingSceneGenerator.js +1 -1
- package/src/uniforms/EquirectHdrInfoUniform.js +4 -2
- package/src/webgpu/API.md +167 -19
- package/src/webgpu/AtlasTexture.js +2 -3
- package/src/webgpu/EquirectHdrInfoNode.js +89 -48
- package/src/webgpu/LightsInfoNode.js +1 -1
- package/src/webgpu/MegaKernelPathTracer.js +10 -3
- package/src/webgpu/PathTracerBackend.js +172 -21
- package/src/webgpu/WaveFrontPathTracer.js +156 -71
- package/src/webgpu/WebGPUPathTracer.js +628 -48
- package/src/webgpu/compute/ComputeKernel.js +8 -0
- package/src/webgpu/compute/PathTracerMegaKernel.js +187 -36
- 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 +67 -20
- 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 -25
- package/src/webgpu/compute/wavefront/structs.js +63 -37
- package/src/webgpu/constants.js +2 -2
- package/src/webgpu/denoise/OIDNDenoiser.js +81 -78
- package/src/webgpu/index.d.ts +41 -6
- package/src/webgpu/materials/GltfCompliantMaterial.js +20 -6
- package/src/webgpu/materials/debug/AuxDebugMaterial.js +54 -0
- package/src/webgpu/nodes/PathtracerBVHComputeData.js +35 -13
- package/src/webgpu/nodes/aux.wgsl.js +61 -0
- package/src/webgpu/nodes/eon.wgsl.js +1 -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 +7 -1
- package/src/webgpu/upscale/FSRUpscaler.js +1 -1
|
@@ -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,22 +270,28 @@ 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();
|
|
259
284
|
|
|
260
285
|
this.populatePixelIndicesKernel.dispose();
|
|
261
286
|
this.logicKernel.dispose();
|
|
262
287
|
this.materialKernel.dispose();
|
|
263
288
|
this.traceRayKernel.dispose();
|
|
264
289
|
this.traceShadowRayKernel.dispose();
|
|
290
|
+
this.mediumKernel.dispose();
|
|
291
|
+
this.queueMediumWalksKernel.dispose();
|
|
265
292
|
this.rayDispatchConverter.dispose();
|
|
266
293
|
this.shadowDispatchConverter.dispose();
|
|
294
|
+
this.mediumDispatchConverter.dispose();
|
|
267
295
|
this.primeSampleCountersKernel.dispose();
|
|
268
296
|
this.tallySampleCountsKernel.dispose();
|
|
269
297
|
this.resetSlotsKernel.dispose();
|
|
@@ -276,67 +304,73 @@ export class WaveFrontPathTracer extends PathTracerBackend {
|
|
|
276
304
|
// retired by a shrink can always return theirs.
|
|
277
305
|
_updatePixelQueue( width, height ) {
|
|
278
306
|
|
|
279
|
-
const size =
|
|
307
|
+
const size = PIXEL_QUEUE_HEADER + Math.max( width * height, 1 );
|
|
280
308
|
if ( ! this.pixelQueue || this.pixelQueue.array.length < size ) {
|
|
281
309
|
|
|
282
310
|
this.pixelQueue?.dispose();
|
|
283
311
|
this.pixelQueue = new StorageBufferAttribute( new Float32Array( size ), size );
|
|
284
312
|
this.pixelQueue.name = 'Pixel Queue';
|
|
285
313
|
|
|
286
|
-
//
|
|
314
|
+
// three rebinds a swapped buffer, so the kernels keep their compiled pipelines
|
|
287
315
|
this.materialKernel.pixelQueue = this.pixelQueue;
|
|
288
|
-
this.materialKernel.needsUpdate = true;
|
|
289
316
|
this.populatePixelIndicesKernel.pixelQueue = this.pixelQueue;
|
|
290
|
-
this.populatePixelIndicesKernel.needsUpdate = true;
|
|
291
317
|
this.resetSlotsKernel.pixelQueue = this.pixelQueue;
|
|
292
|
-
this.resetSlotsKernel.needsUpdate = true;
|
|
293
318
|
|
|
294
319
|
}
|
|
295
320
|
|
|
296
321
|
}
|
|
297
322
|
|
|
298
323
|
// Reallocates the per slot buffers to "count" slots. Slots that survive carry their ray data
|
|
299
|
-
// over, dropped slots return their pixel to the queue, and added slots take one from it.
|
|
300
|
-
//
|
|
301
|
-
|
|
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 ) {
|
|
302
328
|
|
|
303
|
-
const {
|
|
329
|
+
const { resetSlotsKernel, copyRayDataKernel } = this;
|
|
304
330
|
const previousCount = this.rayCount;
|
|
331
|
+
const previousActive = Math.min( previousCount, pixelCount );
|
|
332
|
+
const active = Math.min( count, pixelCount );
|
|
305
333
|
|
|
306
334
|
resetSlotsKernel.pixelQueue = this.pixelQueue;
|
|
307
|
-
if (
|
|
335
|
+
if ( active < previousActive ) {
|
|
308
336
|
|
|
309
337
|
resetSlotsKernel.rayDataStorage = this.rayDataStorage;
|
|
310
|
-
resetSlotsKernel.start =
|
|
311
|
-
resetSlotsKernel.end =
|
|
338
|
+
resetSlotsKernel.start = active;
|
|
339
|
+
resetSlotsKernel.end = previousActive;
|
|
312
340
|
resetSlotsKernel.addingSlots = false;
|
|
313
|
-
|
|
341
|
+
this.dispatch( resetSlotsKernel, resetSlotsKernel.getDispatchSize( previousActive - active, 1, 1 ) );
|
|
314
342
|
|
|
315
343
|
}
|
|
316
344
|
|
|
317
345
|
const rayDataStorage = new StorageBufferAttribute( count, rayDataStruct.getLength() );
|
|
318
346
|
rayDataStorage.name = 'Ray Data';
|
|
319
347
|
|
|
320
|
-
const
|
|
321
|
-
const rayQueue = new StorageBufferAttribute( new Float32Array( queueSize ), queueSize );
|
|
348
|
+
const rayQueue = new StorageBufferAttribute( count, traceQueuedRayStruct.getLength() );
|
|
322
349
|
rayQueue.name = 'Ray Queue';
|
|
323
350
|
|
|
324
|
-
const shadowRayQueue = new StorageBufferAttribute(
|
|
351
|
+
const shadowRayQueue = new StorageBufferAttribute( count, traceQueuedRayStruct.getLength() );
|
|
325
352
|
shadowRayQueue.name = 'Shadow Ray Queue';
|
|
326
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
|
|
327
357
|
const rayIntersectionsStorage = new StorageBufferAttribute( count, intersectionResultStruct.getLength() );
|
|
328
358
|
rayIntersectionsStorage.name = 'Ray Intersections';
|
|
329
359
|
|
|
330
|
-
const shadowRayIntersectionsStorage = new StorageBufferAttribute( count,
|
|
360
|
+
const shadowRayIntersectionsStorage = new StorageBufferAttribute( count, shadowTraceResultStruct.getLength() );
|
|
331
361
|
shadowRayIntersectionsStorage.name = 'Shadow Ray Intersections';
|
|
332
362
|
|
|
363
|
+
const mediumQueueSize = MEDIUM_QUEUE_HEADER + count;
|
|
364
|
+
const mediumQueue = new StorageBufferAttribute( new Uint32Array( mediumQueueSize ), mediumQueueSize );
|
|
365
|
+
mediumQueue.name = 'Medium Queue';
|
|
366
|
+
|
|
333
367
|
const copyCount = Math.min( count, previousCount );
|
|
334
368
|
if ( copyCount > 0 ) {
|
|
335
369
|
|
|
336
370
|
copyRayDataKernel.source = this.rayDataStorage;
|
|
337
371
|
copyRayDataKernel.target = rayDataStorage;
|
|
338
372
|
copyRayDataKernel.count = copyCount;
|
|
339
|
-
|
|
373
|
+
this.dispatch( copyRayDataKernel, copyRayDataKernel.getDispatchSize( copyCount, 1, 1 ) );
|
|
340
374
|
|
|
341
375
|
}
|
|
342
376
|
|
|
@@ -345,48 +379,56 @@ export class WaveFrontPathTracer extends PathTracerBackend {
|
|
|
345
379
|
this.shadowRayQueue?.dispose();
|
|
346
380
|
this.rayIntersectionsStorage?.dispose();
|
|
347
381
|
this.shadowRayIntersectionsStorage?.dispose();
|
|
382
|
+
this.mediumQueue?.dispose();
|
|
348
383
|
|
|
349
384
|
this.rayDataStorage = rayDataStorage;
|
|
350
385
|
this.rayQueue = rayQueue;
|
|
351
386
|
this.shadowRayQueue = shadowRayQueue;
|
|
352
387
|
this.rayIntersectionsStorage = rayIntersectionsStorage;
|
|
353
388
|
this.shadowRayIntersectionsStorage = shadowRayIntersectionsStorage;
|
|
389
|
+
this.mediumQueue = mediumQueue;
|
|
354
390
|
this.rayCount = count;
|
|
355
391
|
|
|
356
|
-
|
|
357
|
-
this.logicKernel.needsUpdate = true;
|
|
358
|
-
this.materialKernel.needsUpdate = true;
|
|
359
|
-
this.traceRayKernel.needsUpdate = true;
|
|
360
|
-
this.traceShadowRayKernel.needsUpdate = true;
|
|
361
|
-
this.populatePixelIndicesKernel.needsUpdate = true;
|
|
362
|
-
this.resetSlotsKernel.needsUpdate = true;
|
|
363
|
-
|
|
364
|
-
if ( count > previousCount ) {
|
|
392
|
+
if ( active > previousActive ) {
|
|
365
393
|
|
|
366
394
|
resetSlotsKernel.rayDataStorage = rayDataStorage;
|
|
367
|
-
resetSlotsKernel.start =
|
|
368
|
-
resetSlotsKernel.end =
|
|
395
|
+
resetSlotsKernel.start = previousActive;
|
|
396
|
+
resetSlotsKernel.end = active;
|
|
369
397
|
resetSlotsKernel.addingSlots = true;
|
|
370
|
-
|
|
398
|
+
this.dispatch( resetSlotsKernel, resetSlotsKernel.getDispatchSize( active - previousActive, 1, 1 ) );
|
|
371
399
|
|
|
372
400
|
}
|
|
373
401
|
|
|
374
402
|
}
|
|
375
403
|
|
|
376
|
-
// The slot count the current budget asks for, capped by the pool limit
|
|
377
|
-
_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() {
|
|
378
420
|
|
|
379
|
-
return Math.min(
|
|
421
|
+
return Math.min( this.frameBudget / this._getPreviewIterations(), MAX_RAY_DATA_COUNT );
|
|
380
422
|
|
|
381
423
|
}
|
|
382
424
|
|
|
383
425
|
// Resizes the pool when the budget changes. Paths in flight past a smaller budget are dropped.
|
|
384
426
|
_applyBudget( pixelCount ) {
|
|
385
427
|
|
|
386
|
-
const requested = this._getRequestedSlotCount(
|
|
428
|
+
const requested = this._getRequestedSlotCount();
|
|
387
429
|
if ( requested !== this.rayCount ) {
|
|
388
430
|
|
|
389
|
-
this._resizePool( requested );
|
|
431
|
+
this._resizePool( requested, pixelCount );
|
|
390
432
|
|
|
391
433
|
}
|
|
392
434
|
|
|
@@ -402,8 +444,11 @@ export class WaveFrontPathTracer extends PathTracerBackend {
|
|
|
402
444
|
materialKernel,
|
|
403
445
|
traceRayKernel,
|
|
404
446
|
traceShadowRayKernel,
|
|
447
|
+
mediumKernel,
|
|
448
|
+
queueMediumWalksKernel,
|
|
405
449
|
rayDispatchConverter,
|
|
406
450
|
shadowDispatchConverter,
|
|
451
|
+
mediumDispatchConverter,
|
|
407
452
|
zeroDispatchKernel,
|
|
408
453
|
populatePixelIndicesKernel,
|
|
409
454
|
} = this;
|
|
@@ -420,19 +465,31 @@ export class WaveFrontPathTracer extends PathTracerBackend {
|
|
|
420
465
|
const pixelCount = targetDimensions.x * targetDimensions.y;
|
|
421
466
|
this._applyBudget( pixelCount );
|
|
422
467
|
|
|
423
|
-
//
|
|
424
|
-
|
|
425
|
-
|
|
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
|
+
] );
|
|
426
482
|
|
|
427
|
-
|
|
428
|
-
|
|
483
|
+
// reset the trace queues — only their lengths need zeroing
|
|
484
|
+
zeroDispatchKernel.target = this.queueCounters;
|
|
485
|
+
this.dispatch( zeroDispatchKernel, [ QUEUE_COUNTER_COUNT ] );
|
|
429
486
|
|
|
430
|
-
// 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
|
|
431
488
|
populatePixelIndicesKernel.rayDataStorage = this.rayDataStorage;
|
|
432
489
|
populatePixelIndicesKernel.pixelQueue = this.pixelQueue;
|
|
433
|
-
populatePixelIndicesKernel.frameBudget = this.rayCount;
|
|
490
|
+
populatePixelIndicesKernel.frameBudget = Math.min( this.rayCount, pixelCount );
|
|
434
491
|
populatePixelIndicesKernel.targetDimensions.copy( targetDimensions );
|
|
435
|
-
|
|
492
|
+
this.dispatch( populatePixelIndicesKernel, populatePixelIndicesKernel.getDispatchSize( targetDimensions.x, targetDimensions.y, 1 ) );
|
|
436
493
|
|
|
437
494
|
// advance the sequence once per task so each render pass starts from fresh
|
|
438
495
|
// noise unless "stableNoise" has reset it
|
|
@@ -440,9 +497,10 @@ export class WaveFrontPathTracer extends PathTracerBackend {
|
|
|
440
497
|
|
|
441
498
|
while ( true ) {
|
|
442
499
|
|
|
443
|
-
//
|
|
444
|
-
|
|
445
|
-
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 ) );
|
|
446
504
|
|
|
447
505
|
for ( let i = 0; i < iter; i ++ ) {
|
|
448
506
|
|
|
@@ -455,6 +513,8 @@ export class WaveFrontPathTracer extends PathTracerBackend {
|
|
|
455
513
|
logicKernel.prevOutputTarget = this.prevOutputTarget;
|
|
456
514
|
logicKernel.outputTarget = this.outputTarget;
|
|
457
515
|
logicKernel.sampleCountTarget = this.sampleCountTarget;
|
|
516
|
+
logicKernel.auxTarget = this.auxTarget ?? this._auxPlaceholder;
|
|
517
|
+
logicKernel.accumulateAux = this.auxTarget ? 1 : 0;
|
|
458
518
|
|
|
459
519
|
// Step 1: resolve last frame's trace results — accumulate NEE / emission / env, terminate
|
|
460
520
|
// finished paths into the output, and pick the next NEE light for each live path
|
|
@@ -462,8 +522,8 @@ export class WaveFrontPathTracer extends PathTracerBackend {
|
|
|
462
522
|
logicKernel.rayIntersectionsStorage = this.rayIntersectionsStorage;
|
|
463
523
|
logicKernel.shadowRayIntersectionsStorage = this.shadowRayIntersectionsStorage;
|
|
464
524
|
logicKernel.maxBounces = this.maxBounces;
|
|
465
|
-
logicKernel.rayCount = this.rayCount;
|
|
466
|
-
|
|
525
|
+
logicKernel.rayCount = Math.min( this.rayCount, pixelCount );
|
|
526
|
+
this.dispatch( logicKernel, logicKernel.getDispatchSize( logicKernel.rayCount, 1, 1 ) );
|
|
467
527
|
|
|
468
528
|
// the trace results are consumed, so only the slot state has to survive a resize
|
|
469
529
|
this._applyBudget( pixelCount );
|
|
@@ -472,23 +532,28 @@ export class WaveFrontPathTracer extends PathTracerBackend {
|
|
|
472
532
|
rayDataStorage,
|
|
473
533
|
rayQueue,
|
|
474
534
|
shadowRayQueue,
|
|
535
|
+
queueCounters,
|
|
475
536
|
rayIntersectionsStorage,
|
|
476
537
|
shadowRayIntersectionsStorage,
|
|
477
|
-
|
|
538
|
+
mediumQueue,
|
|
478
539
|
} = this;
|
|
479
540
|
|
|
541
|
+
// slots past the pixel count never run
|
|
542
|
+
const rayCount = Math.min( this.rayCount, pixelCount );
|
|
543
|
+
|
|
480
544
|
// Step 2: reset the trace queues for this frame's population
|
|
481
|
-
zeroDispatchKernel.target =
|
|
482
|
-
|
|
545
|
+
zeroDispatchKernel.target = queueCounters;
|
|
546
|
+
this.dispatch( zeroDispatchKernel, [ QUEUE_COUNTER_COUNT ] );
|
|
483
547
|
|
|
484
|
-
zeroDispatchKernel.target =
|
|
485
|
-
|
|
548
|
+
zeroDispatchKernel.target = mediumQueue;
|
|
549
|
+
this.dispatch( zeroDispatchKernel, [ MEDIUM_QUEUE_HEADER ] );
|
|
486
550
|
|
|
487
551
|
// Step 3: evaluate materials — spawn camera rays for freed slots, sample the bsdf, and
|
|
488
552
|
// enqueue this frame's bounce + shadow rays
|
|
489
553
|
materialKernel.rayDataStorage = rayDataStorage;
|
|
490
554
|
materialKernel.rayQueue = rayQueue;
|
|
491
555
|
materialKernel.shadowRayQueue = shadowRayQueue;
|
|
556
|
+
materialKernel.queueCounters = queueCounters;
|
|
492
557
|
materialKernel.pixelQueue = this.pixelQueue;
|
|
493
558
|
materialKernel.sampleCountTarget = this.sampleCountTarget;
|
|
494
559
|
materialKernel.seed = this.seed;
|
|
@@ -497,22 +562,42 @@ export class WaveFrontPathTracer extends PathTracerBackend {
|
|
|
497
562
|
materialKernel.maxTransparentBounces = maxTransparentBounces;
|
|
498
563
|
materialKernel.maxBounces = this.maxBounces;
|
|
499
564
|
materialKernel.targetDimensions.copy( targetDimensions );
|
|
500
|
-
|
|
565
|
+
this.dispatch( materialKernel, materialKernel.getDispatchSize( rayCount, 1, 1 ) );
|
|
501
566
|
|
|
502
567
|
// Step 4: convert the queue lengths into indirect dispatch sizes and trace
|
|
503
|
-
rayDispatchConverter.
|
|
504
|
-
|
|
568
|
+
rayDispatchConverter.queueCounters = queueCounters;
|
|
569
|
+
this.dispatch( rayDispatchConverter, [ 1, 1, 1 ] );
|
|
505
570
|
|
|
506
571
|
traceRayKernel.rayQueue = rayQueue;
|
|
572
|
+
traceRayKernel.queueCounters = queueCounters;
|
|
507
573
|
traceRayKernel.rayIntersectionsStorage = rayIntersectionsStorage;
|
|
508
|
-
|
|
574
|
+
this.dispatch( traceRayKernel, rayDispatchConverter.outputDispatch );
|
|
509
575
|
|
|
510
|
-
shadowDispatchConverter.
|
|
511
|
-
|
|
576
|
+
shadowDispatchConverter.queueCounters = queueCounters;
|
|
577
|
+
this.dispatch( shadowDispatchConverter, [ 1, 1, 1 ] );
|
|
512
578
|
|
|
513
579
|
traceShadowRayKernel.shadowRayQueue = shadowRayQueue;
|
|
580
|
+
traceShadowRayKernel.queueCounters = queueCounters;
|
|
514
581
|
traceShadowRayKernel.shadowRayIntersectionsStorage = shadowRayIntersectionsStorage;
|
|
515
|
-
|
|
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 );
|
|
516
601
|
|
|
517
602
|
}
|
|
518
603
|
|