@plasius/gpu-renderer 0.2.40 → 0.2.41
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 +23 -0
- package/README.md +41 -1
- package/dist/index.cjs +626 -147
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +626 -147
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/index.d.ts +34 -0
- package/src/wavefront-compute.js +174 -76
- package/src/wavefront-core.js +2 -0
- package/src/wavefront-frame-encoder.js +13 -5
- package/src/wavefront-frame-runtime.js +24 -0
- package/src/wavefront-frame-stats.js +9 -0
- package/src/wavefront-frame-telemetry.js +411 -0
- package/src/wavefront-pipelines.js +12 -0
- package/src/wavefront-runtime-support.js +37 -4
- package/src/wavefront-shader-bvh.js +43 -30
- package/src/wavefront-shader-kernels.js +1 -30
package/dist/index.cjs
CHANGED
|
@@ -2441,6 +2441,8 @@ function createWavefrontTransportGuardrailSummary(frameStats, options = {}) {
|
|
|
2441
2441
|
0,
|
|
2442
2442
|
Number(frameStats?.radianceDiagnostics?.legacyClampEquivalentSamples ?? 0)
|
|
2443
2443
|
);
|
|
2444
|
+
const rayCountStatus = typeof frameStats?.rayCounts?.status === "string" ? frameStats.rayCounts.status : "not-requested";
|
|
2445
|
+
const rayCountReason = typeof frameStats?.rayCounts?.reason === "string" ? frameStats.rayCounts.reason : null;
|
|
2444
2446
|
const memory = summarizeWavefrontMemory(frameStats?.memory);
|
|
2445
2447
|
const checks = Object.freeze([
|
|
2446
2448
|
createTransportGuardrailCheck(
|
|
@@ -2462,6 +2464,11 @@ function createWavefrontTransportGuardrailSummary(frameStats, options = {}) {
|
|
|
2462
2464
|
"radiance-diagnostics",
|
|
2463
2465
|
invalidSamples > 0 ? "fail" : legacyClampEquivalentSamples > 0 ? "warn" : "pass",
|
|
2464
2466
|
invalidSamples > 0 ? `Renderer recorded ${invalidSamples} invalid radiance sample(s); inspect transport math before trusting the frame.` : legacyClampEquivalentSamples > 0 ? `${legacyClampEquivalentSamples} weighted sample(s) exceeded the legacy 4.0 preview clamp threshold; convergence stayed unbiased, but review firefly diagnostics before rollout.` : "No invalid radiance samples or legacy-clamp-equivalent fireflies were recorded."
|
|
2467
|
+
),
|
|
2468
|
+
createTransportGuardrailCheck(
|
|
2469
|
+
"ray-count-telemetry",
|
|
2470
|
+
rayCountStatus === "failed" ? "fail" : rayCountStatus === "unavailable" ? "warn" : "pass",
|
|
2471
|
+
rayCountStatus === "failed" ? `Ray-count telemetry failed closed: ${rayCountReason ?? "unknown failure"}.` : rayCountStatus === "unavailable" ? `Ray-count telemetry was requested but unavailable: ${rayCountReason ?? "unknown reason"}.` : rayCountStatus === "available" ? "Primary, secondary, and total path-segment counts were captured." : "Ray-count telemetry was not requested for this frame."
|
|
2465
2472
|
)
|
|
2466
2473
|
]);
|
|
2467
2474
|
const status = checks.some((check) => check.status === "fail") ? "fail" : checks.some((check) => check.status === "warn") ? "warn" : "pass";
|
|
@@ -2482,6 +2489,7 @@ function createWavefrontTransportGuardrailSummary(frameStats, options = {}) {
|
|
|
2482
2489
|
invalidSamples,
|
|
2483
2490
|
legacyClampEquivalentSamples
|
|
2484
2491
|
}),
|
|
2492
|
+
rayCountStatus,
|
|
2485
2493
|
memory
|
|
2486
2494
|
}),
|
|
2487
2495
|
checks
|
|
@@ -2592,6 +2600,7 @@ var COUNTER_TRANSPORT_RESIDUAL_LUMINANCE_OFFSET = 22;
|
|
|
2592
2600
|
var COUNTER_TRANSPORT_ZERO_TERMINATION_OFFSET = 23;
|
|
2593
2601
|
var COUNTER_TRANSPORT_CHECKSUM_OFFSET = 24;
|
|
2594
2602
|
var TRACE_STORAGE_BUFFER_BINDINGS = 10;
|
|
2603
|
+
var TRACE_SAMPLED_TEXTURE_BINDINGS = 21;
|
|
2595
2604
|
var BRDF_LUT_UPLOAD_CACHE = /* @__PURE__ */ new Map();
|
|
2596
2605
|
var MATERIAL_DIFFUSE = 0;
|
|
2597
2606
|
var MATERIAL_METAL = 1;
|
|
@@ -2650,6 +2659,7 @@ var EMPTY_TERMINATION_METRICS = Object.freeze({
|
|
|
2650
2659
|
var wavefrontPathTracingComputeLimits = Object.freeze({
|
|
2651
2660
|
workgroupSize: WORKGROUP_SIZE,
|
|
2652
2661
|
traceStorageBufferBindings: TRACE_STORAGE_BUFFER_BINDINGS,
|
|
2662
|
+
traceSampledTextureBindings: TRACE_SAMPLED_TEXTURE_BINDINGS,
|
|
2653
2663
|
rayRecordBytes: RAY_RECORD_BYTES,
|
|
2654
2664
|
hitRecordBytes: HIT_RECORD_BYTES,
|
|
2655
2665
|
sceneObjectRecordBytes: SCENE_OBJECT_RECORD_BYTES,
|
|
@@ -5783,11 +5793,11 @@ function resolveCanvas3(canvasOrSelector, documentRef = globalThis.document) {
|
|
|
5783
5793
|
return fallback;
|
|
5784
5794
|
}
|
|
5785
5795
|
async function getPipelineDiagnostics(shaderModule) {
|
|
5786
|
-
if (typeof shaderModule?.
|
|
5796
|
+
if (typeof shaderModule?.getCompilationInfo !== "function") {
|
|
5787
5797
|
return "";
|
|
5788
5798
|
}
|
|
5789
5799
|
try {
|
|
5790
|
-
const info = await shaderModule.
|
|
5800
|
+
const info = await shaderModule.getCompilationInfo();
|
|
5791
5801
|
const messages = info.messages ?? [];
|
|
5792
5802
|
if (messages.length === 0) {
|
|
5793
5803
|
return "";
|
|
@@ -5825,10 +5835,10 @@ ${diagnostics}` : "";
|
|
|
5825
5835
|
}
|
|
5826
5836
|
}
|
|
5827
5837
|
async function assertShaderModuleCompiles(shaderModule, label) {
|
|
5828
|
-
if (typeof shaderModule?.
|
|
5838
|
+
if (typeof shaderModule?.getCompilationInfo !== "function") {
|
|
5829
5839
|
return;
|
|
5830
5840
|
}
|
|
5831
|
-
const info = await shaderModule.
|
|
5841
|
+
const info = await shaderModule.getCompilationInfo();
|
|
5832
5842
|
const messages = Array.isArray(info?.messages) ? info.messages : [];
|
|
5833
5843
|
const errors = messages.filter((message) => message?.type === "error");
|
|
5834
5844
|
if (errors.length <= 0) {
|
|
@@ -5862,7 +5872,28 @@ function createWavefrontDeviceDescriptor(adapter, options = {}) {
|
|
|
5862
5872
|
TRACE_STORAGE_BUFFER_BINDINGS
|
|
5863
5873
|
);
|
|
5864
5874
|
}
|
|
5875
|
+
const exposedSampledTextureLimit = Number(adapter?.limits?.maxSampledTexturesPerShaderStage);
|
|
5876
|
+
if (Number.isFinite(exposedSampledTextureLimit)) {
|
|
5877
|
+
if (exposedSampledTextureLimit < TRACE_SAMPLED_TEXTURE_BINDINGS) {
|
|
5878
|
+
throw new Error(
|
|
5879
|
+
`Wavefront material tracing requires maxSampledTexturesPerShaderStage>=${TRACE_SAMPLED_TEXTURE_BINDINGS}, but this adapter exposes ${exposedSampledTextureLimit}.`
|
|
5880
|
+
);
|
|
5881
|
+
}
|
|
5882
|
+
requiredLimits.maxSampledTexturesPerShaderStage = Math.max(
|
|
5883
|
+
Number(requiredLimits.maxSampledTexturesPerShaderStage ?? 0),
|
|
5884
|
+
TRACE_SAMPLED_TEXTURE_BINDINGS
|
|
5885
|
+
);
|
|
5886
|
+
}
|
|
5865
5887
|
const descriptor = { ...options.deviceDescriptor ?? {} };
|
|
5888
|
+
const requiredFeatures = Array.from(descriptor.requiredFeatures ?? []);
|
|
5889
|
+
const adapterFeatures = adapter?.features;
|
|
5890
|
+
const timestampQuerySupported = typeof adapterFeatures?.has === "function" ? adapterFeatures.has("timestamp-query") : adapterFeatures && typeof adapterFeatures[Symbol.iterator] === "function" ? Array.from(adapterFeatures).includes("timestamp-query") : false;
|
|
5891
|
+
if (options.gpuTimestamps !== false && timestampQuerySupported && !requiredFeatures.includes("timestamp-query")) {
|
|
5892
|
+
requiredFeatures.push("timestamp-query");
|
|
5893
|
+
}
|
|
5894
|
+
if (requiredFeatures.length > 0) {
|
|
5895
|
+
descriptor.requiredFeatures = requiredFeatures;
|
|
5896
|
+
}
|
|
5866
5897
|
if (Object.keys(requiredLimits).length > 0) {
|
|
5867
5898
|
descriptor.requiredLimits = {
|
|
5868
5899
|
...descriptor.requiredLimits ?? {},
|
|
@@ -6052,6 +6083,372 @@ async function waitForSubmittedGpuWork(device, options = {}) {
|
|
|
6052
6083
|
}
|
|
6053
6084
|
}
|
|
6054
6085
|
|
|
6086
|
+
// src/wavefront-frame-telemetry.js
|
|
6087
|
+
var TIMESTAMP_QUERY_COUNT = 2;
|
|
6088
|
+
var TIMESTAMP_BUFFER_BYTES = TIMESTAMP_QUERY_COUNT * BigUint64Array.BYTES_PER_ELEMENT;
|
|
6089
|
+
function hasGpuFeature(features, name) {
|
|
6090
|
+
if (typeof features?.has === "function") {
|
|
6091
|
+
return features.has(name);
|
|
6092
|
+
}
|
|
6093
|
+
if (features && typeof features[Symbol.iterator] === "function") {
|
|
6094
|
+
return Array.from(features).includes(name);
|
|
6095
|
+
}
|
|
6096
|
+
return false;
|
|
6097
|
+
}
|
|
6098
|
+
function freezeRayCountTelemetry(values = {}) {
|
|
6099
|
+
return Object.freeze({
|
|
6100
|
+
status: values.status ?? "not-requested",
|
|
6101
|
+
source: values.source ?? null,
|
|
6102
|
+
expectedPrimaryRays: values.expectedPrimaryRays ?? null,
|
|
6103
|
+
observedPrimaryRays: values.observedPrimaryRays ?? null,
|
|
6104
|
+
secondaryRays: values.secondaryRays ?? null,
|
|
6105
|
+
totalPathSegments: values.totalPathSegments ?? null,
|
|
6106
|
+
bounceHistogram: Object.freeze([...values.bounceHistogram ?? []]),
|
|
6107
|
+
capturedRayCounts: values.capturedRayCounts ?? 0,
|
|
6108
|
+
expectedRayCounts: values.expectedRayCounts ?? 0,
|
|
6109
|
+
reason: values.reason ?? null
|
|
6110
|
+
});
|
|
6111
|
+
}
|
|
6112
|
+
function createWavefrontRayCountTelemetry(values = {}) {
|
|
6113
|
+
return freezeRayCountTelemetry(values);
|
|
6114
|
+
}
|
|
6115
|
+
function createWavefrontFrameTimingTelemetry(values = {}) {
|
|
6116
|
+
return Object.freeze({
|
|
6117
|
+
status: values.status ?? "not-requested",
|
|
6118
|
+
source: values.source ?? null,
|
|
6119
|
+
timestampQueryStatus: values.timestampQueryStatus ?? "not-recorded",
|
|
6120
|
+
totalGpuTimeMs: values.totalGpuTimeMs ?? null,
|
|
6121
|
+
totalRenderJobTimeMs: Math.max(0, Number(values.totalRenderJobTimeMs ?? 0)),
|
|
6122
|
+
classificationTimeMs: values.classificationTimeMs ?? null,
|
|
6123
|
+
compactionTimeMs: values.compactionTimeMs ?? null,
|
|
6124
|
+
samplingTimeMs: values.samplingTimeMs ?? null,
|
|
6125
|
+
reason: values.reason ?? null
|
|
6126
|
+
});
|
|
6127
|
+
}
|
|
6128
|
+
function safeErrorReason(error) {
|
|
6129
|
+
return error instanceof Error && error.message ? error.message : "unknown-telemetry-readback-failure";
|
|
6130
|
+
}
|
|
6131
|
+
function timestampDurationMs(values) {
|
|
6132
|
+
if (values.length < TIMESTAMP_QUERY_COUNT || values[1] < values[0]) {
|
|
6133
|
+
return null;
|
|
6134
|
+
}
|
|
6135
|
+
const durationNanoseconds = values[1] - values[0];
|
|
6136
|
+
const durationMs = Number(durationNanoseconds) / 1e6;
|
|
6137
|
+
return Number.isFinite(durationMs) && durationMs >= 0 ? durationMs : null;
|
|
6138
|
+
}
|
|
6139
|
+
function createWavefrontFrameTelemetryResources({
|
|
6140
|
+
device,
|
|
6141
|
+
constants,
|
|
6142
|
+
maxRayCountRecords
|
|
6143
|
+
}) {
|
|
6144
|
+
const rayRecordCapacity = Math.max(1, Math.trunc(Number(maxRayCountRecords) || 1));
|
|
6145
|
+
const rayBufferBytes = rayRecordCapacity * Uint32Array.BYTES_PER_ELEMENT;
|
|
6146
|
+
const mapMode = constants.map;
|
|
6147
|
+
if (!mapMode || typeof mapMode.READ !== "number") {
|
|
6148
|
+
return Object.freeze({
|
|
6149
|
+
available: false,
|
|
6150
|
+
reason: "gpu-map-read-unavailable",
|
|
6151
|
+
memoryBytes: 0,
|
|
6152
|
+
beginFrame() {
|
|
6153
|
+
},
|
|
6154
|
+
recordActiveRayCount() {
|
|
6155
|
+
},
|
|
6156
|
+
decorateFirstPass(descriptor) {
|
|
6157
|
+
return descriptor;
|
|
6158
|
+
},
|
|
6159
|
+
decorateFinalPass(descriptor) {
|
|
6160
|
+
return descriptor;
|
|
6161
|
+
},
|
|
6162
|
+
async readFrame({ expectedPrimaryRays = 0, expectedRayCounts = 0 } = {}) {
|
|
6163
|
+
return Object.freeze({
|
|
6164
|
+
rayCounts: freezeRayCountTelemetry({
|
|
6165
|
+
status: "unavailable",
|
|
6166
|
+
expectedPrimaryRays,
|
|
6167
|
+
expectedRayCounts,
|
|
6168
|
+
reason: "gpu-map-read-unavailable"
|
|
6169
|
+
}),
|
|
6170
|
+
timestampQueryStatus: "unsupported",
|
|
6171
|
+
totalGpuTimeMs: null,
|
|
6172
|
+
reason: "gpu-map-read-unavailable"
|
|
6173
|
+
});
|
|
6174
|
+
},
|
|
6175
|
+
destroy() {
|
|
6176
|
+
}
|
|
6177
|
+
});
|
|
6178
|
+
}
|
|
6179
|
+
const rayCountBuffer = createBuffer(
|
|
6180
|
+
device,
|
|
6181
|
+
constants.buffer.COPY_DST | constants.buffer.COPY_SRC,
|
|
6182
|
+
rayBufferBytes,
|
|
6183
|
+
"plasius.wavefront.rayCounts"
|
|
6184
|
+
);
|
|
6185
|
+
const rayCountReadbackBuffer = createBuffer(
|
|
6186
|
+
device,
|
|
6187
|
+
constants.buffer.COPY_DST | constants.buffer.MAP_READ,
|
|
6188
|
+
rayBufferBytes,
|
|
6189
|
+
"plasius.wavefront.rayCounts.readback"
|
|
6190
|
+
);
|
|
6191
|
+
const timestampFeatureAvailable = hasGpuFeature(device?.features, "timestamp-query");
|
|
6192
|
+
const timestampApiAvailable = timestampFeatureAvailable && typeof device.createQuerySet === "function" && typeof constants.buffer.QUERY_RESOLVE === "number";
|
|
6193
|
+
let timestampQuerySet = null;
|
|
6194
|
+
let timestampResolveBuffer = null;
|
|
6195
|
+
let timestampReadbackBuffer = null;
|
|
6196
|
+
let timestampSetupReason = timestampFeatureAvailable ? "timestamp-query-api-unavailable" : "timestamp-query-unsupported";
|
|
6197
|
+
if (timestampApiAvailable) {
|
|
6198
|
+
try {
|
|
6199
|
+
timestampQuerySet = device.createQuerySet({
|
|
6200
|
+
label: "plasius.wavefront.timestamps",
|
|
6201
|
+
type: "timestamp",
|
|
6202
|
+
count: TIMESTAMP_QUERY_COUNT
|
|
6203
|
+
});
|
|
6204
|
+
timestampResolveBuffer = createBuffer(
|
|
6205
|
+
device,
|
|
6206
|
+
constants.buffer.QUERY_RESOLVE | constants.buffer.COPY_SRC,
|
|
6207
|
+
TIMESTAMP_BUFFER_BYTES,
|
|
6208
|
+
"plasius.wavefront.timestamps.resolve"
|
|
6209
|
+
);
|
|
6210
|
+
timestampReadbackBuffer = createBuffer(
|
|
6211
|
+
device,
|
|
6212
|
+
constants.buffer.COPY_DST | constants.buffer.MAP_READ,
|
|
6213
|
+
TIMESTAMP_BUFFER_BYTES,
|
|
6214
|
+
"plasius.wavefront.timestamps.readback"
|
|
6215
|
+
);
|
|
6216
|
+
timestampSetupReason = null;
|
|
6217
|
+
} catch (error) {
|
|
6218
|
+
timestampQuerySet?.destroy?.();
|
|
6219
|
+
timestampQuerySet = null;
|
|
6220
|
+
timestampResolveBuffer?.destroy?.();
|
|
6221
|
+
timestampResolveBuffer = null;
|
|
6222
|
+
timestampReadbackBuffer?.destroy?.();
|
|
6223
|
+
timestampReadbackBuffer = null;
|
|
6224
|
+
timestampSetupReason = `timestamp-query-setup-failed:${safeErrorReason(error)}`;
|
|
6225
|
+
}
|
|
6226
|
+
}
|
|
6227
|
+
const timestampUnavailableStatus = timestampSetupReason?.startsWith(
|
|
6228
|
+
"timestamp-query-setup-failed:"
|
|
6229
|
+
) ? "failed" : "unsupported";
|
|
6230
|
+
let active = false;
|
|
6231
|
+
let recordCount = 0;
|
|
6232
|
+
let recordOverflow = false;
|
|
6233
|
+
let bounceIndices = [];
|
|
6234
|
+
let timestampStarted = false;
|
|
6235
|
+
let timestampEnded = false;
|
|
6236
|
+
let rayReadbackMapped = false;
|
|
6237
|
+
let timestampReadbackMapped = false;
|
|
6238
|
+
const memoryBytes = rayBufferBytes * 2 + (timestampQuerySet ? TIMESTAMP_BUFFER_BYTES * 2 : 0);
|
|
6239
|
+
function beginFrame() {
|
|
6240
|
+
active = true;
|
|
6241
|
+
recordCount = 0;
|
|
6242
|
+
recordOverflow = false;
|
|
6243
|
+
bounceIndices = [];
|
|
6244
|
+
timestampStarted = false;
|
|
6245
|
+
timestampEnded = false;
|
|
6246
|
+
}
|
|
6247
|
+
function recordActiveRayCount(encoder, counterBuffer, bounceIndex) {
|
|
6248
|
+
if (!active) {
|
|
6249
|
+
return;
|
|
6250
|
+
}
|
|
6251
|
+
if (recordCount >= rayRecordCapacity) {
|
|
6252
|
+
recordOverflow = true;
|
|
6253
|
+
return;
|
|
6254
|
+
}
|
|
6255
|
+
encoder.copyBufferToBuffer(
|
|
6256
|
+
counterBuffer,
|
|
6257
|
+
0,
|
|
6258
|
+
rayCountBuffer,
|
|
6259
|
+
recordCount * Uint32Array.BYTES_PER_ELEMENT,
|
|
6260
|
+
Uint32Array.BYTES_PER_ELEMENT
|
|
6261
|
+
);
|
|
6262
|
+
bounceIndices.push(Math.max(0, Math.trunc(Number(bounceIndex) || 0)));
|
|
6263
|
+
recordCount += 1;
|
|
6264
|
+
}
|
|
6265
|
+
function decorateFirstPass(descriptor) {
|
|
6266
|
+
if (!active || !timestampQuerySet || timestampStarted) {
|
|
6267
|
+
return descriptor;
|
|
6268
|
+
}
|
|
6269
|
+
timestampStarted = true;
|
|
6270
|
+
return {
|
|
6271
|
+
...descriptor,
|
|
6272
|
+
timestampWrites: {
|
|
6273
|
+
querySet: timestampQuerySet,
|
|
6274
|
+
beginningOfPassWriteIndex: 0
|
|
6275
|
+
}
|
|
6276
|
+
};
|
|
6277
|
+
}
|
|
6278
|
+
function decorateFinalPass(descriptor) {
|
|
6279
|
+
if (!active || !timestampQuerySet || !timestampStarted || timestampEnded) {
|
|
6280
|
+
return descriptor;
|
|
6281
|
+
}
|
|
6282
|
+
timestampEnded = true;
|
|
6283
|
+
return {
|
|
6284
|
+
...descriptor,
|
|
6285
|
+
timestampWrites: {
|
|
6286
|
+
querySet: timestampQuerySet,
|
|
6287
|
+
endOfPassWriteIndex: 1
|
|
6288
|
+
}
|
|
6289
|
+
};
|
|
6290
|
+
}
|
|
6291
|
+
async function readFrame({
|
|
6292
|
+
expectedPrimaryRays = 0,
|
|
6293
|
+
expectedRayCounts = recordCount,
|
|
6294
|
+
waitForSubmittedGpuWork: waitForSubmittedGpuWork2
|
|
6295
|
+
} = {}) {
|
|
6296
|
+
active = false;
|
|
6297
|
+
if (recordOverflow || recordCount !== expectedRayCounts) {
|
|
6298
|
+
return Object.freeze({
|
|
6299
|
+
rayCounts: freezeRayCountTelemetry({
|
|
6300
|
+
status: "failed",
|
|
6301
|
+
expectedPrimaryRays,
|
|
6302
|
+
capturedRayCounts: recordCount,
|
|
6303
|
+
expectedRayCounts,
|
|
6304
|
+
reason: recordOverflow ? "ray-count-record-capacity-exceeded" : `ray-count-record-mismatch:${recordCount}/${expectedRayCounts}`
|
|
6305
|
+
}),
|
|
6306
|
+
timestampQueryStatus: timestampQuerySet ? "failed" : timestampUnavailableStatus,
|
|
6307
|
+
totalGpuTimeMs: null,
|
|
6308
|
+
reason: "ray-count-recording-incomplete"
|
|
6309
|
+
});
|
|
6310
|
+
}
|
|
6311
|
+
const encoder = device.createCommandEncoder({
|
|
6312
|
+
label: "plasius.wavefront.telemetry.readback"
|
|
6313
|
+
});
|
|
6314
|
+
const rayCopyBytes = Math.max(
|
|
6315
|
+
Uint32Array.BYTES_PER_ELEMENT,
|
|
6316
|
+
recordCount * Uint32Array.BYTES_PER_ELEMENT
|
|
6317
|
+
);
|
|
6318
|
+
encoder.copyBufferToBuffer(rayCountBuffer, 0, rayCountReadbackBuffer, 0, rayCopyBytes);
|
|
6319
|
+
const canResolveTimestamps = timestampQuerySet && timestampStarted && timestampEnded && typeof encoder.resolveQuerySet === "function";
|
|
6320
|
+
if (canResolveTimestamps) {
|
|
6321
|
+
encoder.resolveQuerySet(
|
|
6322
|
+
timestampQuerySet,
|
|
6323
|
+
0,
|
|
6324
|
+
TIMESTAMP_QUERY_COUNT,
|
|
6325
|
+
timestampResolveBuffer,
|
|
6326
|
+
0
|
|
6327
|
+
);
|
|
6328
|
+
encoder.copyBufferToBuffer(
|
|
6329
|
+
timestampResolveBuffer,
|
|
6330
|
+
0,
|
|
6331
|
+
timestampReadbackBuffer,
|
|
6332
|
+
0,
|
|
6333
|
+
TIMESTAMP_BUFFER_BYTES
|
|
6334
|
+
);
|
|
6335
|
+
}
|
|
6336
|
+
device.queue.submit([encoder.finish()]);
|
|
6337
|
+
try {
|
|
6338
|
+
await waitForSubmittedGpuWork2({
|
|
6339
|
+
timeoutMs: GPU_READBACK_COMPLETION_TIMEOUT_MS,
|
|
6340
|
+
allowTimeout: false
|
|
6341
|
+
});
|
|
6342
|
+
await rayCountReadbackBuffer.mapAsync(mapMode.READ);
|
|
6343
|
+
rayReadbackMapped = true;
|
|
6344
|
+
const rayCopy = rayCountReadbackBuffer.getMappedRange().slice(0, rayCopyBytes);
|
|
6345
|
+
const rayCounts = new Uint32Array(rayCopy, 0, recordCount);
|
|
6346
|
+
rayCountReadbackBuffer.unmap();
|
|
6347
|
+
rayReadbackMapped = false;
|
|
6348
|
+
const bounceHistogram = Array.from({ length: Math.max(0, ...bounceIndices) + 1 }, () => 0);
|
|
6349
|
+
let observedPrimaryRays = 0;
|
|
6350
|
+
let secondaryRays = 0;
|
|
6351
|
+
let totalPathSegments = 0;
|
|
6352
|
+
for (let index = 0; index < rayCounts.length; index += 1) {
|
|
6353
|
+
const count = Number(rayCounts[index] ?? 0);
|
|
6354
|
+
const bounceIndex = bounceIndices[index] ?? 0;
|
|
6355
|
+
bounceHistogram[bounceIndex] = (bounceHistogram[bounceIndex] ?? 0) + count;
|
|
6356
|
+
totalPathSegments += count;
|
|
6357
|
+
if (bounceIndex === 0) {
|
|
6358
|
+
observedPrimaryRays += count;
|
|
6359
|
+
} else {
|
|
6360
|
+
secondaryRays += count;
|
|
6361
|
+
}
|
|
6362
|
+
}
|
|
6363
|
+
const primaryCountsMatch = observedPrimaryRays === expectedPrimaryRays;
|
|
6364
|
+
const rayTelemetry = freezeRayCountTelemetry({
|
|
6365
|
+
status: primaryCountsMatch ? "available" : "failed",
|
|
6366
|
+
source: "gpu-active-queue-readback",
|
|
6367
|
+
expectedPrimaryRays,
|
|
6368
|
+
observedPrimaryRays,
|
|
6369
|
+
secondaryRays: primaryCountsMatch ? secondaryRays : null,
|
|
6370
|
+
totalPathSegments: primaryCountsMatch ? totalPathSegments : null,
|
|
6371
|
+
bounceHistogram,
|
|
6372
|
+
capturedRayCounts: recordCount,
|
|
6373
|
+
expectedRayCounts,
|
|
6374
|
+
reason: primaryCountsMatch ? null : `primary-ray-count-mismatch:${observedPrimaryRays}/${expectedPrimaryRays}`
|
|
6375
|
+
});
|
|
6376
|
+
let timestampQueryStatus = timestampQuerySet ? "failed" : timestampUnavailableStatus;
|
|
6377
|
+
let totalGpuTimeMs = null;
|
|
6378
|
+
let timestampReason = timestampSetupReason;
|
|
6379
|
+
if (canResolveTimestamps) {
|
|
6380
|
+
try {
|
|
6381
|
+
await timestampReadbackBuffer.mapAsync(mapMode.READ);
|
|
6382
|
+
timestampReadbackMapped = true;
|
|
6383
|
+
const timestampCopy = timestampReadbackBuffer.getMappedRange().slice(0, TIMESTAMP_BUFFER_BYTES);
|
|
6384
|
+
const timestamps = new BigUint64Array(timestampCopy);
|
|
6385
|
+
timestampReadbackBuffer.unmap();
|
|
6386
|
+
timestampReadbackMapped = false;
|
|
6387
|
+
totalGpuTimeMs = timestampDurationMs(timestamps);
|
|
6388
|
+
timestampQueryStatus = totalGpuTimeMs === null ? "failed" : "available";
|
|
6389
|
+
timestampReason = totalGpuTimeMs === null ? "timestamp-query-returned-invalid-range" : null;
|
|
6390
|
+
} catch (error) {
|
|
6391
|
+
if (timestampReadbackMapped) {
|
|
6392
|
+
timestampReadbackBuffer.unmap();
|
|
6393
|
+
timestampReadbackMapped = false;
|
|
6394
|
+
}
|
|
6395
|
+
timestampQueryStatus = "failed";
|
|
6396
|
+
timestampReason = `timestamp-query-readback-failed:${safeErrorReason(error)}`;
|
|
6397
|
+
}
|
|
6398
|
+
} else if (timestampQuerySet) {
|
|
6399
|
+
timestampReason = "timestamp-query-span-incomplete";
|
|
6400
|
+
}
|
|
6401
|
+
return Object.freeze({
|
|
6402
|
+
rayCounts: rayTelemetry,
|
|
6403
|
+
timestampQueryStatus,
|
|
6404
|
+
totalGpuTimeMs,
|
|
6405
|
+
reason: timestampReason
|
|
6406
|
+
});
|
|
6407
|
+
} catch (error) {
|
|
6408
|
+
if (rayReadbackMapped) {
|
|
6409
|
+
rayCountReadbackBuffer.unmap();
|
|
6410
|
+
rayReadbackMapped = false;
|
|
6411
|
+
}
|
|
6412
|
+
if (timestampReadbackMapped) {
|
|
6413
|
+
timestampReadbackBuffer?.unmap();
|
|
6414
|
+
timestampReadbackMapped = false;
|
|
6415
|
+
}
|
|
6416
|
+
const reason = safeErrorReason(error);
|
|
6417
|
+
return Object.freeze({
|
|
6418
|
+
rayCounts: freezeRayCountTelemetry({
|
|
6419
|
+
status: "failed",
|
|
6420
|
+
expectedPrimaryRays,
|
|
6421
|
+
capturedRayCounts: recordCount,
|
|
6422
|
+
expectedRayCounts,
|
|
6423
|
+
reason: `ray-count-readback-failed:${reason}`
|
|
6424
|
+
}),
|
|
6425
|
+
timestampQueryStatus: timestampQuerySet ? "failed" : timestampUnavailableStatus,
|
|
6426
|
+
totalGpuTimeMs: null,
|
|
6427
|
+
reason: `telemetry-readback-failed:${reason}`
|
|
6428
|
+
});
|
|
6429
|
+
}
|
|
6430
|
+
}
|
|
6431
|
+
function destroy() {
|
|
6432
|
+
active = false;
|
|
6433
|
+
rayCountBuffer.destroy?.();
|
|
6434
|
+
rayCountReadbackBuffer.destroy?.();
|
|
6435
|
+
timestampQuerySet?.destroy?.();
|
|
6436
|
+
timestampResolveBuffer?.destroy?.();
|
|
6437
|
+
timestampReadbackBuffer?.destroy?.();
|
|
6438
|
+
}
|
|
6439
|
+
return Object.freeze({
|
|
6440
|
+
available: true,
|
|
6441
|
+
reason: null,
|
|
6442
|
+
memoryBytes,
|
|
6443
|
+
beginFrame,
|
|
6444
|
+
recordActiveRayCount,
|
|
6445
|
+
decorateFirstPass,
|
|
6446
|
+
decorateFinalPass,
|
|
6447
|
+
readFrame,
|
|
6448
|
+
destroy
|
|
6449
|
+
});
|
|
6450
|
+
}
|
|
6451
|
+
|
|
6055
6452
|
// src/wavefront-frame-stats.js
|
|
6056
6453
|
function resolveWavefrontRenderedSamplesPerPixel({
|
|
6057
6454
|
config,
|
|
@@ -6149,6 +6546,11 @@ function createWavefrontFrameStats({
|
|
|
6149
6546
|
maxFramePassesPerSubmission: config.maxFramePassesPerSubmission,
|
|
6150
6547
|
screenRays: config.width * config.height,
|
|
6151
6548
|
primaryRays: config.width * config.height * renderedSamplesPerPixel,
|
|
6549
|
+
secondaryRays: null,
|
|
6550
|
+
totalPathSegments: null,
|
|
6551
|
+
rayCounts: createWavefrontRayCountTelemetry(),
|
|
6552
|
+
timings: createWavefrontFrameTimingTelemetry(),
|
|
6553
|
+
telemetryMemoryBytes: 0,
|
|
6152
6554
|
sceneObjectCount: config.sceneObjectCount,
|
|
6153
6555
|
triangleCount: config.triangleCount,
|
|
6154
6556
|
emissiveTriangleCount: config.emissiveTriangleCount,
|
|
@@ -6191,7 +6593,8 @@ function createWavefrontFrameEncoder({
|
|
|
6191
6593
|
denoiseDirectResolveBindGroup,
|
|
6192
6594
|
presentPipeline,
|
|
6193
6595
|
presentBindGroup,
|
|
6194
|
-
context
|
|
6596
|
+
context,
|
|
6597
|
+
getFrameTelemetry = () => null
|
|
6195
6598
|
}) {
|
|
6196
6599
|
const resolveConfig = resolveGetter(getConfig);
|
|
6197
6600
|
const resolveBindGroups = resolveGetter(getBindGroups);
|
|
@@ -6199,9 +6602,12 @@ function createWavefrontFrameEncoder({
|
|
|
6199
6602
|
encodeTileSample(encoder, tile, configOffset, parallelism) {
|
|
6200
6603
|
const config = resolveConfig();
|
|
6201
6604
|
const bindGroups = resolveBindGroups();
|
|
6202
|
-
const
|
|
6203
|
-
|
|
6204
|
-
|
|
6605
|
+
const frameTelemetry = getFrameTelemetry();
|
|
6606
|
+
const generatePass = encoder.beginComputePass(
|
|
6607
|
+
frameTelemetry?.decorateFirstPass({
|
|
6608
|
+
label: "plasius.wavefront.generatePrimaryRaysPass"
|
|
6609
|
+
}) ?? { label: "plasius.wavefront.generatePrimaryRaysPass" }
|
|
6610
|
+
);
|
|
6205
6611
|
const tileWorkgroups = Math.ceil(tile.width * tile.height / WORKGROUP_SIZE);
|
|
6206
6612
|
generatePass.setBindGroup(0, bindGroups[0], [configOffset]);
|
|
6207
6613
|
generatePass.setPipeline(pipelines.generatePrimaryRays);
|
|
@@ -6209,6 +6615,7 @@ function createWavefrontFrameEncoder({
|
|
|
6209
6615
|
recordDirectDispatch(parallelism, [tileWorkgroups], WORKGROUP_SIZE);
|
|
6210
6616
|
generatePass.end();
|
|
6211
6617
|
for (let bounceIndex = 0; bounceIndex < config.maxDepth; bounceIndex += 1) {
|
|
6618
|
+
frameTelemetry?.recordActiveRayCount(encoder, counterBuffer, bounceIndex);
|
|
6212
6619
|
encoder.copyBufferToBuffer(
|
|
6213
6620
|
counterBuffer,
|
|
6214
6621
|
COUNTER_DISPATCH_ARGS_OFFSET,
|
|
@@ -6285,7 +6692,7 @@ function createWavefrontFrameEncoder({
|
|
|
6285
6692
|
},
|
|
6286
6693
|
encodePresent(encoder) {
|
|
6287
6694
|
const texture = context.getCurrentTexture();
|
|
6288
|
-
const
|
|
6695
|
+
const descriptor = {
|
|
6289
6696
|
label: "plasius.wavefront.presentPass",
|
|
6290
6697
|
colorAttachments: [
|
|
6291
6698
|
{
|
|
@@ -6295,7 +6702,10 @@ function createWavefrontFrameEncoder({
|
|
|
6295
6702
|
storeOp: "store"
|
|
6296
6703
|
}
|
|
6297
6704
|
]
|
|
6298
|
-
}
|
|
6705
|
+
};
|
|
6706
|
+
const passEncoder = encoder.beginRenderPass(
|
|
6707
|
+
getFrameTelemetry()?.decorateFinalPass(descriptor) ?? descriptor
|
|
6708
|
+
);
|
|
6299
6709
|
passEncoder.setPipeline(presentPipeline);
|
|
6300
6710
|
passEncoder.setBindGroup(0, presentBindGroup);
|
|
6301
6711
|
passEncoder.draw(3);
|
|
@@ -6595,36 +7005,49 @@ fn prepareMeshTrianglesAndLeaves(@builtin(global_invocation_id) globalId: vec3<u
|
|
|
6595
7005
|
let uv1 = select(vec2<f32>(0.0), vertex1.uv.xy, vertex1.uv.z > 0.5);
|
|
6596
7006
|
let uv2 = select(vec2<f32>(0.0), vertex2.uv.xy, vertex2.uv.z > 0.5);
|
|
6597
7007
|
|
|
6598
|
-
|
|
6599
|
-
|
|
6600
|
-
|
|
6601
|
-
|
|
6602
|
-
|
|
6603
|
-
|
|
6604
|
-
|
|
6605
|
-
|
|
6606
|
-
|
|
6607
|
-
|
|
6608
|
-
|
|
6609
|
-
|
|
6610
|
-
|
|
6611
|
-
|
|
6612
|
-
|
|
6613
|
-
|
|
6614
|
-
|
|
6615
|
-
|
|
6616
|
-
|
|
6617
|
-
|
|
6618
|
-
|
|
6619
|
-
|
|
6620
|
-
|
|
6621
|
-
|
|
6622
|
-
|
|
6623
|
-
|
|
6624
|
-
|
|
6625
|
-
|
|
6626
|
-
|
|
6627
|
-
);
|
|
7008
|
+
var preparedTriangle = TriangleRecord();
|
|
7009
|
+
preparedTriangle.triangleId = triangleIndex;
|
|
7010
|
+
preparedTriangle.meshId = mesh.meshId;
|
|
7011
|
+
preparedTriangle.materialKind = mesh.materialKind;
|
|
7012
|
+
preparedTriangle.flags = mesh.flags;
|
|
7013
|
+
preparedTriangle.materialRefId = mesh.materialRefId;
|
|
7014
|
+
preparedTriangle.mediumRefId = mesh.mediumRefId;
|
|
7015
|
+
preparedTriangle.materialSlot = mesh.materialSlot;
|
|
7016
|
+
preparedTriangle.v0 = vec4<f32>(vertex0.position.xyz, 0.0);
|
|
7017
|
+
preparedTriangle.v1 = vec4<f32>(vertex1.position.xyz, 0.0);
|
|
7018
|
+
preparedTriangle.v2 = vec4<f32>(vertex2.position.xyz, 0.0);
|
|
7019
|
+
preparedTriangle.n0 = vec4<f32>(n0, 0.0);
|
|
7020
|
+
preparedTriangle.n1 = vec4<f32>(n1, 0.0);
|
|
7021
|
+
preparedTriangle.n2 = vec4<f32>(n2, 0.0);
|
|
7022
|
+
preparedTriangle.uv0uv1 = vec4<f32>(uv0, uv1);
|
|
7023
|
+
preparedTriangle.uv2Pad = vec4<f32>(uv2, 0.0, 0.0);
|
|
7024
|
+
preparedTriangle.color = mesh.color;
|
|
7025
|
+
preparedTriangle.emission = mesh.emission;
|
|
7026
|
+
preparedTriangle.material = mesh.material;
|
|
7027
|
+
preparedTriangle.materialResponse = mesh.materialResponse;
|
|
7028
|
+
preparedTriangle.materialExtension = mesh.materialExtension;
|
|
7029
|
+
preparedTriangle.specularColor = mesh.specularColor;
|
|
7030
|
+
preparedTriangle.baseColorAtlas = mesh.baseColorAtlas;
|
|
7031
|
+
preparedTriangle.metallicRoughnessAtlas = mesh.metallicRoughnessAtlas;
|
|
7032
|
+
preparedTriangle.normalAtlas = mesh.normalAtlas;
|
|
7033
|
+
preparedTriangle.occlusionAtlas = mesh.occlusionAtlas;
|
|
7034
|
+
preparedTriangle.emissiveAtlas = mesh.emissiveAtlas;
|
|
7035
|
+
preparedTriangle.textureSettings = mesh.textureSettings;
|
|
7036
|
+
preparedTriangle.materialExtension2 = vec4<f32>(0.0, 0.0, 1.3, 0.0);
|
|
7037
|
+
preparedTriangle.materialExtension3 = vec4<f32>(100.0, 400.0, 0.0, 0.0);
|
|
7038
|
+
preparedTriangle.clearcoatAtlas = vec4<f32>(0.0, 0.0, 1.0, 1.0);
|
|
7039
|
+
preparedTriangle.clearcoatRoughnessAtlas = vec4<f32>(0.0, 0.0, 1.0, 1.0);
|
|
7040
|
+
preparedTriangle.clearcoatNormalAtlas = vec4<f32>(0.0, 0.0, 1.0, 1.0);
|
|
7041
|
+
preparedTriangle.transmissionAtlas = vec4<f32>(0.0, 0.0, 1.0, 1.0);
|
|
7042
|
+
preparedTriangle.thicknessAtlas = vec4<f32>(0.0, 0.0, 1.0, 1.0);
|
|
7043
|
+
preparedTriangle.sheenColorAtlas = vec4<f32>(0.0, 0.0, 1.0, 1.0);
|
|
7044
|
+
preparedTriangle.sheenRoughnessAtlas = vec4<f32>(0.0, 0.0, 1.0, 1.0);
|
|
7045
|
+
preparedTriangle.specularAtlas = vec4<f32>(0.0, 0.0, 1.0, 1.0);
|
|
7046
|
+
preparedTriangle.specularColorAtlas = vec4<f32>(0.0, 0.0, 1.0, 1.0);
|
|
7047
|
+
preparedTriangle.iridescenceAtlas = vec4<f32>(0.0, 0.0, 1.0, 1.0);
|
|
7048
|
+
preparedTriangle.iridescenceThicknessAtlas = vec4<f32>(0.0, 0.0, 1.0, 1.0);
|
|
7049
|
+
preparedTriangle.anisotropyAtlas = vec4<f32>(0.0, 0.0, 1.0, 1.0);
|
|
7050
|
+
triangles[triangleIndex] = preparedTriangle;
|
|
6628
7051
|
|
|
6629
7052
|
let leafBase = config.triangleCount - 1u;
|
|
6630
7053
|
let nodeIndex = leafBase + triangleIndex;
|
|
@@ -7294,36 +7717,7 @@ fn intersectActiveQueue(@builtin(global_invocation_id) globalId: vec3<u32>) {
|
|
|
7294
7717
|
vec4<f32>(1.0, 1.0, 1.0, 1.0)
|
|
7295
7718
|
);
|
|
7296
7719
|
var candidate = no_candidate();
|
|
7297
|
-
var hitTriangle = TriangleRecord(
|
|
7298
|
-
0u,
|
|
7299
|
-
0u,
|
|
7300
|
-
0u,
|
|
7301
|
-
0u,
|
|
7302
|
-
0u,
|
|
7303
|
-
0u,
|
|
7304
|
-
0u,
|
|
7305
|
-
0u,
|
|
7306
|
-
vec4<f32>(0.0),
|
|
7307
|
-
vec4<f32>(0.0),
|
|
7308
|
-
vec4<f32>(0.0),
|
|
7309
|
-
vec4<f32>(0.0, 1.0, 0.0, 0.0),
|
|
7310
|
-
vec4<f32>(0.0, 1.0, 0.0, 0.0),
|
|
7311
|
-
vec4<f32>(0.0, 1.0, 0.0, 0.0),
|
|
7312
|
-
vec4<f32>(0.0),
|
|
7313
|
-
vec4<f32>(0.0),
|
|
7314
|
-
vec4<f32>(0.0),
|
|
7315
|
-
vec4<f32>(0.0),
|
|
7316
|
-
vec4<f32>(1.0, 0.0, 1.0, 1.0),
|
|
7317
|
-
vec4<f32>(0.0, 0.0, 0.0, 0.08),
|
|
7318
|
-
vec4<f32>(0.08, 1.0, 0.0, 0.0),
|
|
7319
|
-
vec4<f32>(1.0, 1.0, 1.0, 1.0),
|
|
7320
|
-
vec4<f32>(0.0, 0.0, 1.0, 1.0),
|
|
7321
|
-
vec4<f32>(0.0, 0.0, 1.0, 1.0),
|
|
7322
|
-
vec4<f32>(0.0, 0.0, 1.0, 1.0),
|
|
7323
|
-
vec4<f32>(0.0, 0.0, 1.0, 1.0),
|
|
7324
|
-
vec4<f32>(0.0, 0.0, 1.0, 1.0),
|
|
7325
|
-
vec4<f32>(1.0, 1.0, 1.0, 0.0)
|
|
7326
|
-
);
|
|
7720
|
+
var hitTriangle = TriangleRecord();
|
|
7327
7721
|
|
|
7328
7722
|
for (var objectIndex = 0u; objectIndex < config.sceneObjectCount; objectIndex = objectIndex + 1u) {
|
|
7329
7723
|
let object = sceneObjects[objectIndex];
|
|
@@ -10198,7 +10592,19 @@ function createWavefrontBindGroupLayouts(device, constants) {
|
|
|
10198
10592
|
{ binding: 29, visibility: constants.shader.COMPUTE, texture: { sampleType: "float" } },
|
|
10199
10593
|
{ binding: 30, visibility: constants.shader.COMPUTE, sampler: { type: "filtering" } },
|
|
10200
10594
|
{ binding: 31, visibility: constants.shader.COMPUTE, texture: { sampleType: "float" } },
|
|
10201
|
-
{ binding: 32, visibility: constants.shader.COMPUTE, texture: { sampleType: "float" } }
|
|
10595
|
+
{ binding: 32, visibility: constants.shader.COMPUTE, texture: { sampleType: "float" } },
|
|
10596
|
+
{ binding: 33, visibility: constants.shader.COMPUTE, texture: { sampleType: "float" } },
|
|
10597
|
+
{ binding: 34, visibility: constants.shader.COMPUTE, texture: { sampleType: "float" } },
|
|
10598
|
+
{ binding: 35, visibility: constants.shader.COMPUTE, texture: { sampleType: "float" } },
|
|
10599
|
+
{ binding: 36, visibility: constants.shader.COMPUTE, texture: { sampleType: "float" } },
|
|
10600
|
+
{ binding: 37, visibility: constants.shader.COMPUTE, texture: { sampleType: "float" } },
|
|
10601
|
+
{ binding: 38, visibility: constants.shader.COMPUTE, texture: { sampleType: "float" } },
|
|
10602
|
+
{ binding: 39, visibility: constants.shader.COMPUTE, texture: { sampleType: "float" } },
|
|
10603
|
+
{ binding: 40, visibility: constants.shader.COMPUTE, texture: { sampleType: "float" } },
|
|
10604
|
+
{ binding: 41, visibility: constants.shader.COMPUTE, texture: { sampleType: "float" } },
|
|
10605
|
+
{ binding: 42, visibility: constants.shader.COMPUTE, texture: { sampleType: "float" } },
|
|
10606
|
+
{ binding: 43, visibility: constants.shader.COMPUTE, texture: { sampleType: "float" } },
|
|
10607
|
+
{ binding: 44, visibility: constants.shader.COMPUTE, texture: { sampleType: "float" } }
|
|
10202
10608
|
]
|
|
10203
10609
|
});
|
|
10204
10610
|
const acceleration = device.createBindGroupLayout({
|
|
@@ -11186,6 +11592,8 @@ async function createWavefrontPathTracingComputeRenderer(options = {}) {
|
|
|
11186
11592
|
outputView,
|
|
11187
11593
|
sampler
|
|
11188
11594
|
});
|
|
11595
|
+
let activeFrameTelemetry = null;
|
|
11596
|
+
let frameTelemetryResources = null;
|
|
11189
11597
|
const frameEncoder = createWavefrontFrameEncoder({
|
|
11190
11598
|
getConfig: () => config,
|
|
11191
11599
|
getBindGroups: () => bindGroups,
|
|
@@ -11197,7 +11605,8 @@ async function createWavefrontPathTracingComputeRenderer(options = {}) {
|
|
|
11197
11605
|
denoiseDirectResolveBindGroup,
|
|
11198
11606
|
presentPipeline,
|
|
11199
11607
|
presentBindGroup,
|
|
11200
|
-
context
|
|
11608
|
+
context,
|
|
11609
|
+
getFrameTelemetry: () => activeFrameTelemetry
|
|
11201
11610
|
});
|
|
11202
11611
|
let frame = 0;
|
|
11203
11612
|
let accelerationBuilt = !config.gpuAccelerationBuildRequired;
|
|
@@ -11210,6 +11619,16 @@ async function createWavefrontPathTracingComputeRenderer(options = {}) {
|
|
|
11210
11619
|
createGpuParallelismCounters()
|
|
11211
11620
|
);
|
|
11212
11621
|
const waitForSubmittedGpuWork2 = (waitOptions = {}) => waitForSubmittedGpuWork(device, waitOptions);
|
|
11622
|
+
function ensureFrameTelemetryResources() {
|
|
11623
|
+
if (!frameTelemetryResources) {
|
|
11624
|
+
frameTelemetryResources = createWavefrontFrameTelemetryResources({
|
|
11625
|
+
device,
|
|
11626
|
+
constants,
|
|
11627
|
+
maxRayCountRecords: tiles.length * config.samplesPerPixel * config.maxDepth
|
|
11628
|
+
});
|
|
11629
|
+
}
|
|
11630
|
+
return frameTelemetryResources;
|
|
11631
|
+
}
|
|
11213
11632
|
function resolveRenderedSamplesPerPixel(renderOptions = {}, awaitGPUCompletion = true) {
|
|
11214
11633
|
return resolveWavefrontRenderedSamplesPerPixel({
|
|
11215
11634
|
config,
|
|
@@ -11337,7 +11756,13 @@ async function createWavefrontPathTracingComputeRenderer(options = {}) {
|
|
|
11337
11756
|
frameSubmissionCount + (accelerationBuildSubmitted ? 1 : 0),
|
|
11338
11757
|
frameTimeMs,
|
|
11339
11758
|
false
|
|
11340
|
-
)
|
|
11759
|
+
),
|
|
11760
|
+
timings: createWavefrontFrameTimingTelemetry({
|
|
11761
|
+
status: "not-requested",
|
|
11762
|
+
source: "cpu-submit",
|
|
11763
|
+
timestampQueryStatus: "not-recorded",
|
|
11764
|
+
totalRenderJobTimeMs: frameTimeMs
|
|
11765
|
+
})
|
|
11341
11766
|
});
|
|
11342
11767
|
}
|
|
11343
11768
|
function dispatchFrameAwaitingGpu(frameIndex, parallelism, renderedSamplesPerPixel = config.samplesPerPixel, optionsForFrame = {}) {
|
|
@@ -11367,98 +11792,151 @@ async function createWavefrontPathTracingComputeRenderer(options = {}) {
|
|
|
11367
11792
|
const awaitGPUCompletion = renderOptions.awaitGPUCompletion !== false;
|
|
11368
11793
|
const samplingPlan = resolveRenderedSamplesPerPixel(renderOptions, awaitGPUCompletion);
|
|
11369
11794
|
const useThrottledHighSamplePath = awaitGPUCompletion && samplingPlan.renderedSamplesPerPixel >= 8;
|
|
11795
|
+
const telemetryRequested = renderOptions.readStats === true;
|
|
11796
|
+
const telemetryCanRecord = telemetryRequested && awaitGPUCompletion;
|
|
11797
|
+
const telemetryResourcesForFrame = telemetryCanRecord ? ensureFrameTelemetryResources() : null;
|
|
11798
|
+
if (telemetryResourcesForFrame?.available) {
|
|
11799
|
+
telemetryResourcesForFrame.beginFrame();
|
|
11800
|
+
activeFrameTelemetry = telemetryResourcesForFrame;
|
|
11801
|
+
}
|
|
11370
11802
|
const frameStartTimeMs = nowMs3();
|
|
11371
11803
|
let frameStats;
|
|
11372
|
-
|
|
11373
|
-
|
|
11374
|
-
|
|
11375
|
-
|
|
11376
|
-
|
|
11377
|
-
|
|
11378
|
-
|
|
11379
|
-
|
|
11380
|
-
|
|
11381
|
-
|
|
11382
|
-
|
|
11383
|
-
|
|
11384
|
-
|
|
11385
|
-
|
|
11386
|
-
|
|
11387
|
-
|
|
11388
|
-
|
|
11389
|
-
|
|
11390
|
-
|
|
11391
|
-
|
|
11392
|
-
|
|
11393
|
-
|
|
11394
|
-
|
|
11395
|
-
|
|
11396
|
-
|
|
11397
|
-
sampleRangeStart: 0,
|
|
11398
|
-
sampleRangeEnd: samplingPlan.renderedSamplesPerPixel,
|
|
11399
|
-
tileStartIndex: tileIndex,
|
|
11400
|
-
tileEndIndex: tileIndex + 1,
|
|
11401
|
-
startingSubmissionCount: frameSubmissionCount,
|
|
11402
|
-
startingSlot: frameConfigSlot,
|
|
11403
|
-
includeDenoise: tileIndex + 1 >= tiles.length,
|
|
11404
|
-
includePresent: tileIndex + 1 >= tiles.length
|
|
11405
|
-
}
|
|
11406
|
-
);
|
|
11407
|
-
frameSubmissionCount = tileRangeDispatch.submissionCount;
|
|
11408
|
-
frameConfigSlot = tileRangeDispatch.slot;
|
|
11409
|
-
const tileWaitOptions = {
|
|
11410
|
-
...estimateSubmittedGpuWorkTiming(
|
|
11411
|
-
{ ...config, renderedSamplesPerPixel: samplingPlan.renderedSamplesPerPixel },
|
|
11412
|
-
1,
|
|
11413
|
-
renderOptions.submittedWorkTimeoutMs,
|
|
11804
|
+
try {
|
|
11805
|
+
if (useThrottledHighSamplePath) {
|
|
11806
|
+
frame += 1;
|
|
11807
|
+
const frameIndex = frame + config.frameIndex;
|
|
11808
|
+
const parallelismCounters = createGpuParallelismCounters();
|
|
11809
|
+
const accelerationBuildSubmitted = dispatchGpuAccelerationBuild(frameIndex, parallelismCounters);
|
|
11810
|
+
let frameSubmissionCount = 0;
|
|
11811
|
+
let frameConfigSlot = 0;
|
|
11812
|
+
if (accelerationBuildSubmitted) {
|
|
11813
|
+
const accelerationWaitOptions = {
|
|
11814
|
+
...estimateSubmittedGpuWorkTiming(
|
|
11815
|
+
{ ...config, renderedSamplesPerPixel: 1 },
|
|
11816
|
+
1,
|
|
11817
|
+
renderOptions.submittedWorkTimeoutMs,
|
|
11818
|
+
{ includeAccelerationBuild: true }
|
|
11819
|
+
),
|
|
11820
|
+
allowTimeout: false
|
|
11821
|
+
};
|
|
11822
|
+
await waitForSubmittedGpuWork2(accelerationWaitOptions);
|
|
11823
|
+
}
|
|
11824
|
+
for (let tileIndex = 0; tileIndex < tiles.length; tileIndex += 1) {
|
|
11825
|
+
const tileRangeDispatch = dispatchFrameAwaitingGpu(
|
|
11826
|
+
frameIndex,
|
|
11827
|
+
parallelismCounters,
|
|
11828
|
+
samplingPlan.renderedSamplesPerPixel,
|
|
11414
11829
|
{
|
|
11415
|
-
|
|
11830
|
+
sampleRangeStart: 0,
|
|
11831
|
+
sampleRangeEnd: samplingPlan.renderedSamplesPerPixel,
|
|
11832
|
+
tileStartIndex: tileIndex,
|
|
11833
|
+
tileEndIndex: tileIndex + 1,
|
|
11834
|
+
startingSubmissionCount: frameSubmissionCount,
|
|
11835
|
+
startingSlot: frameConfigSlot,
|
|
11836
|
+
includeDenoise: tileIndex + 1 >= tiles.length,
|
|
11416
11837
|
includePresent: tileIndex + 1 >= tiles.length
|
|
11417
11838
|
}
|
|
11418
|
-
)
|
|
11839
|
+
);
|
|
11840
|
+
frameSubmissionCount = tileRangeDispatch.submissionCount;
|
|
11841
|
+
frameConfigSlot = tileRangeDispatch.slot;
|
|
11842
|
+
const tileWaitOptions = {
|
|
11843
|
+
...estimateSubmittedGpuWorkTiming(
|
|
11844
|
+
{ ...config, renderedSamplesPerPixel: samplingPlan.renderedSamplesPerPixel },
|
|
11845
|
+
1,
|
|
11846
|
+
renderOptions.submittedWorkTimeoutMs,
|
|
11847
|
+
{
|
|
11848
|
+
includeDenoise: tileIndex + 1 >= tiles.length && config.denoise,
|
|
11849
|
+
includePresent: tileIndex + 1 >= tiles.length
|
|
11850
|
+
}
|
|
11851
|
+
),
|
|
11852
|
+
allowTimeout: false
|
|
11853
|
+
};
|
|
11854
|
+
await waitForSubmittedGpuWork2(tileWaitOptions);
|
|
11855
|
+
}
|
|
11856
|
+
frameStats = createFrameStats({
|
|
11857
|
+
frameIndex,
|
|
11858
|
+
accelerationBuildSubmitted,
|
|
11859
|
+
frameSubmissionCount,
|
|
11860
|
+
parallelismCounters,
|
|
11861
|
+
renderedSamplesPerPixel: samplingPlan.renderedSamplesPerPixel,
|
|
11862
|
+
targetSamplesPerPixel: samplingPlan.targetSamplesPerPixel,
|
|
11863
|
+
frameTimeBudgetMs: samplingPlan.frameTimeBudgetMs,
|
|
11864
|
+
budgetConstrained: samplingPlan.budgetConstrained
|
|
11865
|
+
});
|
|
11866
|
+
} else {
|
|
11867
|
+
const submittedWorkTiming = estimateSubmittedGpuWorkTiming(
|
|
11868
|
+
{ ...config, renderedSamplesPerPixel: samplingPlan.renderedSamplesPerPixel },
|
|
11869
|
+
tiles.length,
|
|
11870
|
+
renderOptions.submittedWorkTimeoutMs,
|
|
11871
|
+
{ includeAccelerationBuild: config.gpuAccelerationBuildRequired && !accelerationBuilt }
|
|
11872
|
+
);
|
|
11873
|
+
const submissionWaitOptions = awaitGPUCompletion ? {
|
|
11874
|
+
timeoutMs: submittedWorkTiming.timeoutMs,
|
|
11875
|
+
maxWaitMs: submittedWorkTiming.maxWaitMs,
|
|
11419
11876
|
allowTimeout: false
|
|
11877
|
+
} : {
|
|
11878
|
+
timeoutMs: submittedWorkTiming.timeoutMs,
|
|
11879
|
+
maxWaitMs: submittedWorkTiming.maxWaitMs
|
|
11420
11880
|
};
|
|
11421
|
-
|
|
11422
|
-
|
|
11423
|
-
|
|
11424
|
-
|
|
11425
|
-
accelerationBuildSubmitted,
|
|
11426
|
-
frameSubmissionCount,
|
|
11427
|
-
parallelismCounters,
|
|
11428
|
-
renderedSamplesPerPixel: samplingPlan.renderedSamplesPerPixel,
|
|
11429
|
-
targetSamplesPerPixel: samplingPlan.targetSamplesPerPixel,
|
|
11430
|
-
frameTimeBudgetMs: samplingPlan.frameTimeBudgetMs,
|
|
11431
|
-
budgetConstrained: samplingPlan.budgetConstrained
|
|
11432
|
-
});
|
|
11433
|
-
} else {
|
|
11434
|
-
const submittedWorkTiming = estimateSubmittedGpuWorkTiming(
|
|
11435
|
-
{ ...config, renderedSamplesPerPixel: samplingPlan.renderedSamplesPerPixel },
|
|
11436
|
-
tiles.length,
|
|
11437
|
-
renderOptions.submittedWorkTimeoutMs,
|
|
11438
|
-
{ includeAccelerationBuild: config.gpuAccelerationBuildRequired && !accelerationBuilt }
|
|
11439
|
-
);
|
|
11440
|
-
const submissionWaitOptions = awaitGPUCompletion ? {
|
|
11441
|
-
timeoutMs: submittedWorkTiming.timeoutMs,
|
|
11442
|
-
maxWaitMs: submittedWorkTiming.maxWaitMs,
|
|
11443
|
-
allowTimeout: false
|
|
11444
|
-
} : {
|
|
11445
|
-
timeoutMs: submittedWorkTiming.timeoutMs,
|
|
11446
|
-
maxWaitMs: submittedWorkTiming.maxWaitMs
|
|
11447
|
-
};
|
|
11448
|
-
frameStats = renderOnce(renderOptions, samplingPlan);
|
|
11449
|
-
if (awaitGPUCompletion) {
|
|
11450
|
-
await waitForSubmittedGpuWork2(submissionWaitOptions);
|
|
11881
|
+
frameStats = renderOnce(renderOptions, samplingPlan);
|
|
11882
|
+
if (awaitGPUCompletion) {
|
|
11883
|
+
await waitForSubmittedGpuWork2(submissionWaitOptions);
|
|
11884
|
+
}
|
|
11451
11885
|
}
|
|
11886
|
+
} finally {
|
|
11887
|
+
activeFrameTelemetry = null;
|
|
11452
11888
|
}
|
|
11453
11889
|
const frameTimeMs = Math.max(0, nowMs3() - frameStartTimeMs);
|
|
11454
11890
|
if (awaitGPUCompletion) {
|
|
11455
11891
|
lastCompletedFrameTimeMs = frameTimeMs;
|
|
11456
11892
|
lastCompletedSamplesPerPixel = frameStats.renderedSamplesPerPixel ?? frameStats.samplesPerPixel;
|
|
11457
11893
|
}
|
|
11894
|
+
const expectedPrimaryRays = config.width * config.height * samplingPlan.renderedSamplesPerPixel;
|
|
11895
|
+
const expectedRayCounts = tiles.length * samplingPlan.renderedSamplesPerPixel * config.maxDepth;
|
|
11896
|
+
const telemetryReadback = telemetryCanRecord ? await telemetryResourcesForFrame.readFrame({
|
|
11897
|
+
expectedPrimaryRays,
|
|
11898
|
+
expectedRayCounts,
|
|
11899
|
+
waitForSubmittedGpuWork: waitForSubmittedGpuWork2
|
|
11900
|
+
}) : null;
|
|
11901
|
+
const rayCounts = !telemetryRequested ? createWavefrontRayCountTelemetry() : !awaitGPUCompletion ? createWavefrontRayCountTelemetry({
|
|
11902
|
+
status: "unavailable",
|
|
11903
|
+
expectedPrimaryRays,
|
|
11904
|
+
expectedRayCounts,
|
|
11905
|
+
reason: "gpu-work-not-awaited"
|
|
11906
|
+
}) : telemetryReadback.rayCounts;
|
|
11907
|
+
const timings = !telemetryRequested ? createWavefrontFrameTimingTelemetry({
|
|
11908
|
+
status: "not-requested",
|
|
11909
|
+
source: awaitGPUCompletion ? "queue-completion" : "cpu-submit",
|
|
11910
|
+
timestampQueryStatus: "not-recorded",
|
|
11911
|
+
totalRenderJobTimeMs: frameTimeMs
|
|
11912
|
+
}) : !awaitGPUCompletion ? createWavefrontFrameTimingTelemetry({
|
|
11913
|
+
status: "unavailable",
|
|
11914
|
+
source: "cpu-submit",
|
|
11915
|
+
timestampQueryStatus: "not-recorded",
|
|
11916
|
+
totalRenderJobTimeMs: frameTimeMs,
|
|
11917
|
+
reason: "gpu-work-not-awaited"
|
|
11918
|
+
}) : telemetryReadback.timestampQueryStatus === "available" ? createWavefrontFrameTimingTelemetry({
|
|
11919
|
+
status: "available",
|
|
11920
|
+
source: "timestamp-query",
|
|
11921
|
+
timestampQueryStatus: "available",
|
|
11922
|
+
totalGpuTimeMs: telemetryReadback.totalGpuTimeMs,
|
|
11923
|
+
totalRenderJobTimeMs: frameTimeMs
|
|
11924
|
+
}) : createWavefrontFrameTimingTelemetry({
|
|
11925
|
+
status: "fallback",
|
|
11926
|
+
source: "queue-completion",
|
|
11927
|
+
timestampQueryStatus: telemetryReadback.timestampQueryStatus,
|
|
11928
|
+
totalRenderJobTimeMs: frameTimeMs,
|
|
11929
|
+
reason: telemetryReadback.reason
|
|
11930
|
+
});
|
|
11458
11931
|
const deviceLossStatus = awaitGPUCompletion ? "not-detected" : typeof device.lost?.then === "function" ? "pending" : "not-exposed";
|
|
11459
11932
|
frameStats = Object.freeze({
|
|
11460
11933
|
...frameStats,
|
|
11461
11934
|
deviceLossStatus,
|
|
11935
|
+
secondaryRays: rayCounts.status === "available" ? rayCounts.secondaryRays : null,
|
|
11936
|
+
totalPathSegments: rayCounts.status === "available" ? rayCounts.totalPathSegments : null,
|
|
11937
|
+
rayCounts,
|
|
11938
|
+
timings,
|
|
11939
|
+
telemetryMemoryBytes: telemetryResourcesForFrame?.memoryBytes ?? 0,
|
|
11462
11940
|
gpuWorkerJobs: createGpuWorkerJobDiagnostics(
|
|
11463
11941
|
frameStats.gpuParallelism,
|
|
11464
11942
|
frameStats.commandSubmissions,
|
|
@@ -11567,6 +12045,7 @@ async function createWavefrontPathTracingComputeRenderer(options = {}) {
|
|
|
11567
12045
|
});
|
|
11568
12046
|
}
|
|
11569
12047
|
function destroy() {
|
|
12048
|
+
frameTelemetryResources?.destroy?.();
|
|
11570
12049
|
activeQueue.destroy?.();
|
|
11571
12050
|
nextQueue.destroy?.();
|
|
11572
12051
|
hitBuffer.destroy?.();
|