@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.js
CHANGED
|
@@ -2352,6 +2352,8 @@ function createWavefrontTransportGuardrailSummary(frameStats, options = {}) {
|
|
|
2352
2352
|
0,
|
|
2353
2353
|
Number(frameStats?.radianceDiagnostics?.legacyClampEquivalentSamples ?? 0)
|
|
2354
2354
|
);
|
|
2355
|
+
const rayCountStatus = typeof frameStats?.rayCounts?.status === "string" ? frameStats.rayCounts.status : "not-requested";
|
|
2356
|
+
const rayCountReason = typeof frameStats?.rayCounts?.reason === "string" ? frameStats.rayCounts.reason : null;
|
|
2355
2357
|
const memory = summarizeWavefrontMemory(frameStats?.memory);
|
|
2356
2358
|
const checks = Object.freeze([
|
|
2357
2359
|
createTransportGuardrailCheck(
|
|
@@ -2373,6 +2375,11 @@ function createWavefrontTransportGuardrailSummary(frameStats, options = {}) {
|
|
|
2373
2375
|
"radiance-diagnostics",
|
|
2374
2376
|
invalidSamples > 0 ? "fail" : legacyClampEquivalentSamples > 0 ? "warn" : "pass",
|
|
2375
2377
|
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."
|
|
2378
|
+
),
|
|
2379
|
+
createTransportGuardrailCheck(
|
|
2380
|
+
"ray-count-telemetry",
|
|
2381
|
+
rayCountStatus === "failed" ? "fail" : rayCountStatus === "unavailable" ? "warn" : "pass",
|
|
2382
|
+
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."
|
|
2376
2383
|
)
|
|
2377
2384
|
]);
|
|
2378
2385
|
const status = checks.some((check) => check.status === "fail") ? "fail" : checks.some((check) => check.status === "warn") ? "warn" : "pass";
|
|
@@ -2393,6 +2400,7 @@ function createWavefrontTransportGuardrailSummary(frameStats, options = {}) {
|
|
|
2393
2400
|
invalidSamples,
|
|
2394
2401
|
legacyClampEquivalentSamples
|
|
2395
2402
|
}),
|
|
2403
|
+
rayCountStatus,
|
|
2396
2404
|
memory
|
|
2397
2405
|
}),
|
|
2398
2406
|
checks
|
|
@@ -2503,6 +2511,7 @@ var COUNTER_TRANSPORT_RESIDUAL_LUMINANCE_OFFSET = 22;
|
|
|
2503
2511
|
var COUNTER_TRANSPORT_ZERO_TERMINATION_OFFSET = 23;
|
|
2504
2512
|
var COUNTER_TRANSPORT_CHECKSUM_OFFSET = 24;
|
|
2505
2513
|
var TRACE_STORAGE_BUFFER_BINDINGS = 10;
|
|
2514
|
+
var TRACE_SAMPLED_TEXTURE_BINDINGS = 21;
|
|
2506
2515
|
var BRDF_LUT_UPLOAD_CACHE = /* @__PURE__ */ new Map();
|
|
2507
2516
|
var MATERIAL_DIFFUSE = 0;
|
|
2508
2517
|
var MATERIAL_METAL = 1;
|
|
@@ -2561,6 +2570,7 @@ var EMPTY_TERMINATION_METRICS = Object.freeze({
|
|
|
2561
2570
|
var wavefrontPathTracingComputeLimits = Object.freeze({
|
|
2562
2571
|
workgroupSize: WORKGROUP_SIZE,
|
|
2563
2572
|
traceStorageBufferBindings: TRACE_STORAGE_BUFFER_BINDINGS,
|
|
2573
|
+
traceSampledTextureBindings: TRACE_SAMPLED_TEXTURE_BINDINGS,
|
|
2564
2574
|
rayRecordBytes: RAY_RECORD_BYTES,
|
|
2565
2575
|
hitRecordBytes: HIT_RECORD_BYTES,
|
|
2566
2576
|
sceneObjectRecordBytes: SCENE_OBJECT_RECORD_BYTES,
|
|
@@ -5694,11 +5704,11 @@ function resolveCanvas3(canvasOrSelector, documentRef = globalThis.document) {
|
|
|
5694
5704
|
return fallback;
|
|
5695
5705
|
}
|
|
5696
5706
|
async function getPipelineDiagnostics(shaderModule) {
|
|
5697
|
-
if (typeof shaderModule?.
|
|
5707
|
+
if (typeof shaderModule?.getCompilationInfo !== "function") {
|
|
5698
5708
|
return "";
|
|
5699
5709
|
}
|
|
5700
5710
|
try {
|
|
5701
|
-
const info = await shaderModule.
|
|
5711
|
+
const info = await shaderModule.getCompilationInfo();
|
|
5702
5712
|
const messages = info.messages ?? [];
|
|
5703
5713
|
if (messages.length === 0) {
|
|
5704
5714
|
return "";
|
|
@@ -5736,10 +5746,10 @@ ${diagnostics}` : "";
|
|
|
5736
5746
|
}
|
|
5737
5747
|
}
|
|
5738
5748
|
async function assertShaderModuleCompiles(shaderModule, label) {
|
|
5739
|
-
if (typeof shaderModule?.
|
|
5749
|
+
if (typeof shaderModule?.getCompilationInfo !== "function") {
|
|
5740
5750
|
return;
|
|
5741
5751
|
}
|
|
5742
|
-
const info = await shaderModule.
|
|
5752
|
+
const info = await shaderModule.getCompilationInfo();
|
|
5743
5753
|
const messages = Array.isArray(info?.messages) ? info.messages : [];
|
|
5744
5754
|
const errors = messages.filter((message) => message?.type === "error");
|
|
5745
5755
|
if (errors.length <= 0) {
|
|
@@ -5773,7 +5783,28 @@ function createWavefrontDeviceDescriptor(adapter, options = {}) {
|
|
|
5773
5783
|
TRACE_STORAGE_BUFFER_BINDINGS
|
|
5774
5784
|
);
|
|
5775
5785
|
}
|
|
5786
|
+
const exposedSampledTextureLimit = Number(adapter?.limits?.maxSampledTexturesPerShaderStage);
|
|
5787
|
+
if (Number.isFinite(exposedSampledTextureLimit)) {
|
|
5788
|
+
if (exposedSampledTextureLimit < TRACE_SAMPLED_TEXTURE_BINDINGS) {
|
|
5789
|
+
throw new Error(
|
|
5790
|
+
`Wavefront material tracing requires maxSampledTexturesPerShaderStage>=${TRACE_SAMPLED_TEXTURE_BINDINGS}, but this adapter exposes ${exposedSampledTextureLimit}.`
|
|
5791
|
+
);
|
|
5792
|
+
}
|
|
5793
|
+
requiredLimits.maxSampledTexturesPerShaderStage = Math.max(
|
|
5794
|
+
Number(requiredLimits.maxSampledTexturesPerShaderStage ?? 0),
|
|
5795
|
+
TRACE_SAMPLED_TEXTURE_BINDINGS
|
|
5796
|
+
);
|
|
5797
|
+
}
|
|
5776
5798
|
const descriptor = { ...options.deviceDescriptor ?? {} };
|
|
5799
|
+
const requiredFeatures = Array.from(descriptor.requiredFeatures ?? []);
|
|
5800
|
+
const adapterFeatures = adapter?.features;
|
|
5801
|
+
const timestampQuerySupported = typeof adapterFeatures?.has === "function" ? adapterFeatures.has("timestamp-query") : adapterFeatures && typeof adapterFeatures[Symbol.iterator] === "function" ? Array.from(adapterFeatures).includes("timestamp-query") : false;
|
|
5802
|
+
if (options.gpuTimestamps !== false && timestampQuerySupported && !requiredFeatures.includes("timestamp-query")) {
|
|
5803
|
+
requiredFeatures.push("timestamp-query");
|
|
5804
|
+
}
|
|
5805
|
+
if (requiredFeatures.length > 0) {
|
|
5806
|
+
descriptor.requiredFeatures = requiredFeatures;
|
|
5807
|
+
}
|
|
5777
5808
|
if (Object.keys(requiredLimits).length > 0) {
|
|
5778
5809
|
descriptor.requiredLimits = {
|
|
5779
5810
|
...descriptor.requiredLimits ?? {},
|
|
@@ -5963,6 +5994,376 @@ async function waitForSubmittedGpuWork(device, options = {}) {
|
|
|
5963
5994
|
}
|
|
5964
5995
|
}
|
|
5965
5996
|
|
|
5997
|
+
// src/wavefront-frame-telemetry.js
|
|
5998
|
+
var TIMESTAMP_QUERY_COUNT = 2;
|
|
5999
|
+
var TIMESTAMP_BUFFER_BYTES = TIMESTAMP_QUERY_COUNT * BigUint64Array.BYTES_PER_ELEMENT;
|
|
6000
|
+
function hasGpuFeature(features, name) {
|
|
6001
|
+
if (typeof features?.has === "function") {
|
|
6002
|
+
return features.has(name);
|
|
6003
|
+
}
|
|
6004
|
+
if (features && typeof features[Symbol.iterator] === "function") {
|
|
6005
|
+
return Array.from(features).includes(name);
|
|
6006
|
+
}
|
|
6007
|
+
return false;
|
|
6008
|
+
}
|
|
6009
|
+
function freezeRayCountTelemetry(values = {}) {
|
|
6010
|
+
return Object.freeze({
|
|
6011
|
+
status: values.status ?? "not-requested",
|
|
6012
|
+
source: values.source ?? null,
|
|
6013
|
+
expectedPrimaryRays: values.expectedPrimaryRays ?? null,
|
|
6014
|
+
observedPrimaryRays: values.observedPrimaryRays ?? null,
|
|
6015
|
+
secondaryRays: values.secondaryRays ?? null,
|
|
6016
|
+
totalPathSegments: values.totalPathSegments ?? null,
|
|
6017
|
+
bounceHistogram: Object.freeze([...values.bounceHistogram ?? []]),
|
|
6018
|
+
capturedRayCounts: values.capturedRayCounts ?? 0,
|
|
6019
|
+
expectedRayCounts: values.expectedRayCounts ?? 0,
|
|
6020
|
+
reason: values.reason ?? null
|
|
6021
|
+
});
|
|
6022
|
+
}
|
|
6023
|
+
function createWavefrontRayCountTelemetry(values = {}) {
|
|
6024
|
+
return freezeRayCountTelemetry(values);
|
|
6025
|
+
}
|
|
6026
|
+
function createWavefrontFrameTimingTelemetry(values = {}) {
|
|
6027
|
+
return Object.freeze({
|
|
6028
|
+
status: values.status ?? "not-requested",
|
|
6029
|
+
source: values.source ?? null,
|
|
6030
|
+
timestampQueryStatus: values.timestampQueryStatus ?? "not-recorded",
|
|
6031
|
+
totalGpuTimeMs: values.totalGpuTimeMs ?? null,
|
|
6032
|
+
totalRenderJobTimeMs: Math.max(0, Number(values.totalRenderJobTimeMs ?? 0)),
|
|
6033
|
+
classificationTimeMs: values.classificationTimeMs ?? null,
|
|
6034
|
+
compactionTimeMs: values.compactionTimeMs ?? null,
|
|
6035
|
+
samplingTimeMs: values.samplingTimeMs ?? null,
|
|
6036
|
+
reason: values.reason ?? null
|
|
6037
|
+
});
|
|
6038
|
+
}
|
|
6039
|
+
function safeErrorReason(error) {
|
|
6040
|
+
return error instanceof Error && error.message ? error.message : "unknown-telemetry-readback-failure";
|
|
6041
|
+
}
|
|
6042
|
+
function timestampDurationMs(values) {
|
|
6043
|
+
if (values.length < TIMESTAMP_QUERY_COUNT || values[1] < values[0]) {
|
|
6044
|
+
return null;
|
|
6045
|
+
}
|
|
6046
|
+
const durationNanoseconds = values[1] - values[0];
|
|
6047
|
+
const durationMs = Number(durationNanoseconds) / 1e6;
|
|
6048
|
+
return Number.isFinite(durationMs) && durationMs >= 0 ? durationMs : null;
|
|
6049
|
+
}
|
|
6050
|
+
function createWavefrontFrameTelemetryResources({
|
|
6051
|
+
device,
|
|
6052
|
+
constants,
|
|
6053
|
+
maxRayCountRecords
|
|
6054
|
+
}) {
|
|
6055
|
+
const rayRecordCapacity = Math.max(1, Math.trunc(Number(maxRayCountRecords) || 1));
|
|
6056
|
+
const rayBufferBytes = rayRecordCapacity * Uint32Array.BYTES_PER_ELEMENT;
|
|
6057
|
+
const mapMode = constants.map;
|
|
6058
|
+
if (!mapMode || typeof mapMode.READ !== "number") {
|
|
6059
|
+
return Object.freeze({
|
|
6060
|
+
available: false,
|
|
6061
|
+
reason: "gpu-map-read-unavailable",
|
|
6062
|
+
memoryBytes: 0,
|
|
6063
|
+
beginFrame() {
|
|
6064
|
+
},
|
|
6065
|
+
recordActiveRayCount() {
|
|
6066
|
+
},
|
|
6067
|
+
decorateFirstPass(descriptor) {
|
|
6068
|
+
return descriptor;
|
|
6069
|
+
},
|
|
6070
|
+
decorateFinalPass(descriptor) {
|
|
6071
|
+
return descriptor;
|
|
6072
|
+
},
|
|
6073
|
+
async readFrame({ expectedPrimaryRays = 0, expectedRayCounts = 0 } = {}) {
|
|
6074
|
+
return Object.freeze({
|
|
6075
|
+
rayCounts: freezeRayCountTelemetry({
|
|
6076
|
+
status: "unavailable",
|
|
6077
|
+
expectedPrimaryRays,
|
|
6078
|
+
expectedRayCounts,
|
|
6079
|
+
reason: "gpu-map-read-unavailable"
|
|
6080
|
+
}),
|
|
6081
|
+
timestampQueryStatus: "unsupported",
|
|
6082
|
+
totalGpuTimeMs: null,
|
|
6083
|
+
reason: "gpu-map-read-unavailable"
|
|
6084
|
+
});
|
|
6085
|
+
},
|
|
6086
|
+
destroy() {
|
|
6087
|
+
}
|
|
6088
|
+
});
|
|
6089
|
+
}
|
|
6090
|
+
const rayCountBuffer = createBuffer(
|
|
6091
|
+
device,
|
|
6092
|
+
constants.buffer.COPY_DST | constants.buffer.COPY_SRC,
|
|
6093
|
+
rayBufferBytes,
|
|
6094
|
+
"plasius.wavefront.rayCounts"
|
|
6095
|
+
);
|
|
6096
|
+
const rayCountReadbackBuffer = createBuffer(
|
|
6097
|
+
device,
|
|
6098
|
+
constants.buffer.COPY_DST | constants.buffer.MAP_READ,
|
|
6099
|
+
rayBufferBytes,
|
|
6100
|
+
"plasius.wavefront.rayCounts.readback"
|
|
6101
|
+
);
|
|
6102
|
+
const timestampFeatureAvailable = hasGpuFeature(device?.features, "timestamp-query");
|
|
6103
|
+
const timestampApiAvailable = timestampFeatureAvailable && typeof device.createQuerySet === "function" && typeof constants.buffer.QUERY_RESOLVE === "number";
|
|
6104
|
+
let timestampQuerySet = null;
|
|
6105
|
+
let timestampResolveBuffer = null;
|
|
6106
|
+
let timestampReadbackBuffer = null;
|
|
6107
|
+
let timestampSetupReason = timestampFeatureAvailable ? "timestamp-query-api-unavailable" : "timestamp-query-unsupported";
|
|
6108
|
+
if (timestampApiAvailable) {
|
|
6109
|
+
try {
|
|
6110
|
+
timestampQuerySet = device.createQuerySet({
|
|
6111
|
+
label: "plasius.wavefront.timestamps",
|
|
6112
|
+
type: "timestamp",
|
|
6113
|
+
count: TIMESTAMP_QUERY_COUNT
|
|
6114
|
+
});
|
|
6115
|
+
timestampResolveBuffer = createBuffer(
|
|
6116
|
+
device,
|
|
6117
|
+
constants.buffer.QUERY_RESOLVE | constants.buffer.COPY_SRC,
|
|
6118
|
+
TIMESTAMP_BUFFER_BYTES,
|
|
6119
|
+
"plasius.wavefront.timestamps.resolve"
|
|
6120
|
+
);
|
|
6121
|
+
timestampReadbackBuffer = createBuffer(
|
|
6122
|
+
device,
|
|
6123
|
+
constants.buffer.COPY_DST | constants.buffer.MAP_READ,
|
|
6124
|
+
TIMESTAMP_BUFFER_BYTES,
|
|
6125
|
+
"plasius.wavefront.timestamps.readback"
|
|
6126
|
+
);
|
|
6127
|
+
timestampSetupReason = null;
|
|
6128
|
+
} catch (error) {
|
|
6129
|
+
timestampQuerySet?.destroy?.();
|
|
6130
|
+
timestampQuerySet = null;
|
|
6131
|
+
timestampResolveBuffer?.destroy?.();
|
|
6132
|
+
timestampResolveBuffer = null;
|
|
6133
|
+
timestampReadbackBuffer?.destroy?.();
|
|
6134
|
+
timestampReadbackBuffer = null;
|
|
6135
|
+
timestampSetupReason = `timestamp-query-setup-failed:${safeErrorReason(error)}`;
|
|
6136
|
+
}
|
|
6137
|
+
}
|
|
6138
|
+
const timestampUnavailableStatus = timestampSetupReason?.startsWith(
|
|
6139
|
+
"timestamp-query-setup-failed:"
|
|
6140
|
+
) ? "failed" : "unsupported";
|
|
6141
|
+
let active = false;
|
|
6142
|
+
let recordCount = 0;
|
|
6143
|
+
let recordOverflow = false;
|
|
6144
|
+
let bounceIndices = [];
|
|
6145
|
+
let timestampStarted = false;
|
|
6146
|
+
let timestampEnded = false;
|
|
6147
|
+
let rayReadbackMapped = false;
|
|
6148
|
+
let timestampReadbackMapped = false;
|
|
6149
|
+
const memoryBytes = rayBufferBytes * 2 + (timestampQuerySet ? TIMESTAMP_BUFFER_BYTES * 2 : 0);
|
|
6150
|
+
function beginFrame() {
|
|
6151
|
+
active = true;
|
|
6152
|
+
recordCount = 0;
|
|
6153
|
+
recordOverflow = false;
|
|
6154
|
+
bounceIndices = [];
|
|
6155
|
+
timestampStarted = false;
|
|
6156
|
+
timestampEnded = false;
|
|
6157
|
+
}
|
|
6158
|
+
function recordActiveRayCount(encoder, counterBuffer, bounceIndex) {
|
|
6159
|
+
if (!active) {
|
|
6160
|
+
return;
|
|
6161
|
+
}
|
|
6162
|
+
if (recordCount >= rayRecordCapacity) {
|
|
6163
|
+
recordOverflow = true;
|
|
6164
|
+
return;
|
|
6165
|
+
}
|
|
6166
|
+
encoder.copyBufferToBuffer(
|
|
6167
|
+
counterBuffer,
|
|
6168
|
+
0,
|
|
6169
|
+
rayCountBuffer,
|
|
6170
|
+
recordCount * Uint32Array.BYTES_PER_ELEMENT,
|
|
6171
|
+
Uint32Array.BYTES_PER_ELEMENT
|
|
6172
|
+
);
|
|
6173
|
+
bounceIndices.push(Math.max(0, Math.trunc(Number(bounceIndex) || 0)));
|
|
6174
|
+
recordCount += 1;
|
|
6175
|
+
}
|
|
6176
|
+
function decorateFirstPass(descriptor) {
|
|
6177
|
+
if (!active || !timestampQuerySet || timestampStarted) {
|
|
6178
|
+
return descriptor;
|
|
6179
|
+
}
|
|
6180
|
+
timestampStarted = true;
|
|
6181
|
+
return {
|
|
6182
|
+
...descriptor,
|
|
6183
|
+
timestampWrites: {
|
|
6184
|
+
querySet: timestampQuerySet,
|
|
6185
|
+
beginningOfPassWriteIndex: 0
|
|
6186
|
+
}
|
|
6187
|
+
};
|
|
6188
|
+
}
|
|
6189
|
+
function decorateFinalPass(descriptor) {
|
|
6190
|
+
if (!active || !timestampQuerySet || !timestampStarted || timestampEnded) {
|
|
6191
|
+
return descriptor;
|
|
6192
|
+
}
|
|
6193
|
+
timestampEnded = true;
|
|
6194
|
+
return {
|
|
6195
|
+
...descriptor,
|
|
6196
|
+
timestampWrites: {
|
|
6197
|
+
querySet: timestampQuerySet,
|
|
6198
|
+
endOfPassWriteIndex: 1
|
|
6199
|
+
}
|
|
6200
|
+
};
|
|
6201
|
+
}
|
|
6202
|
+
async function readFrame({
|
|
6203
|
+
expectedPrimaryRays = 0,
|
|
6204
|
+
expectedRayCounts = recordCount,
|
|
6205
|
+
waitForSubmittedGpuWork: waitForSubmittedGpuWork2
|
|
6206
|
+
} = {}) {
|
|
6207
|
+
active = false;
|
|
6208
|
+
if (recordOverflow || recordCount !== expectedRayCounts) {
|
|
6209
|
+
return Object.freeze({
|
|
6210
|
+
rayCounts: freezeRayCountTelemetry({
|
|
6211
|
+
status: "failed",
|
|
6212
|
+
expectedPrimaryRays,
|
|
6213
|
+
capturedRayCounts: recordCount,
|
|
6214
|
+
expectedRayCounts,
|
|
6215
|
+
reason: recordOverflow ? "ray-count-record-capacity-exceeded" : `ray-count-record-mismatch:${recordCount}/${expectedRayCounts}`
|
|
6216
|
+
}),
|
|
6217
|
+
timestampQueryStatus: timestampQuerySet ? "failed" : timestampUnavailableStatus,
|
|
6218
|
+
totalGpuTimeMs: null,
|
|
6219
|
+
reason: "ray-count-recording-incomplete"
|
|
6220
|
+
});
|
|
6221
|
+
}
|
|
6222
|
+
const encoder = device.createCommandEncoder({
|
|
6223
|
+
label: "plasius.wavefront.telemetry.readback"
|
|
6224
|
+
});
|
|
6225
|
+
const rayCopyBytes = Math.max(
|
|
6226
|
+
Uint32Array.BYTES_PER_ELEMENT,
|
|
6227
|
+
recordCount * Uint32Array.BYTES_PER_ELEMENT
|
|
6228
|
+
);
|
|
6229
|
+
encoder.copyBufferToBuffer(rayCountBuffer, 0, rayCountReadbackBuffer, 0, rayCopyBytes);
|
|
6230
|
+
const canResolveTimestamps = timestampQuerySet && timestampStarted && timestampEnded && typeof encoder.resolveQuerySet === "function";
|
|
6231
|
+
if (canResolveTimestamps) {
|
|
6232
|
+
encoder.resolveQuerySet(
|
|
6233
|
+
timestampQuerySet,
|
|
6234
|
+
0,
|
|
6235
|
+
TIMESTAMP_QUERY_COUNT,
|
|
6236
|
+
timestampResolveBuffer,
|
|
6237
|
+
0
|
|
6238
|
+
);
|
|
6239
|
+
encoder.copyBufferToBuffer(
|
|
6240
|
+
timestampResolveBuffer,
|
|
6241
|
+
0,
|
|
6242
|
+
timestampReadbackBuffer,
|
|
6243
|
+
0,
|
|
6244
|
+
TIMESTAMP_BUFFER_BYTES
|
|
6245
|
+
);
|
|
6246
|
+
}
|
|
6247
|
+
device.queue.submit([encoder.finish()]);
|
|
6248
|
+
try {
|
|
6249
|
+
await waitForSubmittedGpuWork2({
|
|
6250
|
+
timeoutMs: GPU_READBACK_COMPLETION_TIMEOUT_MS,
|
|
6251
|
+
allowTimeout: false
|
|
6252
|
+
});
|
|
6253
|
+
await rayCountReadbackBuffer.mapAsync(mapMode.READ);
|
|
6254
|
+
rayReadbackMapped = true;
|
|
6255
|
+
const rayCopy = rayCountReadbackBuffer.getMappedRange().slice(0, rayCopyBytes);
|
|
6256
|
+
const rayCounts = new Uint32Array(rayCopy, 0, recordCount);
|
|
6257
|
+
rayCountReadbackBuffer.unmap();
|
|
6258
|
+
rayReadbackMapped = false;
|
|
6259
|
+
let maximumBounceIndex = 0;
|
|
6260
|
+
for (const bounceIndex of bounceIndices) {
|
|
6261
|
+
maximumBounceIndex = Math.max(maximumBounceIndex, bounceIndex);
|
|
6262
|
+
}
|
|
6263
|
+
const bounceHistogram = Array.from({ length: maximumBounceIndex + 1 }, () => 0);
|
|
6264
|
+
let observedPrimaryRays = 0;
|
|
6265
|
+
let secondaryRays = 0;
|
|
6266
|
+
let totalPathSegments = 0;
|
|
6267
|
+
for (let index = 0; index < rayCounts.length; index += 1) {
|
|
6268
|
+
const count = Number(rayCounts[index] ?? 0);
|
|
6269
|
+
const bounceIndex = bounceIndices[index] ?? 0;
|
|
6270
|
+
bounceHistogram[bounceIndex] = (bounceHistogram[bounceIndex] ?? 0) + count;
|
|
6271
|
+
totalPathSegments += count;
|
|
6272
|
+
if (bounceIndex === 0) {
|
|
6273
|
+
observedPrimaryRays += count;
|
|
6274
|
+
} else {
|
|
6275
|
+
secondaryRays += count;
|
|
6276
|
+
}
|
|
6277
|
+
}
|
|
6278
|
+
const primaryCountsMatch = observedPrimaryRays === expectedPrimaryRays;
|
|
6279
|
+
const rayTelemetry = freezeRayCountTelemetry({
|
|
6280
|
+
status: primaryCountsMatch ? "available" : "failed",
|
|
6281
|
+
source: "gpu-active-queue-readback",
|
|
6282
|
+
expectedPrimaryRays,
|
|
6283
|
+
observedPrimaryRays,
|
|
6284
|
+
secondaryRays: primaryCountsMatch ? secondaryRays : null,
|
|
6285
|
+
totalPathSegments: primaryCountsMatch ? totalPathSegments : null,
|
|
6286
|
+
bounceHistogram,
|
|
6287
|
+
capturedRayCounts: recordCount,
|
|
6288
|
+
expectedRayCounts,
|
|
6289
|
+
reason: primaryCountsMatch ? null : `primary-ray-count-mismatch:${observedPrimaryRays}/${expectedPrimaryRays}`
|
|
6290
|
+
});
|
|
6291
|
+
let timestampQueryStatus = timestampQuerySet ? "failed" : timestampUnavailableStatus;
|
|
6292
|
+
let totalGpuTimeMs = null;
|
|
6293
|
+
let timestampReason = timestampSetupReason;
|
|
6294
|
+
if (canResolveTimestamps) {
|
|
6295
|
+
try {
|
|
6296
|
+
await timestampReadbackBuffer.mapAsync(mapMode.READ);
|
|
6297
|
+
timestampReadbackMapped = true;
|
|
6298
|
+
const timestampCopy = timestampReadbackBuffer.getMappedRange().slice(0, TIMESTAMP_BUFFER_BYTES);
|
|
6299
|
+
const timestamps = new BigUint64Array(timestampCopy);
|
|
6300
|
+
timestampReadbackBuffer.unmap();
|
|
6301
|
+
timestampReadbackMapped = false;
|
|
6302
|
+
totalGpuTimeMs = timestampDurationMs(timestamps);
|
|
6303
|
+
timestampQueryStatus = totalGpuTimeMs === null ? "failed" : "available";
|
|
6304
|
+
timestampReason = totalGpuTimeMs === null ? "timestamp-query-returned-invalid-range" : null;
|
|
6305
|
+
} catch (error) {
|
|
6306
|
+
if (timestampReadbackMapped) {
|
|
6307
|
+
timestampReadbackBuffer.unmap();
|
|
6308
|
+
timestampReadbackMapped = false;
|
|
6309
|
+
}
|
|
6310
|
+
timestampQueryStatus = "failed";
|
|
6311
|
+
timestampReason = `timestamp-query-readback-failed:${safeErrorReason(error)}`;
|
|
6312
|
+
}
|
|
6313
|
+
} else if (timestampQuerySet) {
|
|
6314
|
+
timestampReason = "timestamp-query-span-incomplete";
|
|
6315
|
+
}
|
|
6316
|
+
return Object.freeze({
|
|
6317
|
+
rayCounts: rayTelemetry,
|
|
6318
|
+
timestampQueryStatus,
|
|
6319
|
+
totalGpuTimeMs,
|
|
6320
|
+
reason: timestampReason
|
|
6321
|
+
});
|
|
6322
|
+
} catch (error) {
|
|
6323
|
+
if (rayReadbackMapped) {
|
|
6324
|
+
rayCountReadbackBuffer.unmap();
|
|
6325
|
+
rayReadbackMapped = false;
|
|
6326
|
+
}
|
|
6327
|
+
if (timestampReadbackMapped) {
|
|
6328
|
+
timestampReadbackBuffer?.unmap();
|
|
6329
|
+
timestampReadbackMapped = false;
|
|
6330
|
+
}
|
|
6331
|
+
const reason = safeErrorReason(error);
|
|
6332
|
+
return Object.freeze({
|
|
6333
|
+
rayCounts: freezeRayCountTelemetry({
|
|
6334
|
+
status: "failed",
|
|
6335
|
+
expectedPrimaryRays,
|
|
6336
|
+
capturedRayCounts: recordCount,
|
|
6337
|
+
expectedRayCounts,
|
|
6338
|
+
reason: `ray-count-readback-failed:${reason}`
|
|
6339
|
+
}),
|
|
6340
|
+
timestampQueryStatus: timestampQuerySet ? "failed" : timestampUnavailableStatus,
|
|
6341
|
+
totalGpuTimeMs: null,
|
|
6342
|
+
reason: `telemetry-readback-failed:${reason}`
|
|
6343
|
+
});
|
|
6344
|
+
}
|
|
6345
|
+
}
|
|
6346
|
+
function destroy() {
|
|
6347
|
+
active = false;
|
|
6348
|
+
rayCountBuffer.destroy?.();
|
|
6349
|
+
rayCountReadbackBuffer.destroy?.();
|
|
6350
|
+
timestampQuerySet?.destroy?.();
|
|
6351
|
+
timestampResolveBuffer?.destroy?.();
|
|
6352
|
+
timestampReadbackBuffer?.destroy?.();
|
|
6353
|
+
}
|
|
6354
|
+
return Object.freeze({
|
|
6355
|
+
available: true,
|
|
6356
|
+
reason: null,
|
|
6357
|
+
memoryBytes,
|
|
6358
|
+
beginFrame,
|
|
6359
|
+
recordActiveRayCount,
|
|
6360
|
+
decorateFirstPass,
|
|
6361
|
+
decorateFinalPass,
|
|
6362
|
+
readFrame,
|
|
6363
|
+
destroy
|
|
6364
|
+
});
|
|
6365
|
+
}
|
|
6366
|
+
|
|
5966
6367
|
// src/wavefront-frame-stats.js
|
|
5967
6368
|
function resolveWavefrontRenderedSamplesPerPixel({
|
|
5968
6369
|
config,
|
|
@@ -6060,6 +6461,11 @@ function createWavefrontFrameStats({
|
|
|
6060
6461
|
maxFramePassesPerSubmission: config.maxFramePassesPerSubmission,
|
|
6061
6462
|
screenRays: config.width * config.height,
|
|
6062
6463
|
primaryRays: config.width * config.height * renderedSamplesPerPixel,
|
|
6464
|
+
secondaryRays: null,
|
|
6465
|
+
totalPathSegments: null,
|
|
6466
|
+
rayCounts: createWavefrontRayCountTelemetry(),
|
|
6467
|
+
timings: createWavefrontFrameTimingTelemetry(),
|
|
6468
|
+
telemetryMemoryBytes: 0,
|
|
6063
6469
|
sceneObjectCount: config.sceneObjectCount,
|
|
6064
6470
|
triangleCount: config.triangleCount,
|
|
6065
6471
|
emissiveTriangleCount: config.emissiveTriangleCount,
|
|
@@ -6102,7 +6508,8 @@ function createWavefrontFrameEncoder({
|
|
|
6102
6508
|
denoiseDirectResolveBindGroup,
|
|
6103
6509
|
presentPipeline,
|
|
6104
6510
|
presentBindGroup,
|
|
6105
|
-
context
|
|
6511
|
+
context,
|
|
6512
|
+
getFrameTelemetry = () => null
|
|
6106
6513
|
}) {
|
|
6107
6514
|
const resolveConfig = resolveGetter(getConfig);
|
|
6108
6515
|
const resolveBindGroups = resolveGetter(getBindGroups);
|
|
@@ -6110,9 +6517,12 @@ function createWavefrontFrameEncoder({
|
|
|
6110
6517
|
encodeTileSample(encoder, tile, configOffset, parallelism) {
|
|
6111
6518
|
const config = resolveConfig();
|
|
6112
6519
|
const bindGroups = resolveBindGroups();
|
|
6113
|
-
const
|
|
6114
|
-
|
|
6115
|
-
|
|
6520
|
+
const frameTelemetry = getFrameTelemetry();
|
|
6521
|
+
const generatePass = encoder.beginComputePass(
|
|
6522
|
+
frameTelemetry?.decorateFirstPass({
|
|
6523
|
+
label: "plasius.wavefront.generatePrimaryRaysPass"
|
|
6524
|
+
}) ?? { label: "plasius.wavefront.generatePrimaryRaysPass" }
|
|
6525
|
+
);
|
|
6116
6526
|
const tileWorkgroups = Math.ceil(tile.width * tile.height / WORKGROUP_SIZE);
|
|
6117
6527
|
generatePass.setBindGroup(0, bindGroups[0], [configOffset]);
|
|
6118
6528
|
generatePass.setPipeline(pipelines.generatePrimaryRays);
|
|
@@ -6120,6 +6530,7 @@ function createWavefrontFrameEncoder({
|
|
|
6120
6530
|
recordDirectDispatch(parallelism, [tileWorkgroups], WORKGROUP_SIZE);
|
|
6121
6531
|
generatePass.end();
|
|
6122
6532
|
for (let bounceIndex = 0; bounceIndex < config.maxDepth; bounceIndex += 1) {
|
|
6533
|
+
frameTelemetry?.recordActiveRayCount(encoder, counterBuffer, bounceIndex);
|
|
6123
6534
|
encoder.copyBufferToBuffer(
|
|
6124
6535
|
counterBuffer,
|
|
6125
6536
|
COUNTER_DISPATCH_ARGS_OFFSET,
|
|
@@ -6196,7 +6607,7 @@ function createWavefrontFrameEncoder({
|
|
|
6196
6607
|
},
|
|
6197
6608
|
encodePresent(encoder) {
|
|
6198
6609
|
const texture = context.getCurrentTexture();
|
|
6199
|
-
const
|
|
6610
|
+
const descriptor = {
|
|
6200
6611
|
label: "plasius.wavefront.presentPass",
|
|
6201
6612
|
colorAttachments: [
|
|
6202
6613
|
{
|
|
@@ -6206,7 +6617,10 @@ function createWavefrontFrameEncoder({
|
|
|
6206
6617
|
storeOp: "store"
|
|
6207
6618
|
}
|
|
6208
6619
|
]
|
|
6209
|
-
}
|
|
6620
|
+
};
|
|
6621
|
+
const passEncoder = encoder.beginRenderPass(
|
|
6622
|
+
getFrameTelemetry()?.decorateFinalPass(descriptor) ?? descriptor
|
|
6623
|
+
);
|
|
6210
6624
|
passEncoder.setPipeline(presentPipeline);
|
|
6211
6625
|
passEncoder.setBindGroup(0, presentBindGroup);
|
|
6212
6626
|
passEncoder.draw(3);
|
|
@@ -6506,36 +6920,49 @@ fn prepareMeshTrianglesAndLeaves(@builtin(global_invocation_id) globalId: vec3<u
|
|
|
6506
6920
|
let uv1 = select(vec2<f32>(0.0), vertex1.uv.xy, vertex1.uv.z > 0.5);
|
|
6507
6921
|
let uv2 = select(vec2<f32>(0.0), vertex2.uv.xy, vertex2.uv.z > 0.5);
|
|
6508
6922
|
|
|
6509
|
-
|
|
6510
|
-
|
|
6511
|
-
|
|
6512
|
-
|
|
6513
|
-
|
|
6514
|
-
|
|
6515
|
-
|
|
6516
|
-
|
|
6517
|
-
|
|
6518
|
-
|
|
6519
|
-
|
|
6520
|
-
|
|
6521
|
-
|
|
6522
|
-
|
|
6523
|
-
|
|
6524
|
-
|
|
6525
|
-
|
|
6526
|
-
|
|
6527
|
-
|
|
6528
|
-
|
|
6529
|
-
|
|
6530
|
-
|
|
6531
|
-
|
|
6532
|
-
|
|
6533
|
-
|
|
6534
|
-
|
|
6535
|
-
|
|
6536
|
-
|
|
6537
|
-
|
|
6538
|
-
);
|
|
6923
|
+
var preparedTriangle = TriangleRecord();
|
|
6924
|
+
preparedTriangle.triangleId = triangleIndex;
|
|
6925
|
+
preparedTriangle.meshId = mesh.meshId;
|
|
6926
|
+
preparedTriangle.materialKind = mesh.materialKind;
|
|
6927
|
+
preparedTriangle.flags = mesh.flags;
|
|
6928
|
+
preparedTriangle.materialRefId = mesh.materialRefId;
|
|
6929
|
+
preparedTriangle.mediumRefId = mesh.mediumRefId;
|
|
6930
|
+
preparedTriangle.materialSlot = mesh.materialSlot;
|
|
6931
|
+
preparedTriangle.v0 = vec4<f32>(vertex0.position.xyz, 0.0);
|
|
6932
|
+
preparedTriangle.v1 = vec4<f32>(vertex1.position.xyz, 0.0);
|
|
6933
|
+
preparedTriangle.v2 = vec4<f32>(vertex2.position.xyz, 0.0);
|
|
6934
|
+
preparedTriangle.n0 = vec4<f32>(n0, 0.0);
|
|
6935
|
+
preparedTriangle.n1 = vec4<f32>(n1, 0.0);
|
|
6936
|
+
preparedTriangle.n2 = vec4<f32>(n2, 0.0);
|
|
6937
|
+
preparedTriangle.uv0uv1 = vec4<f32>(uv0, uv1);
|
|
6938
|
+
preparedTriangle.uv2Pad = vec4<f32>(uv2, 0.0, 0.0);
|
|
6939
|
+
preparedTriangle.color = mesh.color;
|
|
6940
|
+
preparedTriangle.emission = mesh.emission;
|
|
6941
|
+
preparedTriangle.material = mesh.material;
|
|
6942
|
+
preparedTriangle.materialResponse = mesh.materialResponse;
|
|
6943
|
+
preparedTriangle.materialExtension = mesh.materialExtension;
|
|
6944
|
+
preparedTriangle.specularColor = mesh.specularColor;
|
|
6945
|
+
preparedTriangle.baseColorAtlas = mesh.baseColorAtlas;
|
|
6946
|
+
preparedTriangle.metallicRoughnessAtlas = mesh.metallicRoughnessAtlas;
|
|
6947
|
+
preparedTriangle.normalAtlas = mesh.normalAtlas;
|
|
6948
|
+
preparedTriangle.occlusionAtlas = mesh.occlusionAtlas;
|
|
6949
|
+
preparedTriangle.emissiveAtlas = mesh.emissiveAtlas;
|
|
6950
|
+
preparedTriangle.textureSettings = mesh.textureSettings;
|
|
6951
|
+
preparedTriangle.materialExtension2 = vec4<f32>(0.0, 0.0, 1.3, 0.0);
|
|
6952
|
+
preparedTriangle.materialExtension3 = vec4<f32>(100.0, 400.0, 0.0, 0.0);
|
|
6953
|
+
preparedTriangle.clearcoatAtlas = vec4<f32>(0.0, 0.0, 1.0, 1.0);
|
|
6954
|
+
preparedTriangle.clearcoatRoughnessAtlas = vec4<f32>(0.0, 0.0, 1.0, 1.0);
|
|
6955
|
+
preparedTriangle.clearcoatNormalAtlas = vec4<f32>(0.0, 0.0, 1.0, 1.0);
|
|
6956
|
+
preparedTriangle.transmissionAtlas = vec4<f32>(0.0, 0.0, 1.0, 1.0);
|
|
6957
|
+
preparedTriangle.thicknessAtlas = vec4<f32>(0.0, 0.0, 1.0, 1.0);
|
|
6958
|
+
preparedTriangle.sheenColorAtlas = vec4<f32>(0.0, 0.0, 1.0, 1.0);
|
|
6959
|
+
preparedTriangle.sheenRoughnessAtlas = vec4<f32>(0.0, 0.0, 1.0, 1.0);
|
|
6960
|
+
preparedTriangle.specularAtlas = vec4<f32>(0.0, 0.0, 1.0, 1.0);
|
|
6961
|
+
preparedTriangle.specularColorAtlas = vec4<f32>(0.0, 0.0, 1.0, 1.0);
|
|
6962
|
+
preparedTriangle.iridescenceAtlas = vec4<f32>(0.0, 0.0, 1.0, 1.0);
|
|
6963
|
+
preparedTriangle.iridescenceThicknessAtlas = vec4<f32>(0.0, 0.0, 1.0, 1.0);
|
|
6964
|
+
preparedTriangle.anisotropyAtlas = vec4<f32>(0.0, 0.0, 1.0, 1.0);
|
|
6965
|
+
triangles[triangleIndex] = preparedTriangle;
|
|
6539
6966
|
|
|
6540
6967
|
let leafBase = config.triangleCount - 1u;
|
|
6541
6968
|
let nodeIndex = leafBase + triangleIndex;
|
|
@@ -7205,36 +7632,7 @@ fn intersectActiveQueue(@builtin(global_invocation_id) globalId: vec3<u32>) {
|
|
|
7205
7632
|
vec4<f32>(1.0, 1.0, 1.0, 1.0)
|
|
7206
7633
|
);
|
|
7207
7634
|
var candidate = no_candidate();
|
|
7208
|
-
var hitTriangle = TriangleRecord(
|
|
7209
|
-
0u,
|
|
7210
|
-
0u,
|
|
7211
|
-
0u,
|
|
7212
|
-
0u,
|
|
7213
|
-
0u,
|
|
7214
|
-
0u,
|
|
7215
|
-
0u,
|
|
7216
|
-
0u,
|
|
7217
|
-
vec4<f32>(0.0),
|
|
7218
|
-
vec4<f32>(0.0),
|
|
7219
|
-
vec4<f32>(0.0),
|
|
7220
|
-
vec4<f32>(0.0, 1.0, 0.0, 0.0),
|
|
7221
|
-
vec4<f32>(0.0, 1.0, 0.0, 0.0),
|
|
7222
|
-
vec4<f32>(0.0, 1.0, 0.0, 0.0),
|
|
7223
|
-
vec4<f32>(0.0),
|
|
7224
|
-
vec4<f32>(0.0),
|
|
7225
|
-
vec4<f32>(0.0),
|
|
7226
|
-
vec4<f32>(0.0),
|
|
7227
|
-
vec4<f32>(1.0, 0.0, 1.0, 1.0),
|
|
7228
|
-
vec4<f32>(0.0, 0.0, 0.0, 0.08),
|
|
7229
|
-
vec4<f32>(0.08, 1.0, 0.0, 0.0),
|
|
7230
|
-
vec4<f32>(1.0, 1.0, 1.0, 1.0),
|
|
7231
|
-
vec4<f32>(0.0, 0.0, 1.0, 1.0),
|
|
7232
|
-
vec4<f32>(0.0, 0.0, 1.0, 1.0),
|
|
7233
|
-
vec4<f32>(0.0, 0.0, 1.0, 1.0),
|
|
7234
|
-
vec4<f32>(0.0, 0.0, 1.0, 1.0),
|
|
7235
|
-
vec4<f32>(0.0, 0.0, 1.0, 1.0),
|
|
7236
|
-
vec4<f32>(1.0, 1.0, 1.0, 0.0)
|
|
7237
|
-
);
|
|
7635
|
+
var hitTriangle = TriangleRecord();
|
|
7238
7636
|
|
|
7239
7637
|
for (var objectIndex = 0u; objectIndex < config.sceneObjectCount; objectIndex = objectIndex + 1u) {
|
|
7240
7638
|
let object = sceneObjects[objectIndex];
|
|
@@ -10109,7 +10507,19 @@ function createWavefrontBindGroupLayouts(device, constants) {
|
|
|
10109
10507
|
{ binding: 29, visibility: constants.shader.COMPUTE, texture: { sampleType: "float" } },
|
|
10110
10508
|
{ binding: 30, visibility: constants.shader.COMPUTE, sampler: { type: "filtering" } },
|
|
10111
10509
|
{ binding: 31, visibility: constants.shader.COMPUTE, texture: { sampleType: "float" } },
|
|
10112
|
-
{ binding: 32, visibility: constants.shader.COMPUTE, texture: { sampleType: "float" } }
|
|
10510
|
+
{ binding: 32, visibility: constants.shader.COMPUTE, texture: { sampleType: "float" } },
|
|
10511
|
+
{ binding: 33, visibility: constants.shader.COMPUTE, texture: { sampleType: "float" } },
|
|
10512
|
+
{ binding: 34, visibility: constants.shader.COMPUTE, texture: { sampleType: "float" } },
|
|
10513
|
+
{ binding: 35, visibility: constants.shader.COMPUTE, texture: { sampleType: "float" } },
|
|
10514
|
+
{ binding: 36, visibility: constants.shader.COMPUTE, texture: { sampleType: "float" } },
|
|
10515
|
+
{ binding: 37, visibility: constants.shader.COMPUTE, texture: { sampleType: "float" } },
|
|
10516
|
+
{ binding: 38, visibility: constants.shader.COMPUTE, texture: { sampleType: "float" } },
|
|
10517
|
+
{ binding: 39, visibility: constants.shader.COMPUTE, texture: { sampleType: "float" } },
|
|
10518
|
+
{ binding: 40, visibility: constants.shader.COMPUTE, texture: { sampleType: "float" } },
|
|
10519
|
+
{ binding: 41, visibility: constants.shader.COMPUTE, texture: { sampleType: "float" } },
|
|
10520
|
+
{ binding: 42, visibility: constants.shader.COMPUTE, texture: { sampleType: "float" } },
|
|
10521
|
+
{ binding: 43, visibility: constants.shader.COMPUTE, texture: { sampleType: "float" } },
|
|
10522
|
+
{ binding: 44, visibility: constants.shader.COMPUTE, texture: { sampleType: "float" } }
|
|
10113
10523
|
]
|
|
10114
10524
|
});
|
|
10115
10525
|
const acceleration = device.createBindGroupLayout({
|
|
@@ -11097,6 +11507,8 @@ async function createWavefrontPathTracingComputeRenderer(options = {}) {
|
|
|
11097
11507
|
outputView,
|
|
11098
11508
|
sampler
|
|
11099
11509
|
});
|
|
11510
|
+
let activeFrameTelemetry = null;
|
|
11511
|
+
let frameTelemetryResources = null;
|
|
11100
11512
|
const frameEncoder = createWavefrontFrameEncoder({
|
|
11101
11513
|
getConfig: () => config,
|
|
11102
11514
|
getBindGroups: () => bindGroups,
|
|
@@ -11108,7 +11520,8 @@ async function createWavefrontPathTracingComputeRenderer(options = {}) {
|
|
|
11108
11520
|
denoiseDirectResolveBindGroup,
|
|
11109
11521
|
presentPipeline,
|
|
11110
11522
|
presentBindGroup,
|
|
11111
|
-
context
|
|
11523
|
+
context,
|
|
11524
|
+
getFrameTelemetry: () => activeFrameTelemetry
|
|
11112
11525
|
});
|
|
11113
11526
|
let frame = 0;
|
|
11114
11527
|
let accelerationBuilt = !config.gpuAccelerationBuildRequired;
|
|
@@ -11121,6 +11534,16 @@ async function createWavefrontPathTracingComputeRenderer(options = {}) {
|
|
|
11121
11534
|
createGpuParallelismCounters()
|
|
11122
11535
|
);
|
|
11123
11536
|
const waitForSubmittedGpuWork2 = (waitOptions = {}) => waitForSubmittedGpuWork(device, waitOptions);
|
|
11537
|
+
function ensureFrameTelemetryResources() {
|
|
11538
|
+
if (!frameTelemetryResources) {
|
|
11539
|
+
frameTelemetryResources = createWavefrontFrameTelemetryResources({
|
|
11540
|
+
device,
|
|
11541
|
+
constants,
|
|
11542
|
+
maxRayCountRecords: tiles.length * config.samplesPerPixel * config.maxDepth
|
|
11543
|
+
});
|
|
11544
|
+
}
|
|
11545
|
+
return frameTelemetryResources;
|
|
11546
|
+
}
|
|
11124
11547
|
function resolveRenderedSamplesPerPixel(renderOptions = {}, awaitGPUCompletion = true) {
|
|
11125
11548
|
return resolveWavefrontRenderedSamplesPerPixel({
|
|
11126
11549
|
config,
|
|
@@ -11248,7 +11671,13 @@ async function createWavefrontPathTracingComputeRenderer(options = {}) {
|
|
|
11248
11671
|
frameSubmissionCount + (accelerationBuildSubmitted ? 1 : 0),
|
|
11249
11672
|
frameTimeMs,
|
|
11250
11673
|
false
|
|
11251
|
-
)
|
|
11674
|
+
),
|
|
11675
|
+
timings: createWavefrontFrameTimingTelemetry({
|
|
11676
|
+
status: "not-requested",
|
|
11677
|
+
source: "cpu-submit",
|
|
11678
|
+
timestampQueryStatus: "not-recorded",
|
|
11679
|
+
totalRenderJobTimeMs: frameTimeMs
|
|
11680
|
+
})
|
|
11252
11681
|
});
|
|
11253
11682
|
}
|
|
11254
11683
|
function dispatchFrameAwaitingGpu(frameIndex, parallelism, renderedSamplesPerPixel = config.samplesPerPixel, optionsForFrame = {}) {
|
|
@@ -11278,98 +11707,151 @@ async function createWavefrontPathTracingComputeRenderer(options = {}) {
|
|
|
11278
11707
|
const awaitGPUCompletion = renderOptions.awaitGPUCompletion !== false;
|
|
11279
11708
|
const samplingPlan = resolveRenderedSamplesPerPixel(renderOptions, awaitGPUCompletion);
|
|
11280
11709
|
const useThrottledHighSamplePath = awaitGPUCompletion && samplingPlan.renderedSamplesPerPixel >= 8;
|
|
11710
|
+
const telemetryRequested = renderOptions.readStats === true;
|
|
11711
|
+
const telemetryCanRecord = telemetryRequested && awaitGPUCompletion;
|
|
11712
|
+
const telemetryResourcesForFrame = telemetryCanRecord ? ensureFrameTelemetryResources() : null;
|
|
11713
|
+
if (telemetryResourcesForFrame?.available) {
|
|
11714
|
+
telemetryResourcesForFrame.beginFrame();
|
|
11715
|
+
activeFrameTelemetry = telemetryResourcesForFrame;
|
|
11716
|
+
}
|
|
11281
11717
|
const frameStartTimeMs = nowMs3();
|
|
11282
11718
|
let frameStats;
|
|
11283
|
-
|
|
11284
|
-
|
|
11285
|
-
|
|
11286
|
-
|
|
11287
|
-
|
|
11288
|
-
|
|
11289
|
-
|
|
11290
|
-
|
|
11291
|
-
|
|
11292
|
-
|
|
11293
|
-
|
|
11294
|
-
|
|
11295
|
-
|
|
11296
|
-
|
|
11297
|
-
|
|
11298
|
-
|
|
11299
|
-
|
|
11300
|
-
|
|
11301
|
-
|
|
11302
|
-
|
|
11303
|
-
|
|
11304
|
-
|
|
11305
|
-
|
|
11306
|
-
|
|
11307
|
-
|
|
11308
|
-
sampleRangeStart: 0,
|
|
11309
|
-
sampleRangeEnd: samplingPlan.renderedSamplesPerPixel,
|
|
11310
|
-
tileStartIndex: tileIndex,
|
|
11311
|
-
tileEndIndex: tileIndex + 1,
|
|
11312
|
-
startingSubmissionCount: frameSubmissionCount,
|
|
11313
|
-
startingSlot: frameConfigSlot,
|
|
11314
|
-
includeDenoise: tileIndex + 1 >= tiles.length,
|
|
11315
|
-
includePresent: tileIndex + 1 >= tiles.length
|
|
11316
|
-
}
|
|
11317
|
-
);
|
|
11318
|
-
frameSubmissionCount = tileRangeDispatch.submissionCount;
|
|
11319
|
-
frameConfigSlot = tileRangeDispatch.slot;
|
|
11320
|
-
const tileWaitOptions = {
|
|
11321
|
-
...estimateSubmittedGpuWorkTiming(
|
|
11322
|
-
{ ...config, renderedSamplesPerPixel: samplingPlan.renderedSamplesPerPixel },
|
|
11323
|
-
1,
|
|
11324
|
-
renderOptions.submittedWorkTimeoutMs,
|
|
11719
|
+
try {
|
|
11720
|
+
if (useThrottledHighSamplePath) {
|
|
11721
|
+
frame += 1;
|
|
11722
|
+
const frameIndex = frame + config.frameIndex;
|
|
11723
|
+
const parallelismCounters = createGpuParallelismCounters();
|
|
11724
|
+
const accelerationBuildSubmitted = dispatchGpuAccelerationBuild(frameIndex, parallelismCounters);
|
|
11725
|
+
let frameSubmissionCount = 0;
|
|
11726
|
+
let frameConfigSlot = 0;
|
|
11727
|
+
if (accelerationBuildSubmitted) {
|
|
11728
|
+
const accelerationWaitOptions = {
|
|
11729
|
+
...estimateSubmittedGpuWorkTiming(
|
|
11730
|
+
{ ...config, renderedSamplesPerPixel: 1 },
|
|
11731
|
+
1,
|
|
11732
|
+
renderOptions.submittedWorkTimeoutMs,
|
|
11733
|
+
{ includeAccelerationBuild: true }
|
|
11734
|
+
),
|
|
11735
|
+
allowTimeout: false
|
|
11736
|
+
};
|
|
11737
|
+
await waitForSubmittedGpuWork2(accelerationWaitOptions);
|
|
11738
|
+
}
|
|
11739
|
+
for (let tileIndex = 0; tileIndex < tiles.length; tileIndex += 1) {
|
|
11740
|
+
const tileRangeDispatch = dispatchFrameAwaitingGpu(
|
|
11741
|
+
frameIndex,
|
|
11742
|
+
parallelismCounters,
|
|
11743
|
+
samplingPlan.renderedSamplesPerPixel,
|
|
11325
11744
|
{
|
|
11326
|
-
|
|
11745
|
+
sampleRangeStart: 0,
|
|
11746
|
+
sampleRangeEnd: samplingPlan.renderedSamplesPerPixel,
|
|
11747
|
+
tileStartIndex: tileIndex,
|
|
11748
|
+
tileEndIndex: tileIndex + 1,
|
|
11749
|
+
startingSubmissionCount: frameSubmissionCount,
|
|
11750
|
+
startingSlot: frameConfigSlot,
|
|
11751
|
+
includeDenoise: tileIndex + 1 >= tiles.length,
|
|
11327
11752
|
includePresent: tileIndex + 1 >= tiles.length
|
|
11328
11753
|
}
|
|
11329
|
-
)
|
|
11754
|
+
);
|
|
11755
|
+
frameSubmissionCount = tileRangeDispatch.submissionCount;
|
|
11756
|
+
frameConfigSlot = tileRangeDispatch.slot;
|
|
11757
|
+
const tileWaitOptions = {
|
|
11758
|
+
...estimateSubmittedGpuWorkTiming(
|
|
11759
|
+
{ ...config, renderedSamplesPerPixel: samplingPlan.renderedSamplesPerPixel },
|
|
11760
|
+
1,
|
|
11761
|
+
renderOptions.submittedWorkTimeoutMs,
|
|
11762
|
+
{
|
|
11763
|
+
includeDenoise: tileIndex + 1 >= tiles.length && config.denoise,
|
|
11764
|
+
includePresent: tileIndex + 1 >= tiles.length
|
|
11765
|
+
}
|
|
11766
|
+
),
|
|
11767
|
+
allowTimeout: false
|
|
11768
|
+
};
|
|
11769
|
+
await waitForSubmittedGpuWork2(tileWaitOptions);
|
|
11770
|
+
}
|
|
11771
|
+
frameStats = createFrameStats({
|
|
11772
|
+
frameIndex,
|
|
11773
|
+
accelerationBuildSubmitted,
|
|
11774
|
+
frameSubmissionCount,
|
|
11775
|
+
parallelismCounters,
|
|
11776
|
+
renderedSamplesPerPixel: samplingPlan.renderedSamplesPerPixel,
|
|
11777
|
+
targetSamplesPerPixel: samplingPlan.targetSamplesPerPixel,
|
|
11778
|
+
frameTimeBudgetMs: samplingPlan.frameTimeBudgetMs,
|
|
11779
|
+
budgetConstrained: samplingPlan.budgetConstrained
|
|
11780
|
+
});
|
|
11781
|
+
} else {
|
|
11782
|
+
const submittedWorkTiming = estimateSubmittedGpuWorkTiming(
|
|
11783
|
+
{ ...config, renderedSamplesPerPixel: samplingPlan.renderedSamplesPerPixel },
|
|
11784
|
+
tiles.length,
|
|
11785
|
+
renderOptions.submittedWorkTimeoutMs,
|
|
11786
|
+
{ includeAccelerationBuild: config.gpuAccelerationBuildRequired && !accelerationBuilt }
|
|
11787
|
+
);
|
|
11788
|
+
const submissionWaitOptions = awaitGPUCompletion ? {
|
|
11789
|
+
timeoutMs: submittedWorkTiming.timeoutMs,
|
|
11790
|
+
maxWaitMs: submittedWorkTiming.maxWaitMs,
|
|
11330
11791
|
allowTimeout: false
|
|
11792
|
+
} : {
|
|
11793
|
+
timeoutMs: submittedWorkTiming.timeoutMs,
|
|
11794
|
+
maxWaitMs: submittedWorkTiming.maxWaitMs
|
|
11331
11795
|
};
|
|
11332
|
-
|
|
11333
|
-
|
|
11334
|
-
|
|
11335
|
-
|
|
11336
|
-
accelerationBuildSubmitted,
|
|
11337
|
-
frameSubmissionCount,
|
|
11338
|
-
parallelismCounters,
|
|
11339
|
-
renderedSamplesPerPixel: samplingPlan.renderedSamplesPerPixel,
|
|
11340
|
-
targetSamplesPerPixel: samplingPlan.targetSamplesPerPixel,
|
|
11341
|
-
frameTimeBudgetMs: samplingPlan.frameTimeBudgetMs,
|
|
11342
|
-
budgetConstrained: samplingPlan.budgetConstrained
|
|
11343
|
-
});
|
|
11344
|
-
} else {
|
|
11345
|
-
const submittedWorkTiming = estimateSubmittedGpuWorkTiming(
|
|
11346
|
-
{ ...config, renderedSamplesPerPixel: samplingPlan.renderedSamplesPerPixel },
|
|
11347
|
-
tiles.length,
|
|
11348
|
-
renderOptions.submittedWorkTimeoutMs,
|
|
11349
|
-
{ includeAccelerationBuild: config.gpuAccelerationBuildRequired && !accelerationBuilt }
|
|
11350
|
-
);
|
|
11351
|
-
const submissionWaitOptions = awaitGPUCompletion ? {
|
|
11352
|
-
timeoutMs: submittedWorkTiming.timeoutMs,
|
|
11353
|
-
maxWaitMs: submittedWorkTiming.maxWaitMs,
|
|
11354
|
-
allowTimeout: false
|
|
11355
|
-
} : {
|
|
11356
|
-
timeoutMs: submittedWorkTiming.timeoutMs,
|
|
11357
|
-
maxWaitMs: submittedWorkTiming.maxWaitMs
|
|
11358
|
-
};
|
|
11359
|
-
frameStats = renderOnce(renderOptions, samplingPlan);
|
|
11360
|
-
if (awaitGPUCompletion) {
|
|
11361
|
-
await waitForSubmittedGpuWork2(submissionWaitOptions);
|
|
11796
|
+
frameStats = renderOnce(renderOptions, samplingPlan);
|
|
11797
|
+
if (awaitGPUCompletion) {
|
|
11798
|
+
await waitForSubmittedGpuWork2(submissionWaitOptions);
|
|
11799
|
+
}
|
|
11362
11800
|
}
|
|
11801
|
+
} finally {
|
|
11802
|
+
activeFrameTelemetry = null;
|
|
11363
11803
|
}
|
|
11364
11804
|
const frameTimeMs = Math.max(0, nowMs3() - frameStartTimeMs);
|
|
11365
11805
|
if (awaitGPUCompletion) {
|
|
11366
11806
|
lastCompletedFrameTimeMs = frameTimeMs;
|
|
11367
11807
|
lastCompletedSamplesPerPixel = frameStats.renderedSamplesPerPixel ?? frameStats.samplesPerPixel;
|
|
11368
11808
|
}
|
|
11809
|
+
const expectedPrimaryRays = config.width * config.height * samplingPlan.renderedSamplesPerPixel;
|
|
11810
|
+
const expectedRayCounts = tiles.length * samplingPlan.renderedSamplesPerPixel * config.maxDepth;
|
|
11811
|
+
const telemetryReadback = telemetryCanRecord ? await telemetryResourcesForFrame.readFrame({
|
|
11812
|
+
expectedPrimaryRays,
|
|
11813
|
+
expectedRayCounts,
|
|
11814
|
+
waitForSubmittedGpuWork: waitForSubmittedGpuWork2
|
|
11815
|
+
}) : null;
|
|
11816
|
+
const rayCounts = !telemetryRequested ? createWavefrontRayCountTelemetry() : !awaitGPUCompletion ? createWavefrontRayCountTelemetry({
|
|
11817
|
+
status: "unavailable",
|
|
11818
|
+
expectedPrimaryRays,
|
|
11819
|
+
expectedRayCounts,
|
|
11820
|
+
reason: "gpu-work-not-awaited"
|
|
11821
|
+
}) : telemetryReadback.rayCounts;
|
|
11822
|
+
const timings = !telemetryRequested ? createWavefrontFrameTimingTelemetry({
|
|
11823
|
+
status: "not-requested",
|
|
11824
|
+
source: awaitGPUCompletion ? "queue-completion" : "cpu-submit",
|
|
11825
|
+
timestampQueryStatus: "not-recorded",
|
|
11826
|
+
totalRenderJobTimeMs: frameTimeMs
|
|
11827
|
+
}) : !awaitGPUCompletion ? createWavefrontFrameTimingTelemetry({
|
|
11828
|
+
status: "unavailable",
|
|
11829
|
+
source: "cpu-submit",
|
|
11830
|
+
timestampQueryStatus: "not-recorded",
|
|
11831
|
+
totalRenderJobTimeMs: frameTimeMs,
|
|
11832
|
+
reason: "gpu-work-not-awaited"
|
|
11833
|
+
}) : telemetryReadback.timestampQueryStatus === "available" ? createWavefrontFrameTimingTelemetry({
|
|
11834
|
+
status: "available",
|
|
11835
|
+
source: "timestamp-query",
|
|
11836
|
+
timestampQueryStatus: "available",
|
|
11837
|
+
totalGpuTimeMs: telemetryReadback.totalGpuTimeMs,
|
|
11838
|
+
totalRenderJobTimeMs: frameTimeMs
|
|
11839
|
+
}) : createWavefrontFrameTimingTelemetry({
|
|
11840
|
+
status: "fallback",
|
|
11841
|
+
source: "queue-completion",
|
|
11842
|
+
timestampQueryStatus: telemetryReadback.timestampQueryStatus,
|
|
11843
|
+
totalRenderJobTimeMs: frameTimeMs,
|
|
11844
|
+
reason: telemetryReadback.reason
|
|
11845
|
+
});
|
|
11369
11846
|
const deviceLossStatus = awaitGPUCompletion ? "not-detected" : typeof device.lost?.then === "function" ? "pending" : "not-exposed";
|
|
11370
11847
|
frameStats = Object.freeze({
|
|
11371
11848
|
...frameStats,
|
|
11372
11849
|
deviceLossStatus,
|
|
11850
|
+
secondaryRays: rayCounts.status === "available" ? rayCounts.secondaryRays : null,
|
|
11851
|
+
totalPathSegments: rayCounts.status === "available" ? rayCounts.totalPathSegments : null,
|
|
11852
|
+
rayCounts,
|
|
11853
|
+
timings,
|
|
11854
|
+
telemetryMemoryBytes: telemetryResourcesForFrame?.memoryBytes ?? 0,
|
|
11373
11855
|
gpuWorkerJobs: createGpuWorkerJobDiagnostics(
|
|
11374
11856
|
frameStats.gpuParallelism,
|
|
11375
11857
|
frameStats.commandSubmissions,
|
|
@@ -11478,6 +11960,7 @@ async function createWavefrontPathTracingComputeRenderer(options = {}) {
|
|
|
11478
11960
|
});
|
|
11479
11961
|
}
|
|
11480
11962
|
function destroy() {
|
|
11963
|
+
frameTelemetryResources?.destroy?.();
|
|
11481
11964
|
activeQueue.destroy?.();
|
|
11482
11965
|
nextQueue.destroy?.();
|
|
11483
11966
|
hitBuffer.destroy?.();
|