@plasius/gpu-renderer 0.1.12 → 0.1.13

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/src/index.d.ts CHANGED
@@ -151,6 +151,19 @@ export type RendererAccelerationStructureUpdateClass =
151
151
  | "rigid-dynamic"
152
152
  | "deforming"
153
153
  | "proxy";
154
+ export type RendererWavefrontHitType =
155
+ | "surface"
156
+ | "emissive"
157
+ | "environment"
158
+ | "transparent"
159
+ | "miss";
160
+ export type RendererWavefrontPassKey =
161
+ | "generatePrimaryRays"
162
+ | "intersectActiveQueue"
163
+ | "resolveSurfaceRecords"
164
+ | "accumulateTerminalRadiance"
165
+ | "scatterContinuations"
166
+ | "compactAndSwapQueues";
154
167
 
155
168
  export interface RendererInputBoundary {
156
169
  readonly type: "stable-visual-snapshot";
@@ -288,9 +301,75 @@ export interface RayTracingRenderPlan {
288
301
  }
289
302
  )[];
290
303
  readonly accelerationStructureUpdates: readonly RendererAccelerationStructureUpdatePolicy[];
304
+ readonly wavefront: RendererWavefrontPathTracingPlan;
291
305
  readonly workerManifest: RendererWorkerManifest;
292
306
  }
293
307
 
308
+ export interface RendererWavefrontFieldContract {
309
+ readonly name: string;
310
+ readonly type: string;
311
+ readonly description: string;
312
+ }
313
+
314
+ export interface RendererWavefrontRecordContract {
315
+ readonly schemaVersion: typeof rendererWavefrontBufferSchemaVersion;
316
+ readonly recordName: string;
317
+ readonly fields: readonly RendererWavefrontFieldContract[];
318
+ }
319
+
320
+ export interface RendererWavefrontQueueDescriptor {
321
+ readonly name: "active" | "next";
322
+ readonly role: "current-bounce" | "next-bounce";
323
+ }
324
+
325
+ export interface RendererWavefrontBounceStep {
326
+ readonly bounce: number;
327
+ readonly readQueue: "active" | "next";
328
+ readonly writeQueue: "active" | "next";
329
+ readonly passOrder: readonly RendererWavefrontPassKey[];
330
+ }
331
+
332
+ export interface RendererWavefrontTerminationPolicy {
333
+ readonly terminalHitTypes: readonly RendererWavefrontHitType[];
334
+ readonly continuationHitTypes: readonly RendererWavefrontHitType[];
335
+ readonly emissive: Readonly<{
336
+ action: "accumulate-and-stop";
337
+ contributesRadiance: true;
338
+ }>;
339
+ readonly environment: Readonly<{
340
+ action: "accumulate-and-stop";
341
+ contributesRadiance: true;
342
+ }>;
343
+ readonly miss: Readonly<{
344
+ action: "accumulate-environment-or-dark-stop";
345
+ contributesRadiance: true;
346
+ }>;
347
+ }
348
+
349
+ export interface RendererWavefrontPathTracingPlan {
350
+ readonly schemaVersion: typeof rendererWavefrontBufferSchemaVersion;
351
+ readonly owner: typeof rendererDebugOwner;
352
+ readonly maxDepth: number;
353
+ readonly queueCapacity: number;
354
+ readonly explicitLightSampling: boolean;
355
+ readonly accumulationResetEpoch: number;
356
+ readonly queueLayout: Readonly<{
357
+ strategy: typeof rendererWavefrontQueuePairStrategy;
358
+ compactAfterScatter: true;
359
+ queues: readonly RendererWavefrontQueueDescriptor[];
360
+ }>;
361
+ readonly bufferContracts: Readonly<{
362
+ ray: RendererWavefrontRecordContract;
363
+ hit: RendererWavefrontRecordContract;
364
+ surface: RendererWavefrontRecordContract;
365
+ materialReference: RendererWavefrontRecordContract;
366
+ mediumReference: RendererWavefrontRecordContract;
367
+ accumulation: RendererWavefrontRecordContract;
368
+ }>;
369
+ readonly bounceSchedule: readonly RendererWavefrontBounceStep[];
370
+ readonly terminationPolicy: RendererWavefrontTerminationPolicy;
371
+ }
372
+
294
373
  export function getRendererWorkerProfile(
295
374
  name?: RendererWorkerProfileName
296
375
  ): RendererWorkerProfile;
