@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/package.json
CHANGED
package/src/index.d.ts
CHANGED
|
@@ -1011,6 +1011,8 @@ export interface CreateWavefrontPathTracingComputeRendererOptions {
|
|
|
1011
1011
|
readonly navigator?: Navigator | { gpu?: GPU };
|
|
1012
1012
|
readonly document?: Document;
|
|
1013
1013
|
readonly deviceDescriptor?: GPUDeviceDescriptor;
|
|
1014
|
+
/** Opportunistically request WebGPU timestamp-query support when exposed. Defaults to true. */
|
|
1015
|
+
readonly gpuTimestamps?: boolean;
|
|
1014
1016
|
readonly requiredLimits?: Partial<Record<string, number>>;
|
|
1015
1017
|
readonly powerPreference?: GPUPowerPreference;
|
|
1016
1018
|
readonly alpha?: boolean;
|
|
@@ -1122,6 +1124,31 @@ export interface WavefrontPathTracingComputeRenderer {
|
|
|
1122
1124
|
destroy(): void;
|
|
1123
1125
|
}
|
|
1124
1126
|
|
|
1127
|
+
export interface WavefrontRayCountTelemetry {
|
|
1128
|
+
readonly status: "not-requested" | "available" | "unavailable" | "failed";
|
|
1129
|
+
readonly source: "gpu-active-queue-readback" | null;
|
|
1130
|
+
readonly expectedPrimaryRays: number | null;
|
|
1131
|
+
readonly observedPrimaryRays: number | null;
|
|
1132
|
+
readonly secondaryRays: number | null;
|
|
1133
|
+
readonly totalPathSegments: number | null;
|
|
1134
|
+
readonly bounceHistogram: readonly number[];
|
|
1135
|
+
readonly capturedRayCounts: number;
|
|
1136
|
+
readonly expectedRayCounts: number;
|
|
1137
|
+
readonly reason: string | null;
|
|
1138
|
+
}
|
|
1139
|
+
|
|
1140
|
+
export interface WavefrontFrameTimingTelemetry {
|
|
1141
|
+
readonly status: "not-requested" | "available" | "fallback" | "unavailable" | "failed";
|
|
1142
|
+
readonly source: "timestamp-query" | "queue-completion" | "cpu-submit" | null;
|
|
1143
|
+
readonly timestampQueryStatus: "not-recorded" | "available" | "unsupported" | "failed";
|
|
1144
|
+
readonly totalGpuTimeMs: number | null;
|
|
1145
|
+
readonly totalRenderJobTimeMs: number;
|
|
1146
|
+
readonly classificationTimeMs: number | null;
|
|
1147
|
+
readonly compactionTimeMs: number | null;
|
|
1148
|
+
readonly samplingTimeMs: number | null;
|
|
1149
|
+
readonly reason: string | null;
|
|
1150
|
+
}
|
|
1151
|
+
|
|
1125
1152
|
export interface WavefrontPathTracingComputeFrameStats {
|
|
1126
1153
|
readonly frame: number;
|
|
1127
1154
|
readonly width: number;
|
|
@@ -1136,6 +1163,11 @@ export interface WavefrontPathTracingComputeFrameStats {
|
|
|
1136
1163
|
readonly maxFramePassesPerSubmission: number;
|
|
1137
1164
|
readonly screenRays: number;
|
|
1138
1165
|
readonly primaryRays: number;
|
|
1166
|
+
readonly secondaryRays: number | null;
|
|
1167
|
+
readonly totalPathSegments: number | null;
|
|
1168
|
+
readonly rayCounts: WavefrontRayCountTelemetry;
|
|
1169
|
+
readonly timings: WavefrontFrameTimingTelemetry;
|
|
1170
|
+
readonly telemetryMemoryBytes: number;
|
|
1139
1171
|
readonly sceneObjectCount: number;
|
|
1140
1172
|
readonly triangleCount: number;
|
|
1141
1173
|
readonly emissiveTriangleCount: number;
|
|
@@ -1184,6 +1216,7 @@ export interface WavefrontPathTracingComputeFrameStats {
|
|
|
1184
1216
|
invalidSamples: number;
|
|
1185
1217
|
legacyClampEquivalentSamples: number;
|
|
1186
1218
|
}>;
|
|
1219
|
+
rayCountStatus: WavefrontRayCountTelemetry["status"];
|
|
1187
1220
|
memory: Readonly<{
|
|
1188
1221
|
totalBytes: number;
|
|
1189
1222
|
breakdown: WavefrontPathTracingMemoryEstimate | null;
|
|
@@ -1418,6 +1451,7 @@ export const rendererWavefrontComputeStatsStride: 8;
|
|
|
1418
1451
|
export const wavefrontPathTracingComputeLimits: Readonly<{
|
|
1419
1452
|
workgroupSize: 64;
|
|
1420
1453
|
traceStorageBufferBindings: number;
|
|
1454
|
+
traceSampledTextureBindings: number;
|
|
1421
1455
|
rayRecordBytes: 96;
|
|
1422
1456
|
hitRecordBytes: 256;
|
|
1423
1457
|
sceneObjectRecordBytes: 160;
|
package/src/wavefront-compute.js
CHANGED
|
@@ -50,6 +50,11 @@ import {
|
|
|
50
50
|
createWavefrontFrameStats,
|
|
51
51
|
resolveWavefrontRenderedSamplesPerPixel,
|
|
52
52
|
} from "./wavefront-frame-stats.js";
|
|
53
|
+
import {
|
|
54
|
+
createWavefrontFrameTelemetryResources,
|
|
55
|
+
createWavefrontFrameTimingTelemetry,
|
|
56
|
+
createWavefrontRayCountTelemetry,
|
|
57
|
+
} from "./wavefront-frame-telemetry.js";
|
|
53
58
|
import { createWavefrontFrameEncoder } from "./wavefront-frame-encoder.js";
|
|
54
59
|
import {
|
|
55
60
|
dispatchWavefrontFrame,
|
|
@@ -573,6 +578,8 @@ export async function createWavefrontPathTracingComputeRenderer(options = {}) {
|
|
|
573
578
|
outputView,
|
|
574
579
|
sampler,
|
|
575
580
|
});
|
|
581
|
+
let activeFrameTelemetry = null;
|
|
582
|
+
let frameTelemetryResources = null;
|
|
576
583
|
const frameEncoder = createWavefrontFrameEncoder({
|
|
577
584
|
getConfig: () => config,
|
|
578
585
|
getBindGroups: () => bindGroups,
|
|
@@ -585,6 +592,7 @@ export async function createWavefrontPathTracingComputeRenderer(options = {}) {
|
|
|
585
592
|
presentPipeline,
|
|
586
593
|
presentBindGroup,
|
|
587
594
|
context,
|
|
595
|
+
getFrameTelemetry: () => activeFrameTelemetry,
|
|
588
596
|
});
|
|
589
597
|
|
|
590
598
|
let frame = 0;
|
|
@@ -600,6 +608,18 @@ export async function createWavefrontPathTracingComputeRenderer(options = {}) {
|
|
|
600
608
|
const waitForSubmittedGpuWork = (waitOptions = {}) =>
|
|
601
609
|
waitForSubmittedGpuWorkForDevice(device, waitOptions);
|
|
602
610
|
|
|
611
|
+
function ensureFrameTelemetryResources() {
|
|
612
|
+
if (!frameTelemetryResources) {
|
|
613
|
+
frameTelemetryResources = createWavefrontFrameTelemetryResources({
|
|
614
|
+
device,
|
|
615
|
+
constants,
|
|
616
|
+
maxRayCountRecords:
|
|
617
|
+
tiles.length * config.samplesPerPixel * config.maxDepth,
|
|
618
|
+
});
|
|
619
|
+
}
|
|
620
|
+
return frameTelemetryResources;
|
|
621
|
+
}
|
|
622
|
+
|
|
603
623
|
function resolveRenderedSamplesPerPixel(renderOptions = {}, awaitGPUCompletion = true) {
|
|
604
624
|
return resolveWavefrontRenderedSamplesPerPixel({
|
|
605
625
|
config,
|
|
@@ -735,6 +755,12 @@ export async function createWavefrontPathTracingComputeRenderer(options = {}) {
|
|
|
735
755
|
frameTimeMs,
|
|
736
756
|
false
|
|
737
757
|
),
|
|
758
|
+
timings: createWavefrontFrameTimingTelemetry({
|
|
759
|
+
status: "not-requested",
|
|
760
|
+
source: "cpu-submit",
|
|
761
|
+
timestampQueryStatus: "not-recorded",
|
|
762
|
+
totalRenderJobTimeMs: frameTimeMs,
|
|
763
|
+
}),
|
|
738
764
|
});
|
|
739
765
|
}
|
|
740
766
|
|
|
@@ -773,96 +799,160 @@ export async function createWavefrontPathTracingComputeRenderer(options = {}) {
|
|
|
773
799
|
const samplingPlan = resolveRenderedSamplesPerPixel(renderOptions, awaitGPUCompletion);
|
|
774
800
|
const useThrottledHighSamplePath =
|
|
775
801
|
awaitGPUCompletion && samplingPlan.renderedSamplesPerPixel >= 8;
|
|
802
|
+
const telemetryRequested = renderOptions.readStats === true;
|
|
803
|
+
const telemetryCanRecord = telemetryRequested && awaitGPUCompletion;
|
|
804
|
+
const telemetryResourcesForFrame = telemetryCanRecord
|
|
805
|
+
? ensureFrameTelemetryResources()
|
|
806
|
+
: null;
|
|
807
|
+
if (telemetryResourcesForFrame?.available) {
|
|
808
|
+
telemetryResourcesForFrame.beginFrame();
|
|
809
|
+
activeFrameTelemetry = telemetryResourcesForFrame;
|
|
810
|
+
}
|
|
776
811
|
const frameStartTimeMs = nowMs();
|
|
777
812
|
let frameStats;
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
sampleRangeStart: 0,
|
|
804
|
-
sampleRangeEnd: samplingPlan.renderedSamplesPerPixel,
|
|
805
|
-
tileStartIndex: tileIndex,
|
|
806
|
-
tileEndIndex: tileIndex + 1,
|
|
807
|
-
startingSubmissionCount: frameSubmissionCount,
|
|
808
|
-
startingSlot: frameConfigSlot,
|
|
809
|
-
includeDenoise: tileIndex + 1 >= tiles.length,
|
|
810
|
-
includePresent: tileIndex + 1 >= tiles.length,
|
|
811
|
-
}
|
|
812
|
-
);
|
|
813
|
-
frameSubmissionCount = tileRangeDispatch.submissionCount;
|
|
814
|
-
frameConfigSlot = tileRangeDispatch.slot;
|
|
815
|
-
const tileWaitOptions = {
|
|
816
|
-
...estimateSubmittedGpuWorkTiming(
|
|
817
|
-
{ ...config, renderedSamplesPerPixel: samplingPlan.renderedSamplesPerPixel },
|
|
818
|
-
1,
|
|
819
|
-
renderOptions.submittedWorkTimeoutMs,
|
|
813
|
+
try {
|
|
814
|
+
if (useThrottledHighSamplePath) {
|
|
815
|
+
frame += 1;
|
|
816
|
+
const frameIndex = frame + config.frameIndex;
|
|
817
|
+
const parallelismCounters = createGpuParallelismCounters();
|
|
818
|
+
const accelerationBuildSubmitted = dispatchGpuAccelerationBuild(frameIndex, parallelismCounters);
|
|
819
|
+
let frameSubmissionCount = 0;
|
|
820
|
+
let frameConfigSlot = 0;
|
|
821
|
+
if (accelerationBuildSubmitted) {
|
|
822
|
+
const accelerationWaitOptions = {
|
|
823
|
+
...estimateSubmittedGpuWorkTiming(
|
|
824
|
+
{ ...config, renderedSamplesPerPixel: 1 },
|
|
825
|
+
1,
|
|
826
|
+
renderOptions.submittedWorkTimeoutMs,
|
|
827
|
+
{ includeAccelerationBuild: true }
|
|
828
|
+
),
|
|
829
|
+
allowTimeout: false,
|
|
830
|
+
};
|
|
831
|
+
await waitForSubmittedGpuWork(accelerationWaitOptions);
|
|
832
|
+
}
|
|
833
|
+
for (let tileIndex = 0; tileIndex < tiles.length; tileIndex += 1) {
|
|
834
|
+
const tileRangeDispatch = dispatchFrameAwaitingGpu(
|
|
835
|
+
frameIndex,
|
|
836
|
+
parallelismCounters,
|
|
837
|
+
samplingPlan.renderedSamplesPerPixel,
|
|
820
838
|
{
|
|
821
|
-
|
|
839
|
+
sampleRangeStart: 0,
|
|
840
|
+
sampleRangeEnd: samplingPlan.renderedSamplesPerPixel,
|
|
841
|
+
tileStartIndex: tileIndex,
|
|
842
|
+
tileEndIndex: tileIndex + 1,
|
|
843
|
+
startingSubmissionCount: frameSubmissionCount,
|
|
844
|
+
startingSlot: frameConfigSlot,
|
|
845
|
+
includeDenoise: tileIndex + 1 >= tiles.length,
|
|
822
846
|
includePresent: tileIndex + 1 >= tiles.length,
|
|
823
847
|
}
|
|
824
|
-
)
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
budgetConstrained: samplingPlan.budgetConstrained,
|
|
838
|
-
});
|
|
839
|
-
} else {
|
|
840
|
-
const submittedWorkTiming = estimateSubmittedGpuWorkTiming(
|
|
841
|
-
{ ...config, renderedSamplesPerPixel: samplingPlan.renderedSamplesPerPixel },
|
|
842
|
-
tiles.length,
|
|
843
|
-
renderOptions.submittedWorkTimeoutMs,
|
|
844
|
-
{ includeAccelerationBuild: config.gpuAccelerationBuildRequired && !accelerationBuilt }
|
|
845
|
-
);
|
|
846
|
-
const submissionWaitOptions = awaitGPUCompletion
|
|
847
|
-
? {
|
|
848
|
-
timeoutMs: submittedWorkTiming.timeoutMs,
|
|
849
|
-
maxWaitMs: submittedWorkTiming.maxWaitMs,
|
|
848
|
+
);
|
|
849
|
+
frameSubmissionCount = tileRangeDispatch.submissionCount;
|
|
850
|
+
frameConfigSlot = tileRangeDispatch.slot;
|
|
851
|
+
const tileWaitOptions = {
|
|
852
|
+
...estimateSubmittedGpuWorkTiming(
|
|
853
|
+
{ ...config, renderedSamplesPerPixel: samplingPlan.renderedSamplesPerPixel },
|
|
854
|
+
1,
|
|
855
|
+
renderOptions.submittedWorkTimeoutMs,
|
|
856
|
+
{
|
|
857
|
+
includeDenoise: tileIndex + 1 >= tiles.length && config.denoise,
|
|
858
|
+
includePresent: tileIndex + 1 >= tiles.length,
|
|
859
|
+
}
|
|
860
|
+
),
|
|
850
861
|
allowTimeout: false,
|
|
851
|
-
}
|
|
852
|
-
: {
|
|
853
|
-
timeoutMs: submittedWorkTiming.timeoutMs,
|
|
854
|
-
maxWaitMs: submittedWorkTiming.maxWaitMs,
|
|
855
862
|
};
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
863
|
+
await waitForSubmittedGpuWork(tileWaitOptions);
|
|
864
|
+
}
|
|
865
|
+
frameStats = createFrameStats({
|
|
866
|
+
frameIndex,
|
|
867
|
+
accelerationBuildSubmitted,
|
|
868
|
+
frameSubmissionCount,
|
|
869
|
+
parallelismCounters,
|
|
870
|
+
renderedSamplesPerPixel: samplingPlan.renderedSamplesPerPixel,
|
|
871
|
+
targetSamplesPerPixel: samplingPlan.targetSamplesPerPixel,
|
|
872
|
+
frameTimeBudgetMs: samplingPlan.frameTimeBudgetMs,
|
|
873
|
+
budgetConstrained: samplingPlan.budgetConstrained,
|
|
874
|
+
});
|
|
875
|
+
} else {
|
|
876
|
+
const submittedWorkTiming = estimateSubmittedGpuWorkTiming(
|
|
877
|
+
{ ...config, renderedSamplesPerPixel: samplingPlan.renderedSamplesPerPixel },
|
|
878
|
+
tiles.length,
|
|
879
|
+
renderOptions.submittedWorkTimeoutMs,
|
|
880
|
+
{ includeAccelerationBuild: config.gpuAccelerationBuildRequired && !accelerationBuilt }
|
|
881
|
+
);
|
|
882
|
+
const submissionWaitOptions = awaitGPUCompletion
|
|
883
|
+
? {
|
|
884
|
+
timeoutMs: submittedWorkTiming.timeoutMs,
|
|
885
|
+
maxWaitMs: submittedWorkTiming.maxWaitMs,
|
|
886
|
+
allowTimeout: false,
|
|
887
|
+
}
|
|
888
|
+
: {
|
|
889
|
+
timeoutMs: submittedWorkTiming.timeoutMs,
|
|
890
|
+
maxWaitMs: submittedWorkTiming.maxWaitMs,
|
|
891
|
+
};
|
|
892
|
+
frameStats = renderOnce(renderOptions, samplingPlan);
|
|
893
|
+
if (awaitGPUCompletion) {
|
|
894
|
+
await waitForSubmittedGpuWork(submissionWaitOptions);
|
|
895
|
+
}
|
|
859
896
|
}
|
|
897
|
+
} finally {
|
|
898
|
+
activeFrameTelemetry = null;
|
|
860
899
|
}
|
|
861
900
|
const frameTimeMs = Math.max(0, nowMs() - frameStartTimeMs);
|
|
862
901
|
if (awaitGPUCompletion) {
|
|
863
902
|
lastCompletedFrameTimeMs = frameTimeMs;
|
|
864
903
|
lastCompletedSamplesPerPixel = frameStats.renderedSamplesPerPixel ?? frameStats.samplesPerPixel;
|
|
865
904
|
}
|
|
905
|
+
const expectedPrimaryRays =
|
|
906
|
+
config.width * config.height * samplingPlan.renderedSamplesPerPixel;
|
|
907
|
+
const expectedRayCounts =
|
|
908
|
+
tiles.length * samplingPlan.renderedSamplesPerPixel * config.maxDepth;
|
|
909
|
+
const telemetryReadback = telemetryCanRecord
|
|
910
|
+
? await telemetryResourcesForFrame.readFrame({
|
|
911
|
+
expectedPrimaryRays,
|
|
912
|
+
expectedRayCounts,
|
|
913
|
+
waitForSubmittedGpuWork,
|
|
914
|
+
})
|
|
915
|
+
: null;
|
|
916
|
+
const rayCounts = !telemetryRequested
|
|
917
|
+
? createWavefrontRayCountTelemetry()
|
|
918
|
+
: !awaitGPUCompletion
|
|
919
|
+
? createWavefrontRayCountTelemetry({
|
|
920
|
+
status: "unavailable",
|
|
921
|
+
expectedPrimaryRays,
|
|
922
|
+
expectedRayCounts,
|
|
923
|
+
reason: "gpu-work-not-awaited",
|
|
924
|
+
})
|
|
925
|
+
: telemetryReadback.rayCounts;
|
|
926
|
+
const timings = !telemetryRequested
|
|
927
|
+
? createWavefrontFrameTimingTelemetry({
|
|
928
|
+
status: "not-requested",
|
|
929
|
+
source: awaitGPUCompletion ? "queue-completion" : "cpu-submit",
|
|
930
|
+
timestampQueryStatus: "not-recorded",
|
|
931
|
+
totalRenderJobTimeMs: frameTimeMs,
|
|
932
|
+
})
|
|
933
|
+
: !awaitGPUCompletion
|
|
934
|
+
? createWavefrontFrameTimingTelemetry({
|
|
935
|
+
status: "unavailable",
|
|
936
|
+
source: "cpu-submit",
|
|
937
|
+
timestampQueryStatus: "not-recorded",
|
|
938
|
+
totalRenderJobTimeMs: frameTimeMs,
|
|
939
|
+
reason: "gpu-work-not-awaited",
|
|
940
|
+
})
|
|
941
|
+
: telemetryReadback.timestampQueryStatus === "available"
|
|
942
|
+
? createWavefrontFrameTimingTelemetry({
|
|
943
|
+
status: "available",
|
|
944
|
+
source: "timestamp-query",
|
|
945
|
+
timestampQueryStatus: "available",
|
|
946
|
+
totalGpuTimeMs: telemetryReadback.totalGpuTimeMs,
|
|
947
|
+
totalRenderJobTimeMs: frameTimeMs,
|
|
948
|
+
})
|
|
949
|
+
: createWavefrontFrameTimingTelemetry({
|
|
950
|
+
status: "fallback",
|
|
951
|
+
source: "queue-completion",
|
|
952
|
+
timestampQueryStatus: telemetryReadback.timestampQueryStatus,
|
|
953
|
+
totalRenderJobTimeMs: frameTimeMs,
|
|
954
|
+
reason: telemetryReadback.reason,
|
|
955
|
+
});
|
|
866
956
|
const deviceLossStatus =
|
|
867
957
|
awaitGPUCompletion
|
|
868
958
|
? "not-detected"
|
|
@@ -872,6 +962,13 @@ export async function createWavefrontPathTracingComputeRenderer(options = {}) {
|
|
|
872
962
|
frameStats = Object.freeze({
|
|
873
963
|
...frameStats,
|
|
874
964
|
deviceLossStatus,
|
|
965
|
+
secondaryRays:
|
|
966
|
+
rayCounts.status === "available" ? rayCounts.secondaryRays : null,
|
|
967
|
+
totalPathSegments:
|
|
968
|
+
rayCounts.status === "available" ? rayCounts.totalPathSegments : null,
|
|
969
|
+
rayCounts,
|
|
970
|
+
timings,
|
|
971
|
+
telemetryMemoryBytes: telemetryResourcesForFrame?.memoryBytes ?? 0,
|
|
875
972
|
gpuWorkerJobs: createGpuWorkerJobDiagnostics(
|
|
876
973
|
frameStats.gpuParallelism,
|
|
877
974
|
frameStats.commandSubmissions,
|
|
@@ -991,6 +1088,7 @@ export async function createWavefrontPathTracingComputeRenderer(options = {}) {
|
|
|
991
1088
|
}
|
|
992
1089
|
|
|
993
1090
|
function destroy() {
|
|
1091
|
+
frameTelemetryResources?.destroy?.();
|
|
994
1092
|
activeQueue.destroy?.();
|
|
995
1093
|
nextQueue.destroy?.();
|
|
996
1094
|
hitBuffer.destroy?.();
|
package/src/wavefront-core.js
CHANGED
|
@@ -58,6 +58,7 @@ export const COUNTER_TRANSPORT_RESIDUAL_LUMINANCE_OFFSET = 22;
|
|
|
58
58
|
export const COUNTER_TRANSPORT_ZERO_TERMINATION_OFFSET = 23;
|
|
59
59
|
export const COUNTER_TRANSPORT_CHECKSUM_OFFSET = 24;
|
|
60
60
|
export const TRACE_STORAGE_BUFFER_BINDINGS = 10;
|
|
61
|
+
export const TRACE_SAMPLED_TEXTURE_BINDINGS = 21;
|
|
61
62
|
export const BRDF_LUT_UPLOAD_CACHE = new Map();
|
|
62
63
|
export const HIT_TYPE_SURFACE = 0;
|
|
63
64
|
export const HIT_TYPE_EMISSIVE = 1;
|
|
@@ -130,6 +131,7 @@ export const EMPTY_TERMINATION_METRICS = Object.freeze({
|
|
|
130
131
|
export const wavefrontPathTracingComputeLimits = Object.freeze({
|
|
131
132
|
workgroupSize: WORKGROUP_SIZE,
|
|
132
133
|
traceStorageBufferBindings: TRACE_STORAGE_BUFFER_BINDINGS,
|
|
134
|
+
traceSampledTextureBindings: TRACE_SAMPLED_TEXTURE_BINDINGS,
|
|
133
135
|
rayRecordBytes: RAY_RECORD_BYTES,
|
|
134
136
|
hitRecordBytes: HIT_RECORD_BYTES,
|
|
135
137
|
sceneObjectRecordBytes: SCENE_OBJECT_RECORD_BYTES,
|
|
@@ -24,6 +24,7 @@ export function createWavefrontFrameEncoder({
|
|
|
24
24
|
presentPipeline,
|
|
25
25
|
presentBindGroup,
|
|
26
26
|
context,
|
|
27
|
+
getFrameTelemetry = () => null,
|
|
27
28
|
}) {
|
|
28
29
|
const resolveConfig = resolveGetter(getConfig);
|
|
29
30
|
const resolveBindGroups = resolveGetter(getBindGroups);
|
|
@@ -32,9 +33,12 @@ export function createWavefrontFrameEncoder({
|
|
|
32
33
|
encodeTileSample(encoder, tile, configOffset, parallelism) {
|
|
33
34
|
const config = resolveConfig();
|
|
34
35
|
const bindGroups = resolveBindGroups();
|
|
35
|
-
const
|
|
36
|
-
|
|
37
|
-
|
|
36
|
+
const frameTelemetry = getFrameTelemetry();
|
|
37
|
+
const generatePass = encoder.beginComputePass(
|
|
38
|
+
frameTelemetry?.decorateFirstPass({
|
|
39
|
+
label: "plasius.wavefront.generatePrimaryRaysPass",
|
|
40
|
+
}) ?? { label: "plasius.wavefront.generatePrimaryRaysPass" }
|
|
41
|
+
);
|
|
38
42
|
const tileWorkgroups = Math.ceil((tile.width * tile.height) / WORKGROUP_SIZE);
|
|
39
43
|
|
|
40
44
|
generatePass.setBindGroup(0, bindGroups[0], [configOffset]);
|
|
@@ -44,6 +48,7 @@ export function createWavefrontFrameEncoder({
|
|
|
44
48
|
generatePass.end();
|
|
45
49
|
|
|
46
50
|
for (let bounceIndex = 0; bounceIndex < config.maxDepth; bounceIndex += 1) {
|
|
51
|
+
frameTelemetry?.recordActiveRayCount(encoder, counterBuffer, bounceIndex);
|
|
47
52
|
encoder.copyBufferToBuffer(
|
|
48
53
|
counterBuffer,
|
|
49
54
|
COUNTER_DISPATCH_ARGS_OFFSET,
|
|
@@ -125,7 +130,7 @@ export function createWavefrontFrameEncoder({
|
|
|
125
130
|
|
|
126
131
|
encodePresent(encoder) {
|
|
127
132
|
const texture = context.getCurrentTexture();
|
|
128
|
-
const
|
|
133
|
+
const descriptor = {
|
|
129
134
|
label: "plasius.wavefront.presentPass",
|
|
130
135
|
colorAttachments: [
|
|
131
136
|
{
|
|
@@ -135,7 +140,10 @@ export function createWavefrontFrameEncoder({
|
|
|
135
140
|
storeOp: "store",
|
|
136
141
|
},
|
|
137
142
|
],
|
|
138
|
-
}
|
|
143
|
+
};
|
|
144
|
+
const passEncoder = encoder.beginRenderPass(
|
|
145
|
+
getFrameTelemetry()?.decorateFinalPass(descriptor) ?? descriptor
|
|
146
|
+
);
|
|
139
147
|
passEncoder.setPipeline(presentPipeline);
|
|
140
148
|
passEncoder.setBindGroup(0, presentBindGroup);
|
|
141
149
|
passEncoder.draw(3);
|
|
@@ -184,6 +184,14 @@ export function createWavefrontTransportGuardrailSummary(frameStats, options = {
|
|
|
184
184
|
0,
|
|
185
185
|
Number(frameStats?.radianceDiagnostics?.legacyClampEquivalentSamples ?? 0)
|
|
186
186
|
)
|
|
187
|
+
const rayCountStatus =
|
|
188
|
+
typeof frameStats?.rayCounts?.status === "string"
|
|
189
|
+
? frameStats.rayCounts.status
|
|
190
|
+
: "not-requested"
|
|
191
|
+
const rayCountReason =
|
|
192
|
+
typeof frameStats?.rayCounts?.reason === "string"
|
|
193
|
+
? frameStats.rayCounts.reason
|
|
194
|
+
: null
|
|
187
195
|
const memory = summarizeWavefrontMemory(frameStats?.memory)
|
|
188
196
|
const checks = Object.freeze([
|
|
189
197
|
createTransportGuardrailCheck(
|
|
@@ -226,6 +234,21 @@ export function createWavefrontTransportGuardrailSummary(frameStats, options = {
|
|
|
226
234
|
? `${legacyClampEquivalentSamples} weighted sample(s) exceeded the legacy 4.0 preview clamp threshold; convergence stayed unbiased, but review firefly diagnostics before rollout.`
|
|
227
235
|
: "No invalid radiance samples or legacy-clamp-equivalent fireflies were recorded."
|
|
228
236
|
),
|
|
237
|
+
createTransportGuardrailCheck(
|
|
238
|
+
"ray-count-telemetry",
|
|
239
|
+
rayCountStatus === "failed"
|
|
240
|
+
? "fail"
|
|
241
|
+
: rayCountStatus === "unavailable"
|
|
242
|
+
? "warn"
|
|
243
|
+
: "pass",
|
|
244
|
+
rayCountStatus === "failed"
|
|
245
|
+
? `Ray-count telemetry failed closed: ${rayCountReason ?? "unknown failure"}.`
|
|
246
|
+
: rayCountStatus === "unavailable"
|
|
247
|
+
? `Ray-count telemetry was requested but unavailable: ${rayCountReason ?? "unknown reason"}.`
|
|
248
|
+
: rayCountStatus === "available"
|
|
249
|
+
? "Primary, secondary, and total path-segment counts were captured."
|
|
250
|
+
: "Ray-count telemetry was not requested for this frame."
|
|
251
|
+
),
|
|
229
252
|
])
|
|
230
253
|
const status = checks.some((check) => check.status === "fail")
|
|
231
254
|
? "fail"
|
|
@@ -249,6 +272,7 @@ export function createWavefrontTransportGuardrailSummary(frameStats, options = {
|
|
|
249
272
|
invalidSamples,
|
|
250
273
|
legacyClampEquivalentSamples,
|
|
251
274
|
}),
|
|
275
|
+
rayCountStatus,
|
|
252
276
|
memory,
|
|
253
277
|
}),
|
|
254
278
|
checks,
|
|
@@ -4,6 +4,10 @@ import {
|
|
|
4
4
|
clamp,
|
|
5
5
|
readPositiveInteger,
|
|
6
6
|
} from "./wavefront-core.js";
|
|
7
|
+
import {
|
|
8
|
+
createWavefrontFrameTimingTelemetry,
|
|
9
|
+
createWavefrontRayCountTelemetry,
|
|
10
|
+
} from "./wavefront-frame-telemetry.js";
|
|
7
11
|
|
|
8
12
|
export function resolveWavefrontRenderedSamplesPerPixel({
|
|
9
13
|
config,
|
|
@@ -107,6 +111,11 @@ export function createWavefrontFrameStats({
|
|
|
107
111
|
maxFramePassesPerSubmission: config.maxFramePassesPerSubmission,
|
|
108
112
|
screenRays: config.width * config.height,
|
|
109
113
|
primaryRays: config.width * config.height * renderedSamplesPerPixel,
|
|
114
|
+
secondaryRays: null,
|
|
115
|
+
totalPathSegments: null,
|
|
116
|
+
rayCounts: createWavefrontRayCountTelemetry(),
|
|
117
|
+
timings: createWavefrontFrameTimingTelemetry(),
|
|
118
|
+
telemetryMemoryBytes: 0,
|
|
110
119
|
sceneObjectCount: config.sceneObjectCount,
|
|
111
120
|
triangleCount: config.triangleCount,
|
|
112
121
|
emissiveTriangleCount: config.emissiveTriangleCount,
|