@plasius/gpu-renderer 0.2.7 → 0.2.8
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 -2
- package/README.md +17 -10
- package/dist/index.cjs +248 -237
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +248 -237
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/wavefront-compute.js +263 -205
package/dist/index.js
CHANGED
|
@@ -165,6 +165,7 @@ var PATH_VERTEX_RECORD_BYTES = 16;
|
|
|
165
165
|
var GPU_SUBMITTED_WORK_TIMEOUT_MS = 5e3;
|
|
166
166
|
var GPU_READBACK_COMPLETION_TIMEOUT_MS = 6e4;
|
|
167
167
|
var GPU_MAX_SUBMITTED_WORK_TIMEOUT_MS = 6e4;
|
|
168
|
+
var GPU_MAX_SUBMITTED_WORK_DEADLINE_MS = 18e4;
|
|
168
169
|
var CONFIG_BUFFER_BYTES = 320;
|
|
169
170
|
var COUNTER_DISPATCH_ARGS_OFFSET = 16;
|
|
170
171
|
var INDIRECT_DISPATCH_ARGS_BYTES = 12;
|
|
@@ -870,141 +871,9 @@ function normalizeWavefrontMesh(input = {}, meshIndex = 0) {
|
|
|
870
871
|
function clampUnit(value) {
|
|
871
872
|
return clamp(Number(value) || 0, 0, 1);
|
|
872
873
|
}
|
|
873
|
-
function
|
|
874
|
-
const channel = clampUnit(value);
|
|
875
|
-
if (channel <= 0.04045) {
|
|
876
|
-
return channel / 12.92;
|
|
877
|
-
}
|
|
878
|
-
return ((channel + 0.055) / 1.055) ** 2.4;
|
|
879
|
-
}
|
|
880
|
-
function sampleTextureRgba(texture, uv = [0, 0], colorSpace = "linear") {
|
|
881
|
-
if (!texture || !Number.isFinite(texture.width) || !Number.isFinite(texture.height) || !texture.data || texture.width <= 0 || texture.height <= 0) {
|
|
882
|
-
return [1, 1, 1, 1];
|
|
883
|
-
}
|
|
884
|
-
const u = (uv[0] % 1 + 1) % 1;
|
|
885
|
-
const v = (uv[1] % 1 + 1) % 1;
|
|
886
|
-
const x = Math.min(texture.width - 1, Math.max(0, Math.round(u * (texture.width - 1))));
|
|
887
|
-
const y = Math.min(texture.height - 1, Math.max(0, Math.round((1 - v) * (texture.height - 1))));
|
|
888
|
-
const offset = (y * texture.width + x) * 4;
|
|
889
|
-
const data = texture.data;
|
|
890
|
-
const scale2 = resolveTextureSampleScale(data);
|
|
891
|
-
const defaultChannel = scale2 === 1 ? 1 : Math.round(1 / scale2);
|
|
892
|
-
const color = [
|
|
893
|
-
(data[offset] ?? defaultChannel) * scale2,
|
|
894
|
-
(data[offset + 1] ?? defaultChannel) * scale2,
|
|
895
|
-
(data[offset + 2] ?? defaultChannel) * scale2,
|
|
896
|
-
(data[offset + 3] ?? defaultChannel) * scale2
|
|
897
|
-
];
|
|
898
|
-
if (colorSpace === "srgb") {
|
|
899
|
-
return [srgbToLinear(color[0]), srgbToLinear(color[1]), srgbToLinear(color[2]), color[3]];
|
|
900
|
-
}
|
|
901
|
-
return color;
|
|
902
|
-
}
|
|
903
|
-
function resolveTextureSampleScale(data) {
|
|
904
|
-
if (data instanceof Uint8Array || data instanceof Uint8ClampedArray) {
|
|
905
|
-
return 1 / 255;
|
|
906
|
-
}
|
|
907
|
-
if (data instanceof Uint16Array) {
|
|
908
|
-
return 1 / 65535;
|
|
909
|
-
}
|
|
910
|
-
if (Array.isArray(data) && data.some((value) => Number(value) > 1)) {
|
|
911
|
-
return 1 / 255;
|
|
912
|
-
}
|
|
913
|
-
return 1;
|
|
914
|
-
}
|
|
915
|
-
function normalizeVectorOrFallback(vector, fallback) {
|
|
916
|
-
return normalize(Array.isArray(vector) ? vector : fallback, fallback);
|
|
917
|
-
}
|
|
918
|
-
function buildTriangleTangentBasis(v0, v1, v2, uv0, uv1, uv2, fallbackNormal) {
|
|
919
|
-
const edge1 = subtract(v1, v0);
|
|
920
|
-
const edge2 = subtract(v2, v0);
|
|
921
|
-
const deltaUv1 = [uv1[0] - uv0[0], uv1[1] - uv0[1]];
|
|
922
|
-
const deltaUv2 = [uv2[0] - uv0[0], uv2[1] - uv0[1]];
|
|
923
|
-
const determinant = deltaUv1[0] * deltaUv2[1] - deltaUv1[1] * deltaUv2[0];
|
|
924
|
-
if (Math.abs(determinant) < 1e-6) {
|
|
925
|
-
const tangentFallback = Math.abs(fallbackNormal[1]) < 0.999 ? [0, 1, 0] : [1, 0, 0];
|
|
926
|
-
const tangent2 = normalize(cross(tangentFallback, fallbackNormal), [1, 0, 0]);
|
|
927
|
-
const bitangent2 = normalize(cross(fallbackNormal, tangent2), [0, 0, 1]);
|
|
928
|
-
return { tangent: tangent2, bitangent: bitangent2 };
|
|
929
|
-
}
|
|
930
|
-
const inverse = 1 / determinant;
|
|
931
|
-
const tangent = normalize(
|
|
932
|
-
[
|
|
933
|
-
inverse * (edge1[0] * deltaUv2[1] - edge2[0] * deltaUv1[1]),
|
|
934
|
-
inverse * (edge1[1] * deltaUv2[1] - edge2[1] * deltaUv1[1]),
|
|
935
|
-
inverse * (edge1[2] * deltaUv2[1] - edge2[2] * deltaUv1[1])
|
|
936
|
-
],
|
|
937
|
-
[1, 0, 0]
|
|
938
|
-
);
|
|
939
|
-
const bitangent = normalize(
|
|
940
|
-
[
|
|
941
|
-
inverse * (-edge1[0] * deltaUv2[0] + edge2[0] * deltaUv1[0]),
|
|
942
|
-
inverse * (-edge1[1] * deltaUv2[0] + edge2[1] * deltaUv1[0]),
|
|
943
|
-
inverse * (-edge1[2] * deltaUv2[0] + edge2[2] * deltaUv1[0])
|
|
944
|
-
],
|
|
945
|
-
[0, 0, 1]
|
|
946
|
-
);
|
|
947
|
-
return { tangent, bitangent };
|
|
948
|
-
}
|
|
949
|
-
function applyNormalMap(normal, tangent, bitangent, normalTexture, uv) {
|
|
950
|
-
if (!normalTexture) {
|
|
951
|
-
return normalizeVectorOrFallback(normal, [0, 1, 0]);
|
|
952
|
-
}
|
|
953
|
-
const sample = sampleTextureRgba(normalTexture, uv, "linear");
|
|
954
|
-
const strength = clampUnit(normalTexture.scale ?? 1);
|
|
955
|
-
const tangentNormal = normalize(
|
|
956
|
-
[
|
|
957
|
-
(sample[0] * 2 - 1) * strength,
|
|
958
|
-
(sample[1] * 2 - 1) * strength,
|
|
959
|
-
1 + (sample[2] * 2 - 1 - 1) * strength
|
|
960
|
-
],
|
|
961
|
-
[0, 0, 1]
|
|
962
|
-
);
|
|
963
|
-
return normalize(
|
|
964
|
-
[
|
|
965
|
-
tangent[0] * tangentNormal[0] + bitangent[0] * tangentNormal[1] + normal[0] * tangentNormal[2],
|
|
966
|
-
tangent[1] * tangentNormal[0] + bitangent[1] * tangentNormal[1] + normal[1] * tangentNormal[2],
|
|
967
|
-
tangent[2] * tangentNormal[0] + bitangent[2] * tangentNormal[1] + normal[2] * tangentNormal[2]
|
|
968
|
-
],
|
|
969
|
-
normal
|
|
970
|
-
);
|
|
971
|
-
}
|
|
972
|
-
function sampleBaseColor(mesh, uv) {
|
|
973
|
-
const sample = mesh.baseColorTexture ? sampleTextureRgba(mesh.baseColorTexture, uv, "srgb") : [1, 1, 1, 1];
|
|
974
|
-
return [
|
|
975
|
-
clampUnit(mesh.color[0] * sample[0]),
|
|
976
|
-
clampUnit(mesh.color[1] * sample[1]),
|
|
977
|
-
clampUnit(mesh.color[2] * sample[2]),
|
|
978
|
-
clampUnit((mesh.color[3] ?? 1) * sample[3])
|
|
979
|
-
];
|
|
980
|
-
}
|
|
981
|
-
function sampleSurfaceMaterial(mesh, uv) {
|
|
982
|
-
const textureSample = mesh.metallicRoughnessTexture ? sampleTextureRgba(mesh.metallicRoughnessTexture, uv, "linear") : [1, 1, 1, 1];
|
|
983
|
-
return {
|
|
984
|
-
roughness: clamp(mesh.roughness * textureSample[1], 0, 1),
|
|
985
|
-
metallic: clamp(mesh.metallic * textureSample[2], 0, 1)
|
|
986
|
-
};
|
|
987
|
-
}
|
|
988
|
-
function averageColors(colors) {
|
|
989
|
-
const count = Math.max(colors.length, 1);
|
|
990
|
-
return colors.reduce(
|
|
991
|
-
(accumulator, color) => [
|
|
992
|
-
accumulator[0] + color[0] / count,
|
|
993
|
-
accumulator[1] + color[1] / count,
|
|
994
|
-
accumulator[2] + color[2] / count,
|
|
995
|
-
accumulator[3] + color[3] / count
|
|
996
|
-
],
|
|
997
|
-
[0, 0, 0, 0]
|
|
998
|
-
);
|
|
999
|
-
}
|
|
1000
|
-
function averageNumbers(values, fallback = 0) {
|
|
1001
|
-
if (!Array.isArray(values) || values.length === 0) {
|
|
1002
|
-
return fallback;
|
|
1003
|
-
}
|
|
1004
|
-
return values.reduce((sum, value) => sum + value, 0) / values.length;
|
|
1005
|
-
}
|
|
1006
|
-
function createMeshTriangleRecords(meshes) {
|
|
874
|
+
function createMeshTriangleRecords(meshes, gpuMaterialSource = null) {
|
|
1007
875
|
const source = Array.isArray(meshes) ? meshes : [];
|
|
876
|
+
const resolvedMaterialSource = gpuMaterialSource ?? createWavefrontGpuMaterialSource(source);
|
|
1008
877
|
let nextTriangleId = 0;
|
|
1009
878
|
return source.flatMap((meshInput, meshIndex) => {
|
|
1010
879
|
const mesh = normalizeWavefrontMesh(meshInput, meshIndex);
|
|
@@ -1023,16 +892,6 @@ function createMeshTriangleRecords(meshes) {
|
|
|
1023
892
|
const uv0 = mesh.uvs ? readVector2(mesh.uvs, a) : [0, 0];
|
|
1024
893
|
const uv1 = mesh.uvs ? readVector2(mesh.uvs, b) : [0, 0];
|
|
1025
894
|
const uv2 = mesh.uvs ? readVector2(mesh.uvs, c) : [0, 0];
|
|
1026
|
-
const tangentBasis = buildTriangleTangentBasis(v0, v1, v2, uv0, uv1, uv2, faceNormal);
|
|
1027
|
-
const shadedN0 = applyNormalMap(n0, tangentBasis.tangent, tangentBasis.bitangent, mesh.normalTexture, uv0);
|
|
1028
|
-
const shadedN1 = applyNormalMap(n1, tangentBasis.tangent, tangentBasis.bitangent, mesh.normalTexture, uv1);
|
|
1029
|
-
const shadedN2 = applyNormalMap(n2, tangentBasis.tangent, tangentBasis.bitangent, mesh.normalTexture, uv2);
|
|
1030
|
-
const sampledColors = [sampleBaseColor(mesh, uv0), sampleBaseColor(mesh, uv1), sampleBaseColor(mesh, uv2)];
|
|
1031
|
-
const sampledMaterials = [
|
|
1032
|
-
sampleSurfaceMaterial(mesh, uv0),
|
|
1033
|
-
sampleSurfaceMaterial(mesh, uv1),
|
|
1034
|
-
sampleSurfaceMaterial(mesh, uv2)
|
|
1035
|
-
];
|
|
1036
895
|
const bounds = triangleBounds(v0, v1, v2);
|
|
1037
896
|
triangles.push(
|
|
1038
897
|
Object.freeze({
|
|
@@ -1046,17 +905,17 @@ function createMeshTriangleRecords(meshes) {
|
|
|
1046
905
|
v0: Object.freeze(v0),
|
|
1047
906
|
v1: Object.freeze(v1),
|
|
1048
907
|
v2: Object.freeze(v2),
|
|
1049
|
-
n0: Object.freeze(
|
|
1050
|
-
n1: Object.freeze(
|
|
1051
|
-
n2: Object.freeze(
|
|
908
|
+
n0: Object.freeze(n0),
|
|
909
|
+
n1: Object.freeze(n1),
|
|
910
|
+
n2: Object.freeze(n2),
|
|
1052
911
|
uv0: Object.freeze(uv0),
|
|
1053
912
|
uv1: Object.freeze(uv1),
|
|
1054
913
|
uv2: Object.freeze(uv2),
|
|
1055
|
-
color:
|
|
914
|
+
color: mesh.color,
|
|
1056
915
|
emission: mesh.emission,
|
|
1057
916
|
material: Object.freeze([
|
|
1058
|
-
|
|
1059
|
-
|
|
917
|
+
mesh.roughness,
|
|
918
|
+
mesh.metallic,
|
|
1060
919
|
mesh.opacity,
|
|
1061
920
|
mesh.ior
|
|
1062
921
|
]),
|
|
@@ -1078,6 +937,27 @@ function createMeshTriangleRecords(meshes) {
|
|
|
1078
937
|
mesh.specularColor[2] ?? 1,
|
|
1079
938
|
1
|
|
1080
939
|
]),
|
|
940
|
+
baseColorAtlas: Object.freeze(
|
|
941
|
+
resolvedMaterialSource.baseColorAtlas.resolveRect(mesh.baseColorTexture)
|
|
942
|
+
),
|
|
943
|
+
metallicRoughnessAtlas: Object.freeze(
|
|
944
|
+
resolvedMaterialSource.metallicRoughnessAtlas.resolveRect(mesh.metallicRoughnessTexture)
|
|
945
|
+
),
|
|
946
|
+
normalAtlas: Object.freeze(
|
|
947
|
+
resolvedMaterialSource.normalAtlas.resolveRect(mesh.normalTexture)
|
|
948
|
+
),
|
|
949
|
+
occlusionAtlas: Object.freeze(
|
|
950
|
+
resolvedMaterialSource.occlusionAtlas.resolveRect(mesh.occlusionTexture)
|
|
951
|
+
),
|
|
952
|
+
emissiveAtlas: Object.freeze(
|
|
953
|
+
resolvedMaterialSource.emissiveAtlas.resolveRect(mesh.emissiveTexture)
|
|
954
|
+
),
|
|
955
|
+
textureSettings: Object.freeze([
|
|
956
|
+
clampUnit(mesh.normalTexture?.scale ?? mesh.normalTexture?.strength ?? 1),
|
|
957
|
+
clampUnit(mesh.occlusionTexture?.strength ?? 1),
|
|
958
|
+
clampUnit(mesh.emissiveTexture?.strength ?? 1),
|
|
959
|
+
0
|
|
960
|
+
]),
|
|
1081
961
|
bounds: Object.freeze({
|
|
1082
962
|
min: Object.freeze(bounds.min),
|
|
1083
963
|
max: Object.freeze(bounds.max)
|
|
@@ -1152,9 +1032,10 @@ function buildBvh(triangles, maxLeafTriangles = 4) {
|
|
|
1152
1032
|
triangles: Object.freeze(orderedTriangles)
|
|
1153
1033
|
});
|
|
1154
1034
|
}
|
|
1155
|
-
function createWavefrontMeshAcceleration(meshes = []) {
|
|
1035
|
+
function createWavefrontMeshAcceleration(meshes = [], gpuMaterialSource = null) {
|
|
1156
1036
|
const source = Array.isArray(meshes) ? meshes : [meshes];
|
|
1157
|
-
const
|
|
1037
|
+
const resolvedMaterialSource = gpuMaterialSource ?? createWavefrontGpuMaterialSource(source);
|
|
1038
|
+
const triangles = createMeshTriangleRecords(source, resolvedMaterialSource);
|
|
1158
1039
|
return buildBvh(triangles);
|
|
1159
1040
|
}
|
|
1160
1041
|
function estimateMeshSourceShape(meshes) {
|
|
@@ -1448,12 +1329,12 @@ function createWavefrontBvhBuildLevels(triangleCountInput) {
|
|
|
1448
1329
|
return Object.freeze(levels);
|
|
1449
1330
|
}
|
|
1450
1331
|
function resolveAccelerationBuildMode(options = {}) {
|
|
1451
|
-
const
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1332
|
+
const requestedMode = options.accelerationBuildMode ?? (options.displayQuality === true ? "cpu-upload" : "cpu-debug");
|
|
1333
|
+
const mode = requestedMode === "cpu-debug" ? "cpu-upload" : requestedMode;
|
|
1334
|
+
if (mode !== "gpu" && mode !== "cpu-upload") {
|
|
1335
|
+
throw new Error(
|
|
1336
|
+
'accelerationBuildMode must be either "gpu", "cpu-upload", or the legacy alias "cpu-debug".'
|
|
1337
|
+
);
|
|
1457
1338
|
}
|
|
1458
1339
|
return mode;
|
|
1459
1340
|
}
|
|
@@ -1939,7 +1820,7 @@ function createWavefrontPathTracingComputeConfig(options = {}) {
|
|
|
1939
1820
|
const meshSourceShape = estimateMeshSourceShape(meshes);
|
|
1940
1821
|
const gpuMaterialSource = meshes.length > 0 ? createWavefrontGpuMaterialSource(meshes) : createWavefrontGpuMaterialSource([]);
|
|
1941
1822
|
const gpuMeshSource = meshes.length > 0 ? createWavefrontGpuMeshSource(meshes, gpuMaterialSource) : createWavefrontGpuMeshSource([]);
|
|
1942
|
-
const meshAcceleration = accelerationBuildMode === "cpu-
|
|
1823
|
+
const meshAcceleration = accelerationBuildMode === "cpu-upload" ? createWavefrontMeshAcceleration(meshes, gpuMaterialSource) : Object.freeze({ nodes: Object.freeze([]), triangles: Object.freeze([]) });
|
|
1943
1824
|
const emissiveTriangleIndices = createWavefrontEmissiveTriangleIndexSource(
|
|
1944
1825
|
meshes,
|
|
1945
1826
|
options.emissiveTriangleCapacity
|
|
@@ -5751,9 +5632,22 @@ function nowMs() {
|
|
|
5751
5632
|
}
|
|
5752
5633
|
return Date.now();
|
|
5753
5634
|
}
|
|
5754
|
-
function
|
|
5635
|
+
function estimateAccelerationBuildWaitFactor(config) {
|
|
5636
|
+
if (config?.gpuAccelerationBuildRequired !== true) {
|
|
5637
|
+
return 1;
|
|
5638
|
+
}
|
|
5639
|
+
const bvhSortStageCount = Array.isArray(config?.bvhSortStages) ? config.bvhSortStages.length : 0;
|
|
5640
|
+
const bvhBuildLevelCount = Array.isArray(config?.bvhBuildLevels) ? config.bvhBuildLevels.length : 0;
|
|
5641
|
+
const accelerationStageCount = 2 + bvhSortStageCount + bvhBuildLevelCount;
|
|
5642
|
+
return Math.max(1, 1 + accelerationStageCount / 96);
|
|
5643
|
+
}
|
|
5644
|
+
function estimateSubmittedGpuWorkTiming(config, tileCount, overrideTimeoutMs = null, options = {}) {
|
|
5755
5645
|
if (Number.isFinite(overrideTimeoutMs)) {
|
|
5756
|
-
|
|
5646
|
+
const overrideMs = Math.max(1, Math.trunc(Number(overrideTimeoutMs)));
|
|
5647
|
+
return Object.freeze({
|
|
5648
|
+
timeoutMs: overrideMs,
|
|
5649
|
+
maxWaitMs: overrideMs
|
|
5650
|
+
});
|
|
5757
5651
|
}
|
|
5758
5652
|
const samplesPerPixel = Math.max(
|
|
5759
5653
|
1,
|
|
@@ -5764,10 +5658,26 @@ function estimateSubmittedGpuWorkTimeoutMs(config, tileCount, overrideTimeoutMs
|
|
|
5764
5658
|
const denoisePasses = config?.denoise ? samplesPerPixel < 4 ? 2 : 1 : 0;
|
|
5765
5659
|
const tiles = Math.max(1, Number(tileCount ?? 1));
|
|
5766
5660
|
const estimatedPasses = tiles * (samplesPerPixel * (maxDepth + 1 + deferredResolvePasses) + denoisePasses + 1);
|
|
5767
|
-
|
|
5661
|
+
const triangleCount = Math.max(0, Number(config?.triangleCount ?? 0));
|
|
5662
|
+
const geometryFactor = Math.max(1, triangleCount / 131072);
|
|
5663
|
+
const includeAccelerationBuild = options.includeAccelerationBuild === true;
|
|
5664
|
+
const accelerationFactor = includeAccelerationBuild ? estimateAccelerationBuildWaitFactor(config) : 1;
|
|
5665
|
+
const estimatedWindowMs = Math.round(
|
|
5666
|
+
(GPU_SUBMITTED_WORK_TIMEOUT_MS + estimatedPasses * 5) * geometryFactor * accelerationFactor
|
|
5667
|
+
);
|
|
5668
|
+
const timeoutMs = Math.min(
|
|
5768
5669
|
GPU_MAX_SUBMITTED_WORK_TIMEOUT_MS,
|
|
5769
|
-
GPU_SUBMITTED_WORK_TIMEOUT_MS
|
|
5670
|
+
Math.max(GPU_SUBMITTED_WORK_TIMEOUT_MS, estimatedWindowMs)
|
|
5770
5671
|
);
|
|
5672
|
+
const maxWaitMultiplier = includeAccelerationBuild ? 3 : 2;
|
|
5673
|
+
const maxWaitMs = Math.min(
|
|
5674
|
+
GPU_MAX_SUBMITTED_WORK_DEADLINE_MS,
|
|
5675
|
+
Math.max(timeoutMs, estimatedWindowMs * maxWaitMultiplier)
|
|
5676
|
+
);
|
|
5677
|
+
return Object.freeze({
|
|
5678
|
+
timeoutMs,
|
|
5679
|
+
maxWaitMs
|
|
5680
|
+
});
|
|
5771
5681
|
}
|
|
5772
5682
|
async function createWavefrontPathTracingComputeRenderer(options = {}) {
|
|
5773
5683
|
assertAnalyticDisplayQualityPolicy(options);
|
|
@@ -6743,6 +6653,10 @@ async function createWavefrontPathTracingComputeRenderer(options = {}) {
|
|
|
6743
6653
|
1,
|
|
6744
6654
|
Number.isFinite(options2.timeoutMs) ? Number(options2.timeoutMs) : GPU_SUBMITTED_WORK_TIMEOUT_MS
|
|
6745
6655
|
);
|
|
6656
|
+
const maxWaitMs = Math.max(
|
|
6657
|
+
timeoutMs,
|
|
6658
|
+
Number.isFinite(options2.maxWaitMs) ? Number(options2.maxWaitMs) : timeoutMs
|
|
6659
|
+
);
|
|
6746
6660
|
const allowTimeout = options2.allowTimeout !== false;
|
|
6747
6661
|
const completionPromise = device.queue.onSubmittedWorkDone().then(
|
|
6748
6662
|
() => ({ status: "done" }),
|
|
@@ -6755,43 +6669,57 @@ async function createWavefrontPathTracingComputeRenderer(options = {}) {
|
|
|
6755
6669
|
`WebGPU device lost while waiting for submitted work (${info?.reason ?? "unknown"}).`
|
|
6756
6670
|
);
|
|
6757
6671
|
}) : null;
|
|
6758
|
-
|
|
6759
|
-
|
|
6760
|
-
|
|
6761
|
-
|
|
6762
|
-
if (
|
|
6763
|
-
|
|
6672
|
+
const startedAtMs = nowMs();
|
|
6673
|
+
while (true) {
|
|
6674
|
+
const elapsedMs = Math.max(0, nowMs() - startedAtMs);
|
|
6675
|
+
const remainingMs = Math.max(0, maxWaitMs - elapsedMs);
|
|
6676
|
+
if (remainingMs <= 0) {
|
|
6677
|
+
if (!allowTimeout) {
|
|
6678
|
+
throw new Error(`Timed out after ${Math.round(maxWaitMs)} ms waiting for submitted GPU work.`);
|
|
6679
|
+
}
|
|
6680
|
+
console.warn(
|
|
6681
|
+
`[plasius.wavefront] Submitted GPU work did not report completion within ${Math.round(maxWaitMs)} ms; continuing.`
|
|
6682
|
+
);
|
|
6683
|
+
return false;
|
|
6764
6684
|
}
|
|
6765
|
-
|
|
6766
|
-
|
|
6767
|
-
|
|
6768
|
-
|
|
6769
|
-
|
|
6770
|
-
|
|
6771
|
-
|
|
6772
|
-
|
|
6773
|
-
|
|
6774
|
-
|
|
6775
|
-
|
|
6776
|
-
)
|
|
6777
|
-
|
|
6778
|
-
|
|
6779
|
-
|
|
6780
|
-
|
|
6685
|
+
const waitWindowMs = Math.max(1, Math.min(timeoutMs, remainingMs));
|
|
6686
|
+
let timeoutHandle = null;
|
|
6687
|
+
let resolveTimeoutPromise = null;
|
|
6688
|
+
let timeoutSettled = false;
|
|
6689
|
+
const settleTimeoutPromise = (value) => {
|
|
6690
|
+
if (timeoutSettled) {
|
|
6691
|
+
return;
|
|
6692
|
+
}
|
|
6693
|
+
timeoutSettled = true;
|
|
6694
|
+
resolveTimeoutPromise?.(value);
|
|
6695
|
+
};
|
|
6696
|
+
const timeoutPromise = new Promise((resolve) => {
|
|
6697
|
+
resolveTimeoutPromise = resolve;
|
|
6698
|
+
timeoutHandle = setTimeout(
|
|
6699
|
+
() => settleTimeoutPromise({ status: "timeout" }),
|
|
6700
|
+
waitWindowMs
|
|
6701
|
+
);
|
|
6702
|
+
});
|
|
6703
|
+
let result;
|
|
6704
|
+
try {
|
|
6705
|
+
result = await Promise.race(
|
|
6706
|
+
[completionPromise, timeoutPromise, lossPromise].filter(Boolean)
|
|
6707
|
+
);
|
|
6708
|
+
} finally {
|
|
6709
|
+
if (timeoutHandle !== null) {
|
|
6710
|
+
clearTimeout(timeoutHandle);
|
|
6711
|
+
settleTimeoutPromise({ status: "cancelled" });
|
|
6712
|
+
}
|
|
6781
6713
|
}
|
|
6782
|
-
|
|
6783
|
-
|
|
6784
|
-
|
|
6785
|
-
|
|
6714
|
+
if (result?.status === "done") {
|
|
6715
|
+
return true;
|
|
6716
|
+
}
|
|
6717
|
+
if (result?.status !== "timeout") {
|
|
6718
|
+
return true;
|
|
6786
6719
|
}
|
|
6787
|
-
console.warn(
|
|
6788
|
-
`[plasius.wavefront] Submitted GPU work did not report completion within ${timeoutMs} ms; continuing.`
|
|
6789
|
-
);
|
|
6790
|
-
return false;
|
|
6791
6720
|
}
|
|
6792
|
-
return true;
|
|
6793
6721
|
}
|
|
6794
|
-
function dispatchFrameAwaitingGpu(frameIndex, parallelism, renderedSamplesPerPixel = config.samplesPerPixel) {
|
|
6722
|
+
function dispatchFrameAwaitingGpu(frameIndex, parallelism, renderedSamplesPerPixel = config.samplesPerPixel, optionsForFrame = {}) {
|
|
6795
6723
|
const samplePassesPerSample = config.maxDepth + 1 + (config.deferredPathResolve ? 1 : 0);
|
|
6796
6724
|
const denoisePassCount = config.denoise ? renderedSamplesPerPixel < 4 ? 2 : 1 : 0;
|
|
6797
6725
|
const tailPassCount = denoisePassCount + 1;
|
|
@@ -6801,17 +6729,42 @@ async function createWavefrontPathTracingComputeRenderer(options = {}) {
|
|
|
6801
6729
|
Math.max(config.maxFramePassesPerSubmission - tailPassCount, 1) / Math.max(samplePassesPerSample, 1)
|
|
6802
6730
|
)
|
|
6803
6731
|
);
|
|
6804
|
-
|
|
6805
|
-
|
|
6806
|
-
|
|
6807
|
-
|
|
6732
|
+
const sampleRangeStart = clamp(
|
|
6733
|
+
readNonNegativeInteger("sampleRangeStart", optionsForFrame.sampleRangeStart, 0),
|
|
6734
|
+
0,
|
|
6735
|
+
renderedSamplesPerPixel
|
|
6736
|
+
);
|
|
6737
|
+
const sampleRangeEnd = clamp(
|
|
6738
|
+
readPositiveInteger("sampleRangeEnd", optionsForFrame.sampleRangeEnd, renderedSamplesPerPixel),
|
|
6739
|
+
sampleRangeStart,
|
|
6740
|
+
renderedSamplesPerPixel
|
|
6741
|
+
);
|
|
6742
|
+
const includeDenoise = optionsForFrame.includeDenoise === true;
|
|
6743
|
+
const includePresent = optionsForFrame.includePresent === true;
|
|
6744
|
+
const tileStartIndex = clamp(
|
|
6745
|
+
readNonNegativeInteger("tileStartIndex", optionsForFrame.tileStartIndex, 0),
|
|
6746
|
+
0,
|
|
6747
|
+
tiles.length
|
|
6748
|
+
);
|
|
6749
|
+
const tileEndIndex = clamp(
|
|
6750
|
+
readPositiveInteger("tileEndIndex", optionsForFrame.tileEndIndex, tiles.length),
|
|
6751
|
+
tileStartIndex,
|
|
6752
|
+
tiles.length
|
|
6753
|
+
);
|
|
6754
|
+
let submissionCount = Math.max(
|
|
6755
|
+
0,
|
|
6756
|
+
readNonNegativeInteger("startingSubmissionCount", optionsForFrame.startingSubmissionCount, 0)
|
|
6757
|
+
);
|
|
6758
|
+
let slot = Math.max(0, readNonNegativeInteger("startingSlot", optionsForFrame.startingSlot, 0));
|
|
6759
|
+
for (const tile of tiles.slice(tileStartIndex, tileEndIndex)) {
|
|
6760
|
+
for (let sampleStart = sampleRangeStart; sampleStart < sampleRangeEnd; sampleStart += sampleBatchSize) {
|
|
6761
|
+
const sampleEnd = Math.min(sampleRangeEnd, sampleStart + sampleBatchSize);
|
|
6808
6762
|
const batch = createGpuSubmissionBatcher({
|
|
6809
6763
|
device,
|
|
6810
6764
|
frameIndex,
|
|
6811
6765
|
maxFramePassesPerSubmission: config.maxFramePassesPerSubmission,
|
|
6812
6766
|
startingSubmissionCount: submissionCount
|
|
6813
6767
|
});
|
|
6814
|
-
let slot = 0;
|
|
6815
6768
|
for (let sampleIndex = sampleStart; sampleIndex < sampleEnd; sampleIndex += 1) {
|
|
6816
6769
|
const configOffset = writeFrameConfigSlot(slot, tile, frameIndex, {
|
|
6817
6770
|
sampleIndex,
|
|
@@ -6828,41 +6781,50 @@ async function createWavefrontPathTracingComputeRenderer(options = {}) {
|
|
|
6828
6781
|
encodeTileOutput(batch.reserve(1), tile, configOffset, parallelism);
|
|
6829
6782
|
}
|
|
6830
6783
|
}
|
|
6831
|
-
if (!config.deferredPathResolve &&
|
|
6784
|
+
if (!config.deferredPathResolve && sampleRangeEnd >= renderedSamplesPerPixel) {
|
|
6832
6785
|
const outputConfigOffset = writeFrameConfigSlot(slot, tile, frameIndex, {
|
|
6833
6786
|
sampleIndex: 0,
|
|
6834
6787
|
sampleWeight: 1 / renderedSamplesPerPixel
|
|
6835
6788
|
});
|
|
6789
|
+
slot += 1;
|
|
6836
6790
|
encodeTileOutput(batch.reserve(1), tile, outputConfigOffset, parallelism);
|
|
6837
6791
|
}
|
|
6838
6792
|
batch.flush();
|
|
6839
6793
|
submissionCount += batch.getSubmissionCount();
|
|
6840
6794
|
}
|
|
6841
6795
|
}
|
|
6842
|
-
|
|
6843
|
-
|
|
6844
|
-
|
|
6845
|
-
maxFramePassesPerSubmission: config.maxFramePassesPerSubmission,
|
|
6846
|
-
startingSubmissionCount: submissionCount
|
|
6847
|
-
});
|
|
6848
|
-
if (config.denoise) {
|
|
6849
|
-
const denoiseConfigOffset = writeFrameConfigSlot(
|
|
6850
|
-
0,
|
|
6851
|
-
{ x: 0, y: 0, width: config.width, height: config.height },
|
|
6796
|
+
if (includeDenoise || includePresent) {
|
|
6797
|
+
const tail = createGpuSubmissionBatcher({
|
|
6798
|
+
device,
|
|
6852
6799
|
frameIndex,
|
|
6853
|
-
|
|
6854
|
-
|
|
6855
|
-
|
|
6856
|
-
|
|
6857
|
-
denoiseConfigOffset
|
|
6858
|
-
|
|
6859
|
-
|
|
6860
|
-
|
|
6800
|
+
maxFramePassesPerSubmission: config.maxFramePassesPerSubmission,
|
|
6801
|
+
startingSubmissionCount: submissionCount
|
|
6802
|
+
});
|
|
6803
|
+
if (includeDenoise && config.denoise) {
|
|
6804
|
+
const denoiseConfigOffset = writeFrameConfigSlot(
|
|
6805
|
+
slot,
|
|
6806
|
+
{ x: 0, y: 0, width: config.width, height: config.height },
|
|
6807
|
+
frameIndex,
|
|
6808
|
+
{ sampleIndex: 0, sampleWeight: 1 / renderedSamplesPerPixel }
|
|
6809
|
+
);
|
|
6810
|
+
slot += 1;
|
|
6811
|
+
encodeDenoise(
|
|
6812
|
+
tail.reserve(denoisePassCount),
|
|
6813
|
+
denoiseConfigOffset,
|
|
6814
|
+
parallelism,
|
|
6815
|
+
renderedSamplesPerPixel
|
|
6816
|
+
);
|
|
6817
|
+
}
|
|
6818
|
+
if (includePresent) {
|
|
6819
|
+
encodePresent(tail.reserve(1));
|
|
6820
|
+
}
|
|
6821
|
+
tail.flush();
|
|
6822
|
+
submissionCount += tail.getSubmissionCount();
|
|
6861
6823
|
}
|
|
6862
|
-
|
|
6863
|
-
|
|
6864
|
-
|
|
6865
|
-
|
|
6824
|
+
return Object.freeze({
|
|
6825
|
+
submissionCount,
|
|
6826
|
+
slot
|
|
6827
|
+
});
|
|
6866
6828
|
}
|
|
6867
6829
|
async function readOutputProbe(optionsForProbe = {}) {
|
|
6868
6830
|
const mapMode = constants.map;
|
|
@@ -6908,24 +6870,59 @@ async function createWavefrontPathTracingComputeRenderer(options = {}) {
|
|
|
6908
6870
|
const awaitGPUCompletion = renderOptions.awaitGPUCompletion !== false;
|
|
6909
6871
|
const samplingPlan = resolveRenderedSamplesPerPixel(renderOptions, awaitGPUCompletion);
|
|
6910
6872
|
const useThrottledHighSamplePath = awaitGPUCompletion && samplingPlan.renderedSamplesPerPixel >= 8;
|
|
6911
|
-
const submittedWorkTimeoutMs = estimateSubmittedGpuWorkTimeoutMs(
|
|
6912
|
-
{ ...config, renderedSamplesPerPixel: samplingPlan.renderedSamplesPerPixel },
|
|
6913
|
-
tiles.length,
|
|
6914
|
-
renderOptions.submittedWorkTimeoutMs
|
|
6915
|
-
);
|
|
6916
6873
|
const frameStartTimeMs = nowMs();
|
|
6917
|
-
const submissionWaitOptions = awaitGPUCompletion ? { timeoutMs: submittedWorkTimeoutMs, allowTimeout: false } : { timeoutMs: submittedWorkTimeoutMs };
|
|
6918
6874
|
let frameStats;
|
|
6919
6875
|
if (useThrottledHighSamplePath) {
|
|
6920
6876
|
frame += 1;
|
|
6921
6877
|
const frameIndex = frame + config.frameIndex;
|
|
6922
6878
|
const parallelismCounters = createGpuParallelismCounters();
|
|
6923
6879
|
const accelerationBuildSubmitted = dispatchGpuAccelerationBuild(frameIndex, parallelismCounters);
|
|
6924
|
-
|
|
6925
|
-
|
|
6926
|
-
|
|
6927
|
-
|
|
6928
|
-
|
|
6880
|
+
let frameSubmissionCount = 0;
|
|
6881
|
+
let frameConfigSlot = 0;
|
|
6882
|
+
if (accelerationBuildSubmitted) {
|
|
6883
|
+
const accelerationWaitOptions = {
|
|
6884
|
+
...estimateSubmittedGpuWorkTiming(
|
|
6885
|
+
{ ...config, renderedSamplesPerPixel: 1 },
|
|
6886
|
+
1,
|
|
6887
|
+
renderOptions.submittedWorkTimeoutMs,
|
|
6888
|
+
{ includeAccelerationBuild: true }
|
|
6889
|
+
),
|
|
6890
|
+
allowTimeout: false
|
|
6891
|
+
};
|
|
6892
|
+
await waitForSubmittedGpuWork(accelerationWaitOptions);
|
|
6893
|
+
}
|
|
6894
|
+
for (let tileIndex = 0; tileIndex < tiles.length; tileIndex += 1) {
|
|
6895
|
+
const tileRangeDispatch = dispatchFrameAwaitingGpu(
|
|
6896
|
+
frameIndex,
|
|
6897
|
+
parallelismCounters,
|
|
6898
|
+
samplingPlan.renderedSamplesPerPixel,
|
|
6899
|
+
{
|
|
6900
|
+
sampleRangeStart: 0,
|
|
6901
|
+
sampleRangeEnd: samplingPlan.renderedSamplesPerPixel,
|
|
6902
|
+
tileStartIndex: tileIndex,
|
|
6903
|
+
tileEndIndex: tileIndex + 1,
|
|
6904
|
+
startingSubmissionCount: frameSubmissionCount,
|
|
6905
|
+
startingSlot: frameConfigSlot,
|
|
6906
|
+
includeDenoise: tileIndex + 1 >= tiles.length,
|
|
6907
|
+
includePresent: tileIndex + 1 >= tiles.length
|
|
6908
|
+
}
|
|
6909
|
+
);
|
|
6910
|
+
frameSubmissionCount = tileRangeDispatch.submissionCount;
|
|
6911
|
+
frameConfigSlot = tileRangeDispatch.slot;
|
|
6912
|
+
const tileWaitOptions = {
|
|
6913
|
+
...estimateSubmittedGpuWorkTiming(
|
|
6914
|
+
{ ...config, renderedSamplesPerPixel: samplingPlan.renderedSamplesPerPixel },
|
|
6915
|
+
1,
|
|
6916
|
+
renderOptions.submittedWorkTimeoutMs,
|
|
6917
|
+
{
|
|
6918
|
+
includeDenoise: tileIndex + 1 >= tiles.length && config.denoise,
|
|
6919
|
+
includePresent: tileIndex + 1 >= tiles.length
|
|
6920
|
+
}
|
|
6921
|
+
),
|
|
6922
|
+
allowTimeout: false
|
|
6923
|
+
};
|
|
6924
|
+
await waitForSubmittedGpuWork(tileWaitOptions);
|
|
6925
|
+
}
|
|
6929
6926
|
frameStats = createFrameStats({
|
|
6930
6927
|
frameIndex,
|
|
6931
6928
|
accelerationBuildSubmitted,
|
|
@@ -6937,10 +6934,24 @@ async function createWavefrontPathTracingComputeRenderer(options = {}) {
|
|
|
6937
6934
|
budgetConstrained: samplingPlan.budgetConstrained
|
|
6938
6935
|
});
|
|
6939
6936
|
} else {
|
|
6937
|
+
const submittedWorkTiming = estimateSubmittedGpuWorkTiming(
|
|
6938
|
+
{ ...config, renderedSamplesPerPixel: samplingPlan.renderedSamplesPerPixel },
|
|
6939
|
+
tiles.length,
|
|
6940
|
+
renderOptions.submittedWorkTimeoutMs,
|
|
6941
|
+
{ includeAccelerationBuild: config.gpuAccelerationBuildRequired && !accelerationBuilt }
|
|
6942
|
+
);
|
|
6943
|
+
const submissionWaitOptions = awaitGPUCompletion ? {
|
|
6944
|
+
timeoutMs: submittedWorkTiming.timeoutMs,
|
|
6945
|
+
maxWaitMs: submittedWorkTiming.maxWaitMs,
|
|
6946
|
+
allowTimeout: false
|
|
6947
|
+
} : {
|
|
6948
|
+
timeoutMs: submittedWorkTiming.timeoutMs,
|
|
6949
|
+
maxWaitMs: submittedWorkTiming.maxWaitMs
|
|
6950
|
+
};
|
|
6940
6951
|
frameStats = renderOnce(renderOptions, samplingPlan);
|
|
6941
|
-
|
|
6942
|
-
|
|
6943
|
-
|
|
6952
|
+
if (awaitGPUCompletion) {
|
|
6953
|
+
await waitForSubmittedGpuWork(submissionWaitOptions);
|
|
6954
|
+
}
|
|
6944
6955
|
}
|
|
6945
6956
|
const frameTimeMs = Math.max(0, nowMs() - frameStartTimeMs);
|
|
6946
6957
|
if (awaitGPUCompletion) {
|