@@ -309,11 +388,28 @@ export function createRayTracingRenderPlan(options: {
309
388
  readonly [key: string]: unknown;
310
389
  }
311
390
  )[];
391
+ wavefront?: {
392
+ maxDepth?: number;
393
+ queueCapacity?: number;
394
+ explicitLightSampling?: boolean;
395
+ accumulationResetEpoch?: number;
396
+ };
312
397
  }): RayTracingRenderPlan;
313
398
 
399
+ export function createWavefrontPathTracingPlan(options?: {
400
+ maxDepth?: number;
401
+ queueCapacity?: number;
402
+ explicitLightSampling?: boolean;
403
+ accumulationResetEpoch?: number;
404
+ }): RendererWavefrontPathTracingPlan;
405
+
314
406
  export const rendererRepresentationBands: readonly RendererRepresentationBand[];
315
407
  export const rendererAccelerationStructureUpdateClasses: readonly RendererAccelerationStructureUpdateClass[];
316
408
  export const rendererRayTracingStageOrder: readonly RendererRenderStage["key"][];
409
+ export const rendererWavefrontBufferSchemaVersion: 1;
410
+ export const rendererWavefrontQueuePairStrategy: "ping-pong-active-next";
411
+ export const rendererWavefrontHitTypes: readonly RendererWavefrontHitType[];
412
+ export const rendererWavefrontPassOrder: readonly RendererWavefrontPassKey[];
317
413
 
318
414
  export const defaultRendererClearColor: readonly [number, number, number, number];
319
415
  export const rendererDebugOwner: "renderer";
package/src/index.js CHANGED
@@ -27,6 +27,23 @@ export const rendererRayTracingStageOrder = Object.freeze([
27
27
  "composition",
28
28
  "present",
29
29
  ]);
30
+ export const rendererWavefrontBufferSchemaVersion = 1;
31
+ export const rendererWavefrontQueuePairStrategy = "ping-pong-active-next";
32
+ export const rendererWavefrontHitTypes = Object.freeze([
33
+ "surface",
34
+ "emissive",
35
+ "environment",
36
+ "transparent",
37
+ "miss",
38
+ ]);
39
+ export const rendererWavefrontPassOrder = Object.freeze([
40
+ "generatePrimaryRays",
41
+ "intersectActiveQueue",
42
+ "resolveSurfaceRecords",
43
+ "accumulateTerminalRadiance",
44
+ "scatterContinuations",
45
+ "compactAndSwapQueues",
46
+ ]);
30
47
 
