@plasius/gpu-renderer 0.2.40 → 0.2.42
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 +41 -0
- package/README.md +44 -1
- package/dist/index.cjs +630 -147
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +630 -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 +415 -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,376 @@ 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
|
+
let maximumBounceIndex = 0;
|
|
6349
|
+
for (const bounceIndex of bounceIndices) {
|
|
6350
|
+
maximumBounceIndex = Math.max(maximumBounceIndex, bounceIndex);
|
|
6351
|
+
}
|
|
6352
|
+
const bounceHistogram = Array.from({ length: maximumBounceIndex + 1 }, () => 0);
|
|
6353
|
+
let observedPrimaryRays = 0;
|
|
6354
|
+
let secondaryRays = 0;
|
|
6355
|
+
let totalPathSegments = 0;
|
|
6356
|
+
for (let index = 0; index < rayCounts.length; index += 1) {
|
|
6357
|
+
const count = Number(rayCounts[index] ?? 0);
|
|
6358
|
+
const bounceIndex = bounceIndices[index] ?? 0;
|
|
6359
|
+
bounceHistogram[bounceIndex] = (bounceHistogram[bounceIndex] ?? 0) + count;
|
|
6360
|
+
totalPathSegments += count;
|
|
6361
|
+
if (bounceIndex === 0) {
|
|
6362
|
+
observedPrimaryRays += count;
|
|
6363
|
+
} else {
|
|
6364
|
+
secondaryRays += count;
|
|
6365
|
+
}
|
|
6366
|
+
}
|
|
6367
|
+
const primaryCountsMatch = observedPrimaryRays === expectedPrimaryRays;
|
|
6368
|
+
const rayTelemetry = freezeRayCountTelemetry({
|
|
6369
|
+
status: primaryCountsMatch ? "available" : "failed",
|
|
6370
|
+
source: "gpu-active-queue-readback",
|
|
6371
|
+
expectedPrimaryRays,
|
|
6372
|
+
observedPrimaryRays,
|
|
6373
|
+
secondaryRays: primaryCountsMatch ? secondaryRays : null,
|
|
6374
|
+
totalPathSegments: primaryCountsMatch ? totalPathSegments : null,
|
|
6375
|
+
bounceHistogram,
|
|
6376
|
+
capturedRayCounts: recordCount,
|
|
6377
|
+
expectedRayCounts,
|
|
6378
|
+
reason: primaryCountsMatch ? null : `primary-ray-count-mismatch:${observedPrimaryRays}/${expectedPrimaryRays}`
|
|
6379
|
+
});
|
|
6380
|
+
let timestampQueryStatus = timestampQuerySet ? "failed" : timestampUnavailableStatus;
|
|
6381
|
+
let totalGpuTimeMs = null;
|
|
6382
|
+
let timestampReason = timestampSetupReason;
|
|
6383
|
+
if (canResolveTimestamps) {
|
|
6384
|
+
try {
|
|
6385
|
+
await timestampReadbackBuffer.mapAsync(mapMode.READ);
|
|
6386
|
+
timestampReadbackMapped = true;
|
|
6387
|
+
const timestampCopy = timestampReadbackBuffer.getMappedRange().slice(0, TIMESTAMP_BUFFER_BYTES);
|
|
6388
|
+
const timestamps = new BigUint64Array(timestampCopy);
|
|
6389
|
+
timestampReadbackBuffer.unmap();
|
|
6390
|
+
timestampReadbackMapped = false;
|
|
6391
|
+
totalGpuTimeMs = timestampDurationMs(timestamps);
|
|
6392
|
+
timestampQueryStatus = totalGpuTimeMs === null ? "failed" : "available";
|
|
6393
|
+
timestampReason = totalGpuTimeMs === null ? "timestamp-query-returned-invalid-range" : null;
|
|
6394
|
+
} catch (error) {
|
|
6395
|
+
if (timestampReadbackMapped) {
|
|
6396
|
+
timestampReadbackBuffer.unmap();
|
|
6397
|
+
timestampReadbackMapped = false;
|
|
6398
|
+
}
|
|
6399
|
+
timestampQueryStatus = "failed";
|
|
6400
|
+
timestampReason = `timestamp-query-readback-failed:${safeErrorReason(error)}`;
|
|
6401
|
+
}
|
|
6402
|
+
} else if (timestampQuerySet) {
|
|
6403
|
+
timestampReason = "timestamp-query-span-incomplete";
|
|
6404
|
+
}
|
|
6405
|
+
return Object.freeze({
|
|
6406
|
+
rayCounts: rayTelemetry,
|
|
6407
|
+
timestampQueryStatus,
|
|
6408
|
+
totalGpuTimeMs,
|
|
6409
|
+
reason: timestampReason
|
|
6410
|
+
});
|
|
6411
|
+
} catch (error) {
|
|
6412
|
+
if (rayReadbackMapped) {
|
|
6413
|
+
rayCountReadbackBuffer.unmap();
|
|
6414
|
+
rayReadbackMapped = false;
|
|
6415
|
+
}
|
|
6416
|
+
if (timestampReadbackMapped) {
|
|
6417
|
+
timestampReadbackBuffer?.unmap();
|
|
6418
|
+
timestampReadbackMapped = false;
|
|
6419
|
+
}
|
|
6420
|
+
const reason = safeErrorReason(error);
|
|
6421
|
+
return Object.freeze({
|
|
6422
|
+
rayCounts: freezeRayCountTelemetry({
|
|
6423
|
+
status: "failed",
|
|
6424
|
+
expectedPrimaryRays,
|
|
6425
|
+
capturedRayCounts: recordCount,
|
|
6426
|
+
expectedRayCounts,
|
|
6427
|
+
reason: `ray-count-readback-failed:${reason}`
|
|
6428
|
+
}),
|
|
6429
|
+
timestampQueryStatus: timestampQuerySet ? "failed" : timestampUnavailableStatus,
|
|
6430
|
+
totalGpuTimeMs: null,
|
|
6431
|
+
reason: `telemetry-readback-failed:${reason}`
|
|
6432
|
+
});
|
|
6433
|
+
}
|
|
6434
|
+
}
|
|
6435
|
+
function destroy() {
|
|
6436
|
+
active = false;
|
|
6437
|
+
rayCountBuffer.destroy?.();
|
|
6438
|
+
rayCountReadbackBuffer.destroy?.();
|
|
6439
|
+
timestampQuerySet?.destroy?.();
|
|
6440
|
+
timestampResolveBuffer?.destroy?.();
|
|
6441
|
+
timestampReadbackBuffer?.destroy?.();
|
|
6442
|
+
}
|
|
6443
|
+
return Object.freeze({
|
|
6444
|
+
available: true,
|
|
6445
|
+
reason: null,
|
|
6446
|
+
memoryBytes,
|
|
6447
|
+
beginFrame,
|
|
6448
|
+
recordActiveRayCount,
|
|
6449
|
+
decorateFirstPass,
|
|
6450
|
+
decorateFinalPass,
|
|
6451
|
+
readFrame,
|
|
6452
|
+
destroy
|
|
6453
|
+
});
|
|
6454
|
+
}
|
|
6455
|
+
|
|
6055
6456
|
// src/wavefront-frame-stats.js
|
|
6056
6457
|
function resolveWavefrontRenderedSamplesPerPixel({
|
|
6057
6458
|
config,
|
|
@@ -6149,6 +6550,11 @@ function createWavefrontFrameStats({
|
|
|
6149
6550
|
maxFramePassesPerSubmission: config.maxFramePassesPerSubmission,
|
|
6150
6551
|
screenRays: config.width * config.height,
|
|
6151
6552
|
primaryRays: config.width * config.height * renderedSamplesPerPixel,
|
|
6553
|
+
secondaryRays: null,
|
|
6554
|
+
totalPathSegments: null,
|
|
6555
|
+
rayCounts: createWavefrontRayCountTelemetry(),
|
|
6556
|
+
timings: createWavefrontFrameTimingTelemetry(),
|
|
6557
|
+
telemetryMemoryBytes: 0,
|
|
6152
6558
|
sceneObjectCount: config.sceneObjectCount,
|
|
6153
6559
|
triangleCount: config.triangleCount,
|
|
6154
6560
|
emissiveTriangleCount: config.emissiveTriangleCount,
|
|
@@ -6191,7 +6597,8 @@ function createWavefrontFrameEncoder({
|
|
|
6191
6597
|
denoiseDirectResolveBindGroup,
|
|
6192
6598
|
presentPipeline,
|
|
6193
6599
|
presentBindGroup,
|
|
6194
|
-
context
|
|
6600
|
+
context,
|
|
6601
|
+
getFrameTelemetry = () => null
|
|
6195
6602
|
}) {
|
|
6196
6603
|
const resolveConfig = resolveGetter(getConfig);
|
|
6197
6604
|
const resolveBindGroups = resolveGetter(getBindGroups);
|
|
@@ -6199,9 +6606,12 @@ function createWavefrontFrameEncoder({
|
|
|
6199
6606
|
encodeTileSample(encoder, tile, configOffset, parallelism) {
|
|
6200
6607
|
const config = resolveConfig();
|
|
6201
6608
|
const bindGroups = resolveBindGroups();
|
|
6202
|
-
const
|
|
6203
|
-
|
|
6204
|
-
|
|
6609
|
+
const frameTelemetry = getFrameTelemetry();
|
|
6610
|
+
const generatePass = encoder.beginComputePass(
|
|
6611
|
+
frameTelemetry?.decorateFirstPass({
|
|
6612
|
+
label: "plasius.wavefront.generatePrimaryRaysPass"
|
|
6613
|
+
}) ?? { label: "plasius.wavefront.generatePrimaryRaysPass" }
|
|
6614
|
+
);
|
|
6205
6615
|
const tileWorkgroups = Math.ceil(tile.width * tile.height / WORKGROUP_SIZE);
|
|
6206
6616
|
generatePass.setBindGroup(0, bindGroups[0], [configOffset]);
|
|
6207
6617
|
generatePass.setPipeline(pipelines.generatePrimaryRays);
|
|
@@ -6209,6 +6619,7 @@ function createWavefrontFrameEncoder({
|
|
|
6209
6619
|
recordDirectDispatch(parallelism, [tileWorkgroups], WORKGROUP_SIZE);
|
|
6210
6620
|
generatePass.end();
|
|
6211
6621
|
for (let bounceIndex = 0; bounceIndex < config.maxDepth; bounceIndex += 1) {
|
|
6622
|
+
frameTelemetry?.recordActiveRayCount(encoder, counterBuffer, bounceIndex);
|
|
6212
6623
|
encoder.copyBufferToBuffer(
|
|
6213
6624
|
counterBuffer,
|
|
6214
6625
|
COUNTER_DISPATCH_ARGS_OFFSET,
|
|
@@ -6285,7 +6696,7 @@ function createWavefrontFrameEncoder({
|
|
|
6285
6696
|
},
|
|
6286
6697
|
encodePresent(encoder) {
|
|
6287
6698
|
const texture = context.getCurrentTexture();
|
|
6288
|
-
const
|
|
6699
|
+
const descriptor = {
|
|
6289
6700
|
label: "plasius.wavefront.presentPass",
|
|
6290
6701
|
colorAttachments: [
|
|
6291
6702
|
{
|
|
@@ -6295,7 +6706,10 @@ function createWavefrontFrameEncoder({
|
|
|
6295
6706
|
storeOp: "store"
|
|
6296
6707
|
}
|
|
6297
6708
|
]
|
|
6298
|
-
}
|
|
6709
|
+
};
|
|
6710
|
+
const passEncoder = encoder.beginRenderPass(
|
|
6711
|
+
getFrameTelemetry()?.decorateFinalPass(descriptor) ?? descriptor
|
|
6712
|
+
);
|
|
6299
6713
|
passEncoder.setPipeline(presentPipeline);
|
|
6300
6714
|
passEncoder.setBindGroup(0, presentBindGroup);
|
|
6301
6715
|
passEncoder.draw(3);
|
|
@@ -6595,36 +7009,49 @@ fn prepareMeshTrianglesAndLeaves(@builtin(global_invocation_id) globalId: vec3<u
|
|
|
6595
7009
|
let uv1 = select(vec2<f32>(0.0), vertex1.uv.xy, vertex1.uv.z > 0.5);
|
|
6596
7010
|
let uv2 = select(vec2<f32>(0.0), vertex2.uv.xy, vertex2.uv.z > 0.5);
|
|
6597
7011
|
|
|
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
|
-
);
|
|
7012
|
+
var preparedTriangle = TriangleRecord();
|
|
7013
|
+
preparedTriangle.triangleId = triangleIndex;
|
|
7014
|
+
preparedTriangle.meshId = mesh.meshId;
|
|
7015
|
+
preparedTriangle.materialKind = mesh.materialKind;
|
|
7016
|
+
preparedTriangle.flags = mesh.flags;
|
|
7017
|
+
preparedTriangle.materialRefId = mesh.materialRefId;
|
|
7018
|
+
preparedTriangle.mediumRefId = mesh.mediumRefId;
|
|
7019
|
+
preparedTriangle.materialSlot = mesh.materialSlot;
|
|
7020
|
+
preparedTriangle.v0 = vec4<f32>(vertex0.position.xyz, 0.0);
|
|
7021
|
+
preparedTriangle.v1 = vec4<f32>(vertex1.position.xyz, 0.0);
|
|
7022
|
+
preparedTriangle.v2 = vec4<f32>(vertex2.position.xyz, 0.0);
|
|
7023
|
+
preparedTriangle.n0 = vec4<f32>(n0, 0.0);
|
|
7024
|
+
preparedTriangle.n1 = vec4<f32>(n1, 0.0);
|
|
7025
|
+
preparedTriangle.n2 = vec4<f32>(n2, 0.0);
|
|
7026
|
+
preparedTriangle.uv0uv1 = vec4<f32>(uv0, uv1);
|
|
7027
|
+
preparedTriangle.uv2Pad = vec4<f32>(uv2, 0.0, 0.0);
|
|
7028
|
+
preparedTriangle.color = mesh.color;
|
|
7029
|
+
preparedTriangle.emission = mesh.emission;
|
|
7030
|
+
preparedTriangle.material = mesh.material;
|
|
7031
|
+
preparedTriangle.materialResponse = mesh.materialResponse;
|
|
7032
|
+
preparedTriangle.materialExtension = mesh.materialExtension;
|
|
7033
|
+
preparedTriangle.specularColor = mesh.specularColor;
|
|
7034
|
+
preparedTriangle.baseColorAtlas = mesh.baseColorAtlas;
|
|
7035
|
+
preparedTriangle.metallicRoughnessAtlas = mesh.metallicRoughnessAtlas;
|
|
7036
|
+
preparedTriangle.normalAtlas = mesh.normalAtlas;
|
|
7037
|
+
preparedTriangle.occlusionAtlas = mesh.occlusionAtlas;
|
|
7038
|
+
preparedTriangle.emissiveAtlas = mesh.emissiveAtlas;
|
|
7039
|
+
preparedTriangle.textureSettings = mesh.textureSettings;
|
|
7040
|
+
preparedTriangle.materialExtension2 = vec4<f32>(0.0, 0.0, 1.3, 0.0);
|
|
7041
|
+
preparedTriangle.materialExtension3 = vec4<f32>(100.0, 400.0, 0.0, 0.0);
|
|
7042
|
+
preparedTriangle.clearcoatAtlas = vec4<f32>(0.0, 0.0, 1.0, 1.0);
|
|
7043
|
+
preparedTriangle.clearcoatRoughnessAtlas = vec4<f32>(0.0, 0.0, 1.0, 1.0);
|
|
7044
|
+
preparedTriangle.clearcoatNormalAtlas = vec4<f32>(0.0, 0.0, 1.0, 1.0);
|
|
7045
|
+
preparedTriangle.transmissionAtlas = vec4<f32>(0.0, 0.0, 1.0, 1.0);
|
|
7046
|
+
preparedTriangle.thicknessAtlas = vec4<f32>(0.0, 0.0, 1.0, 1.0);
|
|
7047
|
+
preparedTriangle.sheenColorAtlas = vec4<f32>(0.0, 0.0, 1.0, 1.0);
|
|
7048
|
+
preparedTriangle.sheenRoughnessAtlas = vec4<f32>(0.0, 0.0, 1.0, 1.0);
|
|
7049
|
+
preparedTriangle.specularAtlas = vec4<f32>(0.0, 0.0, 1.0, 1.0);
|
|
7050
|
+
preparedTriangle.specularColorAtlas = vec4<f32>(0.0, 0.0, 1.0, 1.0);
|
|
7051
|
+
preparedTriangle.iridescenceAtlas = vec4<f32>(0.0, 0.0, 1.0, 1.0);
|
|
7052
|
+
preparedTriangle.iridescenceThicknessAtlas = vec4<f32>(0.0, 0.0, 1.0, 1.0);
|
|
7053
|
+
preparedTriangle.anisotropyAtlas = vec4<f32>(0.0, 0.0, 1.0, 1.0);
|
|
7054
|
+
triangles[triangleIndex] = preparedTriangle;
|
|
6628
7055
|
|
|
6629
7056
|
let leafBase = config.triangleCount - 1u;
|
|
6630
7057
|
let nodeIndex = leafBase + triangleIndex;
|
|
@@ -7294,36 +7721,7 @@ fn intersectActiveQueue(@builtin(global_invocation_id) globalId: vec3<u32>) {
|
|
|
7294
7721
|
vec4<f32>(1.0, 1.0, 1.0, 1.0)
|
|
7295
7722
|
);
|
|
7296
7723
|
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
|
-
);
|
|
7724
|
+
var hitTriangle = TriangleRecord();
|
|
7327
7725
|
|
|
7328
7726
|
for (var objectIndex = 0u; objectIndex < config.sceneObjectCount; objectIndex = objectIndex + 1u) {
|
|
7329
7727
|
let object = sceneObjects[objectIndex];
|
|
@@ -10198,7 +10596,19 @@ function createWavefrontBindGroupLayouts(device, constants) {
|
|
|
10198
10596
|
{ binding: 29, visibility: constants.shader.COMPUTE, texture: { sampleType: "float" } },
|
|
10199
10597
|
{ binding: 30, visibility: constants.shader.COMPUTE, sampler: { type: "filtering" } },
|
|
10200
10598
|
{ binding: 31, visibility: constants.shader.COMPUTE, texture: { sampleType: "float" } },
|
|
10201
|
-
{ binding: 32, visibility: constants.shader.COMPUTE, texture: { sampleType: "float" } }
|
|
10599
|
+
{ binding: 32, visibility: constants.shader.COMPUTE, texture: { sampleType: "float" } },
|
|
10600
|
+
{ binding: 33, visibility: constants.shader.COMPUTE, texture: { sampleType: "float" } },
|
|
10601
|
+
{ binding: 34, visibility: constants.shader.COMPUTE, texture: { sampleType: "float" } },
|
|
10602
|
+
{ binding: 35, visibility: constants.shader.COMPUTE, texture: { sampleType: "float" } },
|
|
10603
|
+
{ binding: 36, visibility: constants.shader.COMPUTE, texture: { sampleType: "float" } },
|
|
10604
|
+
{ binding: 37, visibility: constants.shader.COMPUTE, texture: { sampleType: "float" } },
|
|
10605
|
+
{ binding: 38, visibility: constants.shader.COMPUTE, texture: { sampleType: "float" } },
|
|
10606
|
+
{ binding: 39, visibility: constants.shader.COMPUTE, texture: { sampleType: "float" } },
|
|
10607
|
+
{ binding: 40, visibility: constants.shader.COMPUTE, texture: { sampleType: "float" } },
|
|
10608
|
+
{ binding: 41, visibility: constants.shader.COMPUTE, texture: { sampleType: "float" } },
|
|
10609
|
+
{ binding: 42, visibility: constants.shader.COMPUTE, texture: { sampleType: "float" } },
|
|
10610
|
+
{ binding: 43, visibility: constants.shader.COMPUTE, texture: { sampleType: "float" } },
|
|
10611
|
+
{ binding: 44, visibility: constants.shader.COMPUTE, texture: { sampleType: "float" } }
|
|
10202
10612
|
]
|
|
10203
10613
|
});
|
|
10204
10614
|
const acceleration = device.createBindGroupLayout({
|
|
@@ -11186,6 +11596,8 @@ async function createWavefrontPathTracingComputeRenderer(options = {}) {
|
|
|
11186
11596
|
outputView,
|
|
11187
11597
|
sampler
|
|
11188
11598
|
});
|
|
11599
|
+
let activeFrameTelemetry = null;
|
|
11600
|
+
let frameTelemetryResources = null;
|
|
11189
11601
|
const frameEncoder = createWavefrontFrameEncoder({
|
|
11190
11602
|
getConfig: () => config,
|
|
11191
11603
|
getBindGroups: () => bindGroups,
|
|
@@ -11197,7 +11609,8 @@ async function createWavefrontPathTracingComputeRenderer(options = {}) {
|
|
|
11197
11609
|
denoiseDirectResolveBindGroup,
|
|
11198
11610
|
presentPipeline,
|
|
11199
11611
|
presentBindGroup,
|
|
11200
|
-
context
|
|
11612
|
+
context,
|
|
11613
|
+
getFrameTelemetry: () => activeFrameTelemetry
|
|
11201
11614
|
});
|
|
11202
11615
|
let frame = 0;
|
|
11203
11616
|
let accelerationBuilt = !config.gpuAccelerationBuildRequired;
|
|
@@ -11210,6 +11623,16 @@ async function createWavefrontPathTracingComputeRenderer(options = {}) {
|
|
|
11210
11623
|
createGpuParallelismCounters()
|
|
11211
11624
|
);
|
|
11212
11625
|
const waitForSubmittedGpuWork2 = (waitOptions = {}) => waitForSubmittedGpuWork(device, waitOptions);
|
|
11626
|
+
function ensureFrameTelemetryResources() {
|
|
11627
|
+
if (!frameTelemetryResources) {
|
|
11628
|
+
frameTelemetryResources = createWavefrontFrameTelemetryResources({
|
|
11629
|
+
device,
|
|
11630
|
+
constants,
|
|
11631
|
+
maxRayCountRecords: tiles.length * config.samplesPerPixel * config.maxDepth
|
|
11632
|
+
});
|
|
11633
|
+
}
|
|
11634
|
+
return frameTelemetryResources;
|
|
11635
|
+
}
|
|
11213
11636
|
function resolveRenderedSamplesPerPixel(renderOptions = {}, awaitGPUCompletion = true) {
|
|
11214
11637
|
return resolveWavefrontRenderedSamplesPerPixel({
|
|
11215
11638
|
config,
|
|
@@ -11337,7 +11760,13 @@ async function createWavefrontPathTracingComputeRenderer(options = {}) {
|
|
|
11337
11760
|
frameSubmissionCount + (accelerationBuildSubmitted ? 1 : 0),
|
|
11338
11761
|
frameTimeMs,
|
|
11339
11762
|
false
|
|
11340
|
-
)
|
|
11763
|
+
),
|
|
11764
|
+
timings: createWavefrontFrameTimingTelemetry({
|
|
11765
|
+
status: "not-requested",
|
|
11766
|
+
source: "cpu-submit",
|
|
11767
|
+
timestampQueryStatus: "not-recorded",
|
|
11768
|
+
totalRenderJobTimeMs: frameTimeMs
|
|
11769
|
+
})
|
|
11341
11770
|
});
|
|
11342
11771
|
}
|
|
11343
11772
|
function dispatchFrameAwaitingGpu(frameIndex, parallelism, renderedSamplesPerPixel = config.samplesPerPixel, optionsForFrame = {}) {
|
|
@@ -11367,98 +11796,151 @@ async function createWavefrontPathTracingComputeRenderer(options = {}) {
|
|
|
11367
11796
|
const awaitGPUCompletion = renderOptions.awaitGPUCompletion !== false;
|
|
11368
11797
|
const samplingPlan = resolveRenderedSamplesPerPixel(renderOptions, awaitGPUCompletion);
|
|
11369
11798
|
const useThrottledHighSamplePath = awaitGPUCompletion && samplingPlan.renderedSamplesPerPixel >= 8;
|
|
11799
|
+
const telemetryRequested = renderOptions.readStats === true;
|
|
11800
|
+
const telemetryCanRecord = telemetryRequested && awaitGPUCompletion;
|
|
11801
|
+
const telemetryResourcesForFrame = telemetryCanRecord ? ensureFrameTelemetryResources() : null;
|
|
11802
|
+
if (telemetryResourcesForFrame?.available) {
|
|
11803
|
+
telemetryResourcesForFrame.beginFrame();
|
|
11804
|
+
activeFrameTelemetry = telemetryResourcesForFrame;
|
|
11805
|
+
}
|
|
11370
11806
|
const frameStartTimeMs = nowMs3();
|
|
11371
11807
|
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,
|
|
11808
|
+
try {
|
|
11809
|
+
if (useThrottledHighSamplePath) {
|
|
11810
|
+
frame += 1;
|
|
11811
|
+
const frameIndex = frame + config.frameIndex;
|
|
11812
|
+
const parallelismCounters = createGpuParallelismCounters();
|
|
11813
|
+
const accelerationBuildSubmitted = dispatchGpuAccelerationBuild(frameIndex, parallelismCounters);
|
|
11814
|
+
let frameSubmissionCount = 0;
|
|
11815
|
+
let frameConfigSlot = 0;
|
|
11816
|
+
if (accelerationBuildSubmitted) {
|
|
11817
|
+
const accelerationWaitOptions = {
|
|
11818
|
+
...estimateSubmittedGpuWorkTiming(
|
|
11819
|
+
{ ...config, renderedSamplesPerPixel: 1 },
|
|
11820
|
+
1,
|
|
11821
|
+
renderOptions.submittedWorkTimeoutMs,
|
|
11822
|
+
{ includeAccelerationBuild: true }
|
|
11823
|
+
),
|
|
11824
|
+
allowTimeout: false
|
|
11825
|
+
};
|
|
11826
|
+
await waitForSubmittedGpuWork2(accelerationWaitOptions);
|
|
11827
|
+
}
|
|
11828
|
+
for (let tileIndex = 0; tileIndex < tiles.length; tileIndex += 1) {
|
|
11829
|
+
const tileRangeDispatch = dispatchFrameAwaitingGpu(
|
|
11830
|
+
frameIndex,
|
|
11831
|
+
parallelismCounters,
|
|
11832
|
+
samplingPlan.renderedSamplesPerPixel,
|
|
11414
11833
|
{
|
|
11415
|
-
|
|
11834
|
+
sampleRangeStart: 0,
|
|
11835
|
+
sampleRangeEnd: samplingPlan.renderedSamplesPerPixel,
|
|
11836
|
+
tileStartIndex: tileIndex,
|
|
11837
|
+
tileEndIndex: tileIndex + 1,
|
|
11838
|
+
startingSubmissionCount: frameSubmissionCount,
|
|
11839
|
+
startingSlot: frameConfigSlot,
|
|
11840
|
+
includeDenoise: tileIndex + 1 >= tiles.length,
|
|
11416
11841
|
includePresent: tileIndex + 1 >= tiles.length
|
|
11417
11842
|
}
|
|
11418
|
-
)
|
|
11843
|
+
);
|
|
11844
|
+
frameSubmissionCount = tileRangeDispatch.submissionCount;
|
|
11845
|
+
frameConfigSlot = tileRangeDispatch.slot;
|
|
11846
|
+
const tileWaitOptions = {
|
|
11847
|
+
...estimateSubmittedGpuWorkTiming(
|
|
11848
|
+
{ ...config, renderedSamplesPerPixel: samplingPlan.renderedSamplesPerPixel },
|
|
11849
|
+
1,
|
|
11850
|
+
renderOptions.submittedWorkTimeoutMs,
|
|
11851
|
+
{
|
|
11852
|
+
includeDenoise: tileIndex + 1 >= tiles.length && config.denoise,
|
|
11853
|
+
includePresent: tileIndex + 1 >= tiles.length
|
|
11854
|
+
}
|
|
11855
|
+
),
|
|
11856
|
+
allowTimeout: false
|
|
11857
|
+
};
|
|
11858
|
+
await waitForSubmittedGpuWork2(tileWaitOptions);
|
|
11859
|
+
}
|
|
11860
|
+
frameStats = createFrameStats({
|
|
11861
|
+
frameIndex,
|
|
11862
|
+
accelerationBuildSubmitted,
|
|
11863
|
+
frameSubmissionCount,
|
|
11864
|
+
parallelismCounters,
|
|
11865
|
+
renderedSamplesPerPixel: samplingPlan.renderedSamplesPerPixel,
|
|
11866
|
+
targetSamplesPerPixel: samplingPlan.targetSamplesPerPixel,
|
|
11867
|
+
frameTimeBudgetMs: samplingPlan.frameTimeBudgetMs,
|
|
11868
|
+
budgetConstrained: samplingPlan.budgetConstrained
|
|
11869
|
+
});
|
|
11870
|
+
} else {
|
|
11871
|
+
const submittedWorkTiming = estimateSubmittedGpuWorkTiming(
|
|
11872
|
+
{ ...config, renderedSamplesPerPixel: samplingPlan.renderedSamplesPerPixel },
|
|
11873
|
+
tiles.length,
|
|
11874
|
+
renderOptions.submittedWorkTimeoutMs,
|
|
11875
|
+
{ includeAccelerationBuild: config.gpuAccelerationBuildRequired && !accelerationBuilt }
|
|
11876
|
+
);
|
|
11877
|
+
const submissionWaitOptions = awaitGPUCompletion ? {
|
|
11878
|
+
timeoutMs: submittedWorkTiming.timeoutMs,
|
|
11879
|
+
maxWaitMs: submittedWorkTiming.maxWaitMs,
|
|
11419
11880
|
allowTimeout: false
|
|
11881
|
+
} : {
|
|
11882
|
+
timeoutMs: submittedWorkTiming.timeoutMs,
|
|
11883
|
+
maxWaitMs: submittedWorkTiming.maxWaitMs
|
|
11420
11884
|
};
|
|
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);
|
|
11885
|
+
frameStats = renderOnce(renderOptions, samplingPlan);
|
|
11886
|
+
if (awaitGPUCompletion) {
|
|
11887
|
+
await waitForSubmittedGpuWork2(submissionWaitOptions);
|
|
11888
|
+
}
|
|
11451
11889
|
}
|
|
11890
|
+
} finally {
|
|
11891
|
+
activeFrameTelemetry = null;
|
|
11452
11892
|
}
|
|
11453
11893
|
const frameTimeMs = Math.max(0, nowMs3() - frameStartTimeMs);
|
|
11454
11894
|
if (awaitGPUCompletion) {
|
|
11455
11895
|
lastCompletedFrameTimeMs = frameTimeMs;
|
|
11456
11896
|
lastCompletedSamplesPerPixel = frameStats.renderedSamplesPerPixel ?? frameStats.samplesPerPixel;
|
|
11457
11897
|
}
|
|
11898
|
+
const expectedPrimaryRays = config.width * config.height * samplingPlan.renderedSamplesPerPixel;
|
|
11899
|
+
const expectedRayCounts = tiles.length * samplingPlan.renderedSamplesPerPixel * config.maxDepth;
|
|
11900
|
+
const telemetryReadback = telemetryCanRecord ? await telemetryResourcesForFrame.readFrame({
|
|
11901
|
+
expectedPrimaryRays,
|
|
11902
|
+
expectedRayCounts,
|
|
11903
|
+
waitForSubmittedGpuWork: waitForSubmittedGpuWork2
|
|
11904
|
+
}) : null;
|
|
11905
|
+
const rayCounts = !telemetryRequested ? createWavefrontRayCountTelemetry() : !awaitGPUCompletion ? createWavefrontRayCountTelemetry({
|
|
11906
|
+
status: "unavailable",
|
|
11907
|
+
expectedPrimaryRays,
|
|
11908
|
+
expectedRayCounts,
|
|
11909
|
+
reason: "gpu-work-not-awaited"
|
|
11910
|
+
}) : telemetryReadback.rayCounts;
|
|
11911
|
+
const timings = !telemetryRequested ? createWavefrontFrameTimingTelemetry({
|
|
11912
|
+
status: "not-requested",
|
|
11913
|
+
source: awaitGPUCompletion ? "queue-completion" : "cpu-submit",
|
|
11914
|
+
timestampQueryStatus: "not-recorded",
|
|
11915
|
+
totalRenderJobTimeMs: frameTimeMs
|
|
11916
|
+
}) : !awaitGPUCompletion ? createWavefrontFrameTimingTelemetry({
|
|
11917
|
+
status: "unavailable",
|
|
11918
|
+
source: "cpu-submit",
|
|
11919
|
+
timestampQueryStatus: "not-recorded",
|
|
11920
|
+
totalRenderJobTimeMs: frameTimeMs,
|
|
11921
|
+
reason: "gpu-work-not-awaited"
|
|
11922
|
+
}) : telemetryReadback.timestampQueryStatus === "available" ? createWavefrontFrameTimingTelemetry({
|
|
11923
|
+
status: "available",
|
|
11924
|
+
source: "timestamp-query",
|
|
11925
|
+
timestampQueryStatus: "available",
|
|
11926
|
+
totalGpuTimeMs: telemetryReadback.totalGpuTimeMs,
|
|
11927
|
+
totalRenderJobTimeMs: frameTimeMs
|
|
11928
|
+
}) : createWavefrontFrameTimingTelemetry({
|
|
11929
|
+
status: "fallback",
|
|
11930
|
+
source: "queue-completion",
|
|
11931
|
+
timestampQueryStatus: telemetryReadback.timestampQueryStatus,
|
|
11932
|
+
totalRenderJobTimeMs: frameTimeMs,
|
|
11933
|
+
reason: telemetryReadback.reason
|
|
11934
|
+
});
|
|
11458
11935
|
const deviceLossStatus = awaitGPUCompletion ? "not-detected" : typeof device.lost?.then === "function" ? "pending" : "not-exposed";
|
|
11459
11936
|
frameStats = Object.freeze({
|
|
11460
11937
|
...frameStats,
|
|
11461
11938
|
deviceLossStatus,
|
|
11939
|
+
secondaryRays: rayCounts.status === "available" ? rayCounts.secondaryRays : null,
|
|
11940
|
+
totalPathSegments: rayCounts.status === "available" ? rayCounts.totalPathSegments : null,
|
|
11941
|
+
rayCounts,
|
|
11942
|
+
timings,
|
|
11943
|
+
telemetryMemoryBytes: telemetryResourcesForFrame?.memoryBytes ?? 0,
|
|
11462
11944
|
gpuWorkerJobs: createGpuWorkerJobDiagnostics(
|
|
11463
11945
|
frameStats.gpuParallelism,
|
|
11464
11946
|
frameStats.commandSubmissions,
|
|
@@ -11567,6 +12049,7 @@ async function createWavefrontPathTracingComputeRenderer(options = {}) {
|
|
|
11567
12049
|
});
|
|
11568
12050
|
}
|
|
11569
12051
|
function destroy() {
|
|
12052
|
+
frameTelemetryResources?.destroy?.();
|
|
11570
12053
|
activeQueue.destroy?.();
|
|
11571
12054
|
nextQueue.destroy?.();
|
|
11572
12055
|
hitBuffer.destroy?.();
|