@plasius/gpu-renderer 0.2.40 → 0.2.41
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +23 -0
- package/README.md +41 -1
- package/dist/index.cjs +626 -147
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +626 -147
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/index.d.ts +34 -0
- package/src/wavefront-compute.js +174 -76
- package/src/wavefront-core.js +2 -0
- package/src/wavefront-frame-encoder.js +13 -5
- package/src/wavefront-frame-runtime.js +24 -0
- package/src/wavefront-frame-stats.js +9 -0
- package/src/wavefront-frame-telemetry.js +411 -0
- package/src/wavefront-pipelines.js +12 -0
- package/src/wavefront-runtime-support.js +37 -4
- package/src/wavefront-shader-bvh.js +43 -30
- package/src/wavefront-shader-kernels.js +1 -30
package/dist/index.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,372 @@ 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
|
+
const bounceHistogram = Array.from({ length: Math.max(0, ...bounceIndices) + 1 }, () => 0);
|
|
6260
|
+
let observedPrimaryRays = 0;
|
|
6261
|
+
let secondaryRays = 0;
|
|
6262
|
+
let totalPathSegments = 0;
|
|
6263
|
+
for (let index = 0; index < rayCounts.length; index += 1) {
|
|
6264
|
+
const count = Number(rayCounts[index] ?? 0);
|
|
6265
|
+
const bounceIndex = bounceIndices[index] ?? 0;
|
|
6266
|
+
bounceHistogram[bounceIndex] = (bounceHistogram[bounceIndex] ?? 0) + count;
|
|
6267
|
+
totalPathSegments += count;
|
|
6268
|
+
if (bounceIndex === 0) {
|
|
6269
|
+
observedPrimaryRays += count;
|
|
6270
|
+
} else {
|
|
6271
|
+
secondaryRays += count;
|
|
6272
|
+
}
|
|
6273
|
+
}
|
|
6274
|
+
const primaryCountsMatch = observedPrimaryRays === expectedPrimaryRays;
|
|
6275
|
+
const rayTelemetry = freezeRayCountTelemetry({
|
|
6276
|
+
status: primaryCountsMatch ? "available" : "failed",
|
|
6277
|
+
source: "gpu-active-queue-readback",
|
|
6278
|
+
expectedPrimaryRays,
|
|
6279
|
+
observedPrimaryRays,
|
|
6280
|
+
secondaryRays: primaryCountsMatch ? secondaryRays : null,
|
|
6281
|
+
totalPathSegments: primaryCountsMatch ? totalPathSegments : null,
|
|
6282
|
+
bounceHistogram,
|
|
6283
|
+
capturedRayCounts: recordCount,
|
|
6284
|
+
expectedRayCounts,
|
|
6285
|
+
reason: primaryCountsMatch ? null : `primary-ray-count-mismatch:${observedPrimaryRays}/${expectedPrimaryRays}`
|
|
6286
|
+
});
|
|
6287
|
+
let timestampQueryStatus = timestampQuerySet ? "failed" : timestampUnavailableStatus;
|
|
6288
|
+
let totalGpuTimeMs = null;
|
|
6289
|
+
let timestampReason = timestampSetupReason;
|
|
6290
|
+
if (canResolveTimestamps) {
|
|
6291
|
+
try {
|
|
6292
|
+
await timestampReadbackBuffer.mapAsync(mapMode.READ);
|
|
6293
|
+
timestampReadbackMapped = true;
|
|
6294
|
+
const timestampCopy = timestampReadbackBuffer.getMappedRange().slice(0, TIMESTAMP_BUFFER_BYTES);
|
|
6295
|
+
const timestamps = new BigUint64Array(timestampCopy);
|
|
6296
|
+
timestampReadbackBuffer.unmap();
|
|
6297
|
+
timestampReadbackMapped = false;
|
|
6298
|
+
totalGpuTimeMs = timestampDurationMs(timestamps);
|
|
6299
|
+
timestampQueryStatus = totalGpuTimeMs === null ? "failed" : "available";
|
|
6300
|
+
timestampReason = totalGpuTimeMs === null ? "timestamp-query-returned-invalid-range" : null;
|
|
6301
|
+
} catch (error) {
|
|
6302
|
+
if (timestampReadbackMapped) {
|
|
6303
|
+
timestampReadbackBuffer.unmap();
|
|
6304
|
+
timestampReadbackMapped = false;
|
|
6305
|
+
}
|
|
6306
|
+
timestampQueryStatus = "failed";
|
|
6307
|
+
timestampReason = `timestamp-query-readback-failed:${safeErrorReason(error)}`;
|
|
6308
|
+
}
|
|
6309
|
+
} else if (timestampQuerySet) {
|
|
6310
|
+
timestampReason = "timestamp-query-span-incomplete";
|
|
6311
|
+
}
|
|
6312
|
+
return Object.freeze({
|
|
6313
|
+
rayCounts: rayTelemetry,
|
|
6314
|
+
timestampQueryStatus,
|
|
6315
|
+
totalGpuTimeMs,
|
|
6316
|
+
reason: timestampReason
|
|
6317
|
+
});
|
|
6318
|
+
} catch (error) {
|
|
6319
|
+
if (rayReadbackMapped) {
|
|
6320
|
+
rayCountReadbackBuffer.unmap();
|
|
6321
|
+
rayReadbackMapped = false;
|
|
6322
|
+
}
|
|
6323
|
+
if (timestampReadbackMapped) {
|
|
6324
|
+
timestampReadbackBuffer?.unmap();
|
|
6325
|
+
timestampReadbackMapped = false;
|
|
6326
|
+
}
|
|
6327
|
+
const reason = safeErrorReason(error);
|
|
6328
|
+
return Object.freeze({
|
|
6329
|
+
rayCounts: freezeRayCountTelemetry({
|
|
6330
|
+
status: "failed",
|
|
6331
|
+
expectedPrimaryRays,
|
|
6332
|
+
capturedRayCounts: recordCount,
|
|
6333
|
+
expectedRayCounts,
|
|
6334
|
+
reason: `ray-count-readback-failed:${reason}`
|
|
6335
|
+
}),
|
|
6336
|
+
timestampQueryStatus: timestampQuerySet ? "failed" : timestampUnavailableStatus,
|
|
6337
|
+
totalGpuTimeMs: null,
|
|
6338
|
+
reason: `telemetry-readback-failed:${reason}`
|
|
6339
|
+
});
|
|
6340
|
+
}
|
|
6341
|
+
}
|
|
6342
|
+
function destroy() {
|
|
6343
|
+
active = false;
|
|
6344
|
+
rayCountBuffer.destroy?.();
|
|
6345
|
+
rayCountReadbackBuffer.destroy?.();
|
|
6346
|
+
timestampQuerySet?.destroy?.();
|
|
6347
|
+
timestampResolveBuffer?.destroy?.();
|
|
6348
|
+
timestampReadbackBuffer?.destroy?.();
|
|
6349
|
+
}
|
|
6350
|
+
return Object.freeze({
|
|
6351
|
+
available: true,
|
|
6352
|
+
reason: null,
|
|
6353
|
+
memoryBytes,
|
|
6354
|
+
beginFrame,
|
|
6355
|
+
recordActiveRayCount,
|
|
6356
|
+
decorateFirstPass,
|
|
6357
|
+
decorateFinalPass,
|
|
6358
|
+
readFrame,
|
|
6359
|
+
destroy
|
|
6360
|
+
});
|
|
6361
|
+
}
|
|
6362
|
+
|
|
5966
6363
|
// src/wavefront-frame-stats.js
|
|
5967
6364
|
function resolveWavefrontRenderedSamplesPerPixel({
|
|
5968
6365
|
config,
|
|
@@ -6060,6 +6457,11 @@ function createWavefrontFrameStats({
|
|
|
6060
6457
|
maxFramePassesPerSubmission: config.maxFramePassesPerSubmission,
|
|
6061
6458
|
screenRays: config.width * config.height,
|
|
6062
6459
|
primaryRays: config.width * config.height * renderedSamplesPerPixel,
|
|
6460
|
+
secondaryRays: null,
|
|
6461
|
+
totalPathSegments: null,
|
|
6462
|
+
rayCounts: createWavefrontRayCountTelemetry(),
|
|
6463
|
+
timings: createWavefrontFrameTimingTelemetry(),
|
|
6464
|
+
telemetryMemoryBytes: 0,
|
|
6063
6465
|
sceneObjectCount: config.sceneObjectCount,
|
|
6064
6466
|
triangleCount: config.triangleCount,
|
|
6065
6467
|
emissiveTriangleCount: config.emissiveTriangleCount,
|
|
@@ -6102,7 +6504,8 @@ function createWavefrontFrameEncoder({
|
|
|
6102
6504
|
denoiseDirectResolveBindGroup,
|
|
6103
6505
|
presentPipeline,
|
|
6104
6506
|
presentBindGroup,
|
|
6105
|
-
context
|
|
6507
|
+
context,
|
|
6508
|
+
getFrameTelemetry = () => null
|
|
6106
6509
|
}) {
|
|
6107
6510
|
const resolveConfig = resolveGetter(getConfig);
|
|
6108
6511
|
const resolveBindGroups = resolveGetter(getBindGroups);
|
|
@@ -6110,9 +6513,12 @@ function createWavefrontFrameEncoder({
|
|
|
6110
6513
|
encodeTileSample(encoder, tile, configOffset, parallelism) {
|
|
6111
6514
|
const config = resolveConfig();
|
|
6112
6515
|
const bindGroups = resolveBindGroups();
|
|
6113
|
-
const
|
|
6114
|
-
|
|
6115
|
-
|
|
6516
|
+
const frameTelemetry = getFrameTelemetry();
|
|
6517
|
+
const generatePass = encoder.beginComputePass(
|
|
6518
|
+
frameTelemetry?.decorateFirstPass({
|
|
6519
|
+
label: "plasius.wavefront.generatePrimaryRaysPass"
|
|
6520
|
+
}) ?? { label: "plasius.wavefront.generatePrimaryRaysPass" }
|
|
6521
|
+
);
|
|
6116
6522
|
const tileWorkgroups = Math.ceil(tile.width * tile.height / WORKGROUP_SIZE);
|
|
6117
6523
|
generatePass.setBindGroup(0, bindGroups[0], [configOffset]);
|
|
6118
6524
|
generatePass.setPipeline(pipelines.generatePrimaryRays);
|
|
@@ -6120,6 +6526,7 @@ function createWavefrontFrameEncoder({
|
|
|
6120
6526
|
recordDirectDispatch(parallelism, [tileWorkgroups], WORKGROUP_SIZE);
|
|
6121
6527
|
generatePass.end();
|
|
6122
6528
|
for (let bounceIndex = 0; bounceIndex < config.maxDepth; bounceIndex += 1) {
|
|
6529
|
+
frameTelemetry?.recordActiveRayCount(encoder, counterBuffer, bounceIndex);
|
|
6123
6530
|
encoder.copyBufferToBuffer(
|
|
6124
6531
|
counterBuffer,
|
|
6125
6532
|
COUNTER_DISPATCH_ARGS_OFFSET,
|
|
@@ -6196,7 +6603,7 @@ function createWavefrontFrameEncoder({
|
|
|
6196
6603
|
},
|
|
6197
6604
|
encodePresent(encoder) {
|
|
6198
6605
|
const texture = context.getCurrentTexture();
|
|
6199
|
-
const
|
|
6606
|
+
const descriptor = {
|
|
6200
6607
|
label: "plasius.wavefront.presentPass",
|
|
6201
6608
|
colorAttachments: [
|
|
6202
6609
|
{
|
|
@@ -6206,7 +6613,10 @@ function createWavefrontFrameEncoder({
|
|
|
6206
6613
|
storeOp: "store"
|
|
6207
6614
|
}
|
|
6208
6615
|
]
|
|
6209
|
-
}
|
|
6616
|
+
};
|
|
6617
|
+
const passEncoder = encoder.beginRenderPass(
|
|
6618
|
+
getFrameTelemetry()?.decorateFinalPass(descriptor) ?? descriptor
|
|
6619
|
+
);
|
|
6210
6620
|
passEncoder.setPipeline(presentPipeline);
|
|
6211
6621
|
passEncoder.setBindGroup(0, presentBindGroup);
|
|
6212
6622
|
passEncoder.draw(3);
|
|
@@ -6506,36 +6916,49 @@ fn prepareMeshTrianglesAndLeaves(@builtin(global_invocation_id) globalId: vec3<u
|
|
|
6506
6916
|
let uv1 = select(vec2<f32>(0.0), vertex1.uv.xy, vertex1.uv.z > 0.5);
|
|
6507
6917
|
let uv2 = select(vec2<f32>(0.0), vertex2.uv.xy, vertex2.uv.z > 0.5);
|
|
6508
6918
|
|
|
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
|
-
);
|
|
6919
|
+
var preparedTriangle = TriangleRecord();
|
|
6920
|
+
preparedTriangle.triangleId = triangleIndex;
|
|
6921
|
+
preparedTriangle.meshId = mesh.meshId;
|
|
6922
|
+
preparedTriangle.materialKind = mesh.materialKind;
|
|
6923
|
+
preparedTriangle.flags = mesh.flags;
|
|
6924
|
+
preparedTriangle.materialRefId = mesh.materialRefId;
|
|
6925
|
+
preparedTriangle.mediumRefId = mesh.mediumRefId;
|
|
6926
|
+
preparedTriangle.materialSlot = mesh.materialSlot;
|
|
6927
|
+
preparedTriangle.v0 = vec4<f32>(vertex0.position.xyz, 0.0);
|
|
6928
|
+
preparedTriangle.v1 = vec4<f32>(vertex1.position.xyz, 0.0);
|
|
6929
|
+
preparedTriangle.v2 = vec4<f32>(vertex2.position.xyz, 0.0);
|
|
6930
|
+
preparedTriangle.n0 = vec4<f32>(n0, 0.0);
|
|
6931
|
+
preparedTriangle.n1 = vec4<f32>(n1, 0.0);
|
|
6932
|
+
preparedTriangle.n2 = vec4<f32>(n2, 0.0);
|
|
6933
|
+
preparedTriangle.uv0uv1 = vec4<f32>(uv0, uv1);
|
|
6934
|
+
preparedTriangle.uv2Pad = vec4<f32>(uv2, 0.0, 0.0);
|
|
6935
|
+
preparedTriangle.color = mesh.color;
|
|
6936
|
+
preparedTriangle.emission = mesh.emission;
|
|
6937
|
+
preparedTriangle.material = mesh.material;
|
|
6938
|
+
preparedTriangle.materialResponse = mesh.materialResponse;
|
|
6939
|
+
preparedTriangle.materialExtension = mesh.materialExtension;
|
|
6940
|
+
preparedTriangle.specularColor = mesh.specularColor;
|
|
6941
|
+
preparedTriangle.baseColorAtlas = mesh.baseColorAtlas;
|
|
6942
|
+
preparedTriangle.metallicRoughnessAtlas = mesh.metallicRoughnessAtlas;
|
|
6943
|
+
preparedTriangle.normalAtlas = mesh.normalAtlas;
|
|
6944
|
+
preparedTriangle.occlusionAtlas = mesh.occlusionAtlas;
|
|
6945
|
+
preparedTriangle.emissiveAtlas = mesh.emissiveAtlas;
|
|
6946
|
+
preparedTriangle.textureSettings = mesh.textureSettings;
|
|
6947
|
+
preparedTriangle.materialExtension2 = vec4<f32>(0.0, 0.0, 1.3, 0.0);
|
|
6948
|
+
preparedTriangle.materialExtension3 = vec4<f32>(100.0, 400.0, 0.0, 0.0);
|
|
6949
|
+
preparedTriangle.clearcoatAtlas = vec4<f32>(0.0, 0.0, 1.0, 1.0);
|
|
6950
|
+
preparedTriangle.clearcoatRoughnessAtlas = vec4<f32>(0.0, 0.0, 1.0, 1.0);
|
|
6951
|
+
preparedTriangle.clearcoatNormalAtlas = vec4<f32>(0.0, 0.0, 1.0, 1.0);
|
|
6952
|
+
preparedTriangle.transmissionAtlas = vec4<f32>(0.0, 0.0, 1.0, 1.0);
|
|
6953
|
+
preparedTriangle.thicknessAtlas = vec4<f32>(0.0, 0.0, 1.0, 1.0);
|
|
6954
|
+
preparedTriangle.sheenColorAtlas = vec4<f32>(0.0, 0.0, 1.0, 1.0);
|
|
6955
|
+
preparedTriangle.sheenRoughnessAtlas = vec4<f32>(0.0, 0.0, 1.0, 1.0);
|
|
6956
|
+
preparedTriangle.specularAtlas = vec4<f32>(0.0, 0.0, 1.0, 1.0);
|
|
6957
|
+
preparedTriangle.specularColorAtlas = vec4<f32>(0.0, 0.0, 1.0, 1.0);
|
|
6958
|
+
preparedTriangle.iridescenceAtlas = vec4<f32>(0.0, 0.0, 1.0, 1.0);
|
|
6959
|
+
preparedTriangle.iridescenceThicknessAtlas = vec4<f32>(0.0, 0.0, 1.0, 1.0);
|
|
6960
|
+
preparedTriangle.anisotropyAtlas = vec4<f32>(0.0, 0.0, 1.0, 1.0);
|
|
6961
|
+
triangles[triangleIndex] = preparedTriangle;
|
|
6539
6962
|
|
|
6540
6963
|
let leafBase = config.triangleCount - 1u;
|
|
6541
6964
|
let nodeIndex = leafBase + triangleIndex;
|
|
@@ -7205,36 +7628,7 @@ fn intersectActiveQueue(@builtin(global_invocation_id) globalId: vec3<u32>) {
|
|
|
7205
7628
|
vec4<f32>(1.0, 1.0, 1.0, 1.0)
|
|
7206
7629
|
);
|
|
7207
7630
|
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
|
-
);
|
|
7631
|
+
var hitTriangle = TriangleRecord();
|
|
7238
7632
|
|
|
7239
7633
|
for (var objectIndex = 0u; objectIndex < config.sceneObjectCount; objectIndex = objectIndex + 1u) {
|
|
7240
7634
|
let object = sceneObjects[objectIndex];
|
|
@@ -10109,7 +10503,19 @@ function createWavefrontBindGroupLayouts(device, constants) {
|
|
|
10109
10503
|
{ binding: 29, visibility: constants.shader.COMPUTE, texture: { sampleType: "float" } },
|
|
10110
10504
|
{ binding: 30, visibility: constants.shader.COMPUTE, sampler: { type: "filtering" } },
|
|
10111
10505
|
{ binding: 31, visibility: constants.shader.COMPUTE, texture: { sampleType: "float" } },
|
|
10112
|
-
{ binding: 32, visibility: constants.shader.COMPUTE, texture: { sampleType: "float" } }
|
|
10506
|
+
{ binding: 32, visibility: constants.shader.COMPUTE, texture: { sampleType: "float" } },
|
|
10507
|
+
{ binding: 33, visibility: constants.shader.COMPUTE, texture: { sampleType: "float" } },
|
|
10508
|
+
{ binding: 34, visibility: constants.shader.COMPUTE, texture: { sampleType: "float" } },
|
|
10509
|
+
{ binding: 35, visibility: constants.shader.COMPUTE, texture: { sampleType: "float" } },
|
|
10510
|
+
{ binding: 36, visibility: constants.shader.COMPUTE, texture: { sampleType: "float" } },
|
|
10511
|
+
{ binding: 37, visibility: constants.shader.COMPUTE, texture: { sampleType: "float" } },
|
|
10512
|
+
{ binding: 38, visibility: constants.shader.COMPUTE, texture: { sampleType: "float" } },
|
|
10513
|
+
{ binding: 39, visibility: constants.shader.COMPUTE, texture: { sampleType: "float" } },
|
|
10514
|
+
{ binding: 40, visibility: constants.shader.COMPUTE, texture: { sampleType: "float" } },
|
|
10515
|
+
{ binding: 41, visibility: constants.shader.COMPUTE, texture: { sampleType: "float" } },
|
|
10516
|
+
{ binding: 42, visibility: constants.shader.COMPUTE, texture: { sampleType: "float" } },
|
|
10517
|
+
{ binding: 43, visibility: constants.shader.COMPUTE, texture: { sampleType: "float" } },
|
|
10518
|
+
{ binding: 44, visibility: constants.shader.COMPUTE, texture: { sampleType: "float" } }
|
|
10113
10519
|
]
|
|
10114
10520
|
});
|
|
10115
10521
|
const acceleration = device.createBindGroupLayout({
|
|
@@ -11097,6 +11503,8 @@ async function createWavefrontPathTracingComputeRenderer(options = {}) {
|
|
|
11097
11503
|
outputView,
|
|
11098
11504
|
sampler
|
|
11099
11505
|
});
|
|
11506
|
+
let activeFrameTelemetry = null;
|
|
11507
|
+
let frameTelemetryResources = null;
|
|
11100
11508
|
const frameEncoder = createWavefrontFrameEncoder({
|
|
11101
11509
|
getConfig: () => config,
|
|
11102
11510
|
getBindGroups: () => bindGroups,
|
|
@@ -11108,7 +11516,8 @@ async function createWavefrontPathTracingComputeRenderer(options = {}) {
|
|
|
11108
11516
|
denoiseDirectResolveBindGroup,
|
|
11109
11517
|
presentPipeline,
|
|
11110
11518
|
presentBindGroup,
|
|
11111
|
-
context
|
|
11519
|
+
context,
|
|
11520
|
+
getFrameTelemetry: () => activeFrameTelemetry
|
|
11112
11521
|
});
|
|
11113
11522
|
let frame = 0;
|
|
11114
11523
|
let accelerationBuilt = !config.gpuAccelerationBuildRequired;
|
|
@@ -11121,6 +11530,16 @@ async function createWavefrontPathTracingComputeRenderer(options = {}) {
|
|
|
11121
11530
|
createGpuParallelismCounters()
|
|
11122
11531
|
);
|
|
11123
11532
|
const waitForSubmittedGpuWork2 = (waitOptions = {}) => waitForSubmittedGpuWork(device, waitOptions);
|
|
11533
|
+
function ensureFrameTelemetryResources() {
|
|
11534
|
+
if (!frameTelemetryResources) {
|
|
11535
|
+
frameTelemetryResources = createWavefrontFrameTelemetryResources({
|
|
11536
|
+
device,
|
|
11537
|
+
constants,
|
|
11538
|
+
maxRayCountRecords: tiles.length * config.samplesPerPixel * config.maxDepth
|
|
11539
|
+
});
|
|
11540
|
+
}
|
|
11541
|
+
return frameTelemetryResources;
|
|
11542
|
+
}
|
|
11124
11543
|
function resolveRenderedSamplesPerPixel(renderOptions = {}, awaitGPUCompletion = true) {
|
|
11125
11544
|
return resolveWavefrontRenderedSamplesPerPixel({
|
|
11126
11545
|
config,
|
|
@@ -11248,7 +11667,13 @@ async function createWavefrontPathTracingComputeRenderer(options = {}) {
|
|
|
11248
11667
|
frameSubmissionCount + (accelerationBuildSubmitted ? 1 : 0),
|
|
11249
11668
|
frameTimeMs,
|
|
11250
11669
|
false
|
|
11251
|
-
)
|
|
11670
|
+
),
|
|
11671
|
+
timings: createWavefrontFrameTimingTelemetry({
|
|
11672
|
+
status: "not-requested",
|
|
11673
|
+
source: "cpu-submit",
|
|
11674
|
+
timestampQueryStatus: "not-recorded",
|
|
11675
|
+
totalRenderJobTimeMs: frameTimeMs
|
|
11676
|
+
})
|
|
11252
11677
|
});
|
|
11253
11678
|
}
|
|
11254
11679
|
function dispatchFrameAwaitingGpu(frameIndex, parallelism, renderedSamplesPerPixel = config.samplesPerPixel, optionsForFrame = {}) {
|
|
@@ -11278,98 +11703,151 @@ async function createWavefrontPathTracingComputeRenderer(options = {}) {
|
|
|
11278
11703
|
const awaitGPUCompletion = renderOptions.awaitGPUCompletion !== false;
|
|
11279
11704
|
const samplingPlan = resolveRenderedSamplesPerPixel(renderOptions, awaitGPUCompletion);
|
|
11280
11705
|
const useThrottledHighSamplePath = awaitGPUCompletion && samplingPlan.renderedSamplesPerPixel >= 8;
|
|
11706
|
+
const telemetryRequested = renderOptions.readStats === true;
|
|
11707
|
+
const telemetryCanRecord = telemetryRequested && awaitGPUCompletion;
|
|
11708
|
+
const telemetryResourcesForFrame = telemetryCanRecord ? ensureFrameTelemetryResources() : null;
|
|
11709
|
+
if (telemetryResourcesForFrame?.available) {
|
|
11710
|
+
telemetryResourcesForFrame.beginFrame();
|
|
11711
|
+
activeFrameTelemetry = telemetryResourcesForFrame;
|
|
11712
|
+
}
|
|
11281
11713
|
const frameStartTimeMs = nowMs3();
|
|
11282
11714
|
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,
|
|
11715
|
+
try {
|
|
11716
|
+
if (useThrottledHighSamplePath) {
|
|
11717
|
+
frame += 1;
|
|
11718
|
+
const frameIndex = frame + config.frameIndex;
|
|
11719
|
+
const parallelismCounters = createGpuParallelismCounters();
|
|
11720
|
+
const accelerationBuildSubmitted = dispatchGpuAccelerationBuild(frameIndex, parallelismCounters);
|
|
11721
|
+
let frameSubmissionCount = 0;
|
|
11722
|
+
let frameConfigSlot = 0;
|
|
11723
|
+
if (accelerationBuildSubmitted) {
|
|
11724
|
+
const accelerationWaitOptions = {
|
|
11725
|
+
...estimateSubmittedGpuWorkTiming(
|
|
11726
|
+
{ ...config, renderedSamplesPerPixel: 1 },
|
|
11727
|
+
1,
|
|
11728
|
+
renderOptions.submittedWorkTimeoutMs,
|
|
11729
|
+
{ includeAccelerationBuild: true }
|
|
11730
|
+
),
|
|
11731
|
+
allowTimeout: false
|
|
11732
|
+
};
|
|
11733
|
+
await waitForSubmittedGpuWork2(accelerationWaitOptions);
|
|
11734
|
+
}
|
|
11735
|
+
for (let tileIndex = 0; tileIndex < tiles.length; tileIndex += 1) {
|
|
11736
|
+
const tileRangeDispatch = dispatchFrameAwaitingGpu(
|
|
11737
|
+
frameIndex,
|
|
11738
|
+
parallelismCounters,
|
|
11739
|
+
samplingPlan.renderedSamplesPerPixel,
|
|
11325
11740
|
{
|
|
11326
|
-
|
|
11741
|
+
sampleRangeStart: 0,
|
|
11742
|
+
sampleRangeEnd: samplingPlan.renderedSamplesPerPixel,
|
|
11743
|
+
tileStartIndex: tileIndex,
|
|
11744
|
+
tileEndIndex: tileIndex + 1,
|
|
11745
|
+
startingSubmissionCount: frameSubmissionCount,
|
|
11746
|
+
startingSlot: frameConfigSlot,
|
|
11747
|
+
includeDenoise: tileIndex + 1 >= tiles.length,
|
|
11327
11748
|
includePresent: tileIndex + 1 >= tiles.length
|
|
11328
11749
|
}
|
|
11329
|
-
)
|
|
11750
|
+
);
|
|
11751
|
+
frameSubmissionCount = tileRangeDispatch.submissionCount;
|
|
11752
|
+
frameConfigSlot = tileRangeDispatch.slot;
|
|
11753
|
+
const tileWaitOptions = {
|
|
11754
|
+
...estimateSubmittedGpuWorkTiming(
|
|
11755
|
+
{ ...config, renderedSamplesPerPixel: samplingPlan.renderedSamplesPerPixel },
|
|
11756
|
+
1,
|
|
11757
|
+
renderOptions.submittedWorkTimeoutMs,
|
|
11758
|
+
{
|
|
11759
|
+
includeDenoise: tileIndex + 1 >= tiles.length && config.denoise,
|
|
11760
|
+
includePresent: tileIndex + 1 >= tiles.length
|
|
11761
|
+
}
|
|
11762
|
+
),
|
|
11763
|
+
allowTimeout: false
|
|
11764
|
+
};
|
|
11765
|
+
await waitForSubmittedGpuWork2(tileWaitOptions);
|
|
11766
|
+
}
|
|
11767
|
+
frameStats = createFrameStats({
|
|
11768
|
+
frameIndex,
|
|
11769
|
+
accelerationBuildSubmitted,
|
|
11770
|
+
frameSubmissionCount,
|
|
11771
|
+
parallelismCounters,
|
|
11772
|
+
renderedSamplesPerPixel: samplingPlan.renderedSamplesPerPixel,
|
|
11773
|
+
targetSamplesPerPixel: samplingPlan.targetSamplesPerPixel,
|
|
11774
|
+
frameTimeBudgetMs: samplingPlan.frameTimeBudgetMs,
|
|
11775
|
+
budgetConstrained: samplingPlan.budgetConstrained
|
|
11776
|
+
});
|
|
11777
|
+
} else {
|
|
11778
|
+
const submittedWorkTiming = estimateSubmittedGpuWorkTiming(
|
|
11779
|
+
{ ...config, renderedSamplesPerPixel: samplingPlan.renderedSamplesPerPixel },
|
|
11780
|
+
tiles.length,
|
|
11781
|
+
renderOptions.submittedWorkTimeoutMs,
|
|
11782
|
+
{ includeAccelerationBuild: config.gpuAccelerationBuildRequired && !accelerationBuilt }
|
|
11783
|
+
);
|
|
11784
|
+
const submissionWaitOptions = awaitGPUCompletion ? {
|
|
11785
|
+
timeoutMs: submittedWorkTiming.timeoutMs,
|
|
11786
|
+
maxWaitMs: submittedWorkTiming.maxWaitMs,
|
|
11330
11787
|
allowTimeout: false
|
|
11788
|
+
} : {
|
|
11789
|
+
timeoutMs: submittedWorkTiming.timeoutMs,
|
|
11790
|
+
maxWaitMs: submittedWorkTiming.maxWaitMs
|
|
11331
11791
|
};
|
|
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);
|
|
11792
|
+
frameStats = renderOnce(renderOptions, samplingPlan);
|
|
11793
|
+
if (awaitGPUCompletion) {
|
|
11794
|
+
await waitForSubmittedGpuWork2(submissionWaitOptions);
|
|
11795
|
+
}
|
|
11362
11796
|
}
|
|
11797
|
+
} finally {
|
|
11798
|
+
activeFrameTelemetry = null;
|
|
11363
11799
|
}
|
|
11364
11800
|
const frameTimeMs = Math.max(0, nowMs3() - frameStartTimeMs);
|
|
11365
11801
|
if (awaitGPUCompletion) {
|
|
11366
11802
|
lastCompletedFrameTimeMs = frameTimeMs;
|
|
11367
11803
|
lastCompletedSamplesPerPixel = frameStats.renderedSamplesPerPixel ?? frameStats.samplesPerPixel;
|
|
11368
11804
|
}
|
|
11805
|
+
const expectedPrimaryRays = config.width * config.height * samplingPlan.renderedSamplesPerPixel;
|
|
11806
|
+
const expectedRayCounts = tiles.length * samplingPlan.renderedSamplesPerPixel * config.maxDepth;
|
|
11807
|
+
const telemetryReadback = telemetryCanRecord ? await telemetryResourcesForFrame.readFrame({
|
|
11808
|
+
expectedPrimaryRays,
|
|
11809
|
+
expectedRayCounts,
|
|
11810
|
+
waitForSubmittedGpuWork: waitForSubmittedGpuWork2
|
|
11811
|
+
}) : null;
|
|
11812
|
+
const rayCounts = !telemetryRequested ? createWavefrontRayCountTelemetry() : !awaitGPUCompletion ? createWavefrontRayCountTelemetry({
|
|
11813
|
+
status: "unavailable",
|
|
11814
|
+
expectedPrimaryRays,
|
|
11815
|
+
expectedRayCounts,
|
|
11816
|
+
reason: "gpu-work-not-awaited"
|
|
11817
|
+
}) : telemetryReadback.rayCounts;
|
|
11818
|
+
const timings = !telemetryRequested ? createWavefrontFrameTimingTelemetry({
|
|
11819
|
+
status: "not-requested",
|
|
11820
|
+
source: awaitGPUCompletion ? "queue-completion" : "cpu-submit",
|
|
11821
|
+
timestampQueryStatus: "not-recorded",
|
|
11822
|
+
totalRenderJobTimeMs: frameTimeMs
|
|
11823
|
+
}) : !awaitGPUCompletion ? createWavefrontFrameTimingTelemetry({
|
|
11824
|
+
status: "unavailable",
|
|
11825
|
+
source: "cpu-submit",
|
|
11826
|
+
timestampQueryStatus: "not-recorded",
|
|
11827
|
+
totalRenderJobTimeMs: frameTimeMs,
|
|
11828
|
+
reason: "gpu-work-not-awaited"
|
|
11829
|
+
}) : telemetryReadback.timestampQueryStatus === "available" ? createWavefrontFrameTimingTelemetry({
|
|
11830
|
+
status: "available",
|
|
11831
|
+
source: "timestamp-query",
|
|
11832
|
+
timestampQueryStatus: "available",
|
|
11833
|
+
totalGpuTimeMs: telemetryReadback.totalGpuTimeMs,
|
|
11834
|
+
totalRenderJobTimeMs: frameTimeMs
|
|
11835
|
+
}) : createWavefrontFrameTimingTelemetry({
|
|
11836
|
+
status: "fallback",
|
|
11837
|
+
source: "queue-completion",
|
|
11838
|
+
timestampQueryStatus: telemetryReadback.timestampQueryStatus,
|
|
11839
|
+
totalRenderJobTimeMs: frameTimeMs,
|
|
11840
|
+
reason: telemetryReadback.reason
|
|
11841
|
+
});
|
|
11369
11842
|
const deviceLossStatus = awaitGPUCompletion ? "not-detected" : typeof device.lost?.then === "function" ? "pending" : "not-exposed";
|
|
11370
11843
|
frameStats = Object.freeze({
|
|
11371
11844
|
...frameStats,
|
|
11372
11845
|
deviceLossStatus,
|
|
11846
|
+
secondaryRays: rayCounts.status === "available" ? rayCounts.secondaryRays : null,
|
|
11847
|
+
totalPathSegments: rayCounts.status === "available" ? rayCounts.totalPathSegments : null,
|
|
11848
|
+
rayCounts,
|
|
11849
|
+
timings,
|
|
11850
|
+
telemetryMemoryBytes: telemetryResourcesForFrame?.memoryBytes ?? 0,
|
|
11373
11851
|
gpuWorkerJobs: createGpuWorkerJobDiagnostics(
|
|
11374
11852
|
frameStats.gpuParallelism,
|
|
11375
11853
|
frameStats.commandSubmissions,
|
|
@@ -11478,6 +11956,7 @@ async function createWavefrontPathTracingComputeRenderer(options = {}) {
|
|
|
11478
11956
|
});
|
|
11479
11957
|
}
|
|
11480
11958
|
function destroy() {
|
|
11959
|
+
frameTelemetryResources?.destroy?.();
|
|
11481
11960
|
activeQueue.destroy?.();
|
|
11482
11961
|
nextQueue.destroy?.();
|
|
11483
11962
|
hitBuffer.destroy?.();
|