31
48
  const rendererRayTracingStageDefinitions = Object.freeze(
32
49
  rendererRayTracingStageOrder.map((key, index) =>
@@ -104,6 +121,167 @@ const rendererAccelerationStructurePolicies = Object.freeze(
104
121
  )
105
122
  );
106
123
 
124
+ function createWavefrontField(name, type, description) {
125
+ return Object.freeze({
126
+ name,
127
+ type,
128
+ description,
129
+ });
130
+ }
131
+
132
+ const rendererWavefrontBufferContracts = Object.freeze({
133
+ ray: Object.freeze({
134
+ schemaVersion: rendererWavefrontBufferSchemaVersion,
135
+ recordName: "RayRecord",
136
+ fields: Object.freeze([
137
+ createWavefrontField("rayId", "u32", "Stable ray identifier for correlation and debugging."),
138
+ createWavefrontField("parentRayId", "u32", "Parent ray identifier for continuation lineage."),
139
+ createWavefrontField("sourcePixelId", "u32", "Screen pixel or texel that owns the sample."),
140
+ createWavefrontField("sampleId", "u32", "Per-pixel sample slot for accumulation."),
141
+ createWavefrontField("bounce", "u32", "Breadth-first bounce depth for the queue entry."),
142
+ createWavefrontField("origin", "vec3<f32>", "Ray origin in renderer world space."),
143
+ createWavefrontField("direction", "vec3<f32>", "Normalized ray direction in renderer world space."),
144
+ createWavefrontField("throughput", "vec3<f32>", "Current path throughput before the next event."),
145
+ createWavefrontField("mediumRefId", "u32", "Active medium reference identifier for the ray."),
146
+ createWavefrontField("flags", "u32", "Bit flags for front-face state, debug, and quality toggles."),
147
+ ]),
148
+ }),
149
+ hit: Object.freeze({
150
+ schemaVersion: rendererWavefrontBufferSchemaVersion,
151
+ recordName: "HitRecord",
152
+ fields: Object.freeze([
153
+ createWavefrontField("rayId", "u32", "Ray identifier copied from the active queue."),
154
+ createWavefrontField("sourcePixelId", "u32", "Pixel/texel owner for the ray sample."),
155
+ createWavefrontField("hitType", rendererWavefrontHitTypes.join(" | "), "Resolved hit classification for termination or continuation."),
156
+ createWavefrontField("distance", "f32", "Nearest-hit distance or miss sentinel."),
157
+ createWavefrontField("entityId", "u32", "Stable scene entity identifier."),
158
+ createWavefrontField("instanceId", "u32", "Renderer instance identifier."),
159
+ createWavefrontField("primitiveId", "u32", "Primitive or triangle identifier."),
160
+ createWavefrontField("materialId", "u32", "Surface material identifier."),
161
+ createWavefrontField("barycentrics", "vec3<f32>", "Triangle barycentric coordinates for interpolation."),
162
+ createWavefrontField("uv", "vec2<f32>", "Resolved surface UV when available."),
163
+ createWavefrontField("geometricNormal", "vec3<f32>", "True geometric face normal."),
164
+ createWavefrontField("shadingNormal", "vec3<f32>", "Interpolated or repaired shading normal."),
165
+ createWavefrontField("frontFace", "bool", "Front-face classification for shading and medium transitions."),
166
+ ]),
167
+ }),
168
+ surface: Object.freeze({
169
+ schemaVersion: rendererWavefrontBufferSchemaVersion,
170
+ recordName: "SurfaceRecord",
171
+ fields: Object.freeze([
172
+ createWavefrontField("rayId", "u32", "Ray identifier matched to the resolved hit."),
173
+ createWavefrontField("entityId", "u32", "Stable scene entity identifier."),
174
+ createWavefrontField("materialRefId", "u32", "Material-reference indirection for shading lookup tables."),
175
+ createWavefrontField("mediumRefId", "u32", "Resolved medium transition/reference identifier."),
176
+ createWavefrontField("geometricNormal", "vec3<f32>", "Preserved geometric normal for hemisphere checks."),
177
+ createWavefrontField("shadingNormal", "vec3<f32>", "Normal used for BSDF/BTDF evaluation."),
178
+ createWavefrontField("uv", "vec2<f32>", "Resolved texture coordinate."),
179
+ createWavefrontField("tangentFrame", "mat3x3<f32>", "Optional tangent basis for normal-map transforms."),
180
+ ]),
181
+ }),
182
+ materialReference: Object.freeze({
183
+ schemaVersion: rendererWavefrontBufferSchemaVersion,
184
+ recordName: "MaterialReferenceRecord",
185
+ fields: Object.freeze([
186
+ createWavefrontField("materialRefId", "u32", "Stable material lookup identifier."),
187
+ createWavefrontField("materialId", "u32", "Authoritative material id from scene submission."),
188
+ createWavefrontField("shadingModel", "u32", "Renderer-owned shading model enum."),
189
+ createWavefrontField("textureSetId", "u32", "Texture indirection set for the material."),
190
+ createWavefrontField("flags", "u32", "Alpha, emissive, transmission, and debug flags."),
191
+ ]),
192
+ }),
193
+ mediumReference: Object.freeze({
194
+ schemaVersion: rendererWavefrontBufferSchemaVersion,
195
+ recordName: "MediumReferenceRecord",
196
+ fields: Object.freeze([
197
+ createWavefrontField("mediumRefId", "u32", "Stable medium lookup identifier."),
198
+ createWavefrontField("mediumId", "u32", "Authoritative medium or fluid descriptor id."),
199
+ createWavefrontField("phaseModel", "u32", "Medium phase-function selector."),
200
+ createWavefrontField("absorption", "vec3<f32>", "Absorption coefficients for the active medium."),
201
+ createWavefrontField("scattering", "vec3<f32>", "Scattering coefficients for the active medium."),
202
+ ]),
203
+ }),
204
+ accumulation: Object.freeze({
205
+ schemaVersion: rendererWavefrontBufferSchemaVersion,
206
+ recordName: "AccumulationRecord",
207
+ fields: Object.freeze([
208
+ createWavefrontField("sourcePixelId", "u32", "Screen pixel or texel accumulator owner."),
209
+ createWavefrontField("sampleCount", "u32", "Committed sample count for the pixel."),
210
+ createWavefrontField("radiance", "vec3<f32>", "Accumulated radiance before tone-map/output resolve."),
211
+ createWavefrontField("throughput", "vec3<f32>", "Last surviving throughput for debug and variance tracking."),
212
+ createWavefrontField("resetEpoch", "u32", "Accumulation reset generation for history invalidation."),
213
+ ]),
214
+ }),
215
+ });
216
+
217
+ function buildWavefrontTerminationPolicy() {
218
+ return Object.freeze({
219
+ terminalHitTypes: Object.freeze(["emissive", "environment", "miss"]),
220
+ continuationHitTypes: Object.freeze(["surface", "transparent"]),
221
+ emissive: Object.freeze({
222
+ action: "accumulate-and-stop",
223
+ contributesRadiance: true,
224
+ }),
225
+ environment: Object.freeze({
226
+ action: "accumulate-and-stop",
227
+ contributesRadiance: true,
228
+ }),
229
+ miss: Object.freeze({
230
+ action: "accumulate-environment-or-dark-stop",
231
+ contributesRadiance: true,
232
+ }),
233
+ });
234
+ }
235
+
236
+ function buildWavefrontBounceSchedule(maxDepth) {
237
+ return Object.freeze(
238
+ Array.from({ length: maxDepth }, (_, index) =>
239
+ Object.freeze({
240
+ bounce: index,
241
+ readQueue: index % 2 === 0 ? "active" : "next",
242
+ writeQueue: index % 2 === 0 ? "next" : "active",
243
+ passOrder: rendererWavefrontPassOrder,
244
+ })
245
+ )
246
+ );
247
+ }
248
+
249
+ export function createWavefrontPathTracingPlan(options = {}) {
250
+ const maxDepth =
251
+ options.maxDepth === undefined
252
+ ? 6
253
+ : readPositiveInteger("maxDepth", options.maxDepth);
254
+ const queueCapacity =
255
+ options.queueCapacity === undefined
256
+ ? 8192
257
+ : readPositiveInteger("queueCapacity", options.queueCapacity);
258
+ const accumulationResetEpoch =
259
+ options.accumulationResetEpoch === undefined
260
+ ? 0
261
+ : readNonNegativeInteger("accumulationResetEpoch", options.accumulationResetEpoch);
262
+ const explicitLightSampling = options.explicitLightSampling === true;
263
+
264
+ return Object.freeze({
265
+ schemaVersion: rendererWavefrontBufferSchemaVersion,
266
+ owner: rendererDebugOwner,
267
+ maxDepth,
268
+ queueCapacity,
269
+ explicitLightSampling,
270
+ accumulationResetEpoch,
271
+ queueLayout: Object.freeze({
272
+ strategy: rendererWavefrontQueuePairStrategy,
273
+ compactAfterScatter: true,
274
+ queues: Object.freeze([
275
+ Object.freeze({ name: "active", role: "current-bounce" }),
276
+ Object.freeze({ name: "next", role: "next-bounce" }),
277
+ ]),
278
+ }),
279
+ bufferContracts: rendererWavefrontBufferContracts,
280
+ bounceSchedule: buildWavefrontBounceSchedule(maxDepth),
281
+ terminationPolicy: buildWavefrontTerminationPolicy(),
282
+ });
283
+ }
284
+
107
285
  function buildRendererWorkerBudgetLevels(jobType, queueClass, levels) {
108
286
  return Object.freeze(
109
287
  levels.map((level) =>
@@ -678,6 +856,7 @@ export function createRayTracingRenderPlan(options = {}) {
678
856
  renderStages: workerManifest.renderStages,
679
857
  representationBands: representations,
680
858
  accelerationStructureUpdates: workerManifest.accelerationStructureUpdates,
859
+ wavefront: createWavefrontPathTracingPlan(options.wavefront),
681
860
  workerManifest,
682
861
  });
683
862
  }
@@ -732,6 +911,20 @@ function readPositiveNumber(name, value) {
732
911
  return value;
733
912
  }
734
913
 
914
+ function readPositiveInteger(name, value) {
915
+ if (!Number.isInteger(value) || value <= 0) {
916
+ throw new Error(`${name} must be a positive integer.`);
917
+ }
918
+ return value;
919
+ }
920
+
921
+ function readNonNegativeInteger(name, value) {
922
+ if (!Number.isInteger(value) || value < 0) {
923
+ throw new Error(`${name} must be a non-negative integer.`);
924
+ }
925
+ return value;
926
+ }
927
+
735
928
  function now() {
736
929
  if (typeof performance !== "undefined" && typeof performance.now === "function") {
737
930
  return performance.now();