@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.cjs
CHANGED
|
@@ -239,6 +239,7 @@ var PATH_VERTEX_RECORD_BYTES = 16;
|
|
|
239
239
|
var GPU_SUBMITTED_WORK_TIMEOUT_MS = 5e3;
|
|
240
240
|
var GPU_READBACK_COMPLETION_TIMEOUT_MS = 6e4;
|
|
241
241
|
var GPU_MAX_SUBMITTED_WORK_TIMEOUT_MS = 6e4;
|
|
242
|
+
var GPU_MAX_SUBMITTED_WORK_DEADLINE_MS = 18e4;
|
|
242
243
|
var CONFIG_BUFFER_BYTES = 320;
|
|
243
244
|
var COUNTER_DISPATCH_ARGS_OFFSET = 16;
|
|
244
245
|
var INDIRECT_DISPATCH_ARGS_BYTES = 12;
|
|
@@ -944,141 +945,9 @@ function normalizeWavefrontMesh(input = {}, meshIndex = 0) {
|
|
|
944
945
|
function clampUnit(value) {
|
|
945
946
|
return clamp(Number(value) || 0, 0, 1);
|
|
946
947
|
}
|
|
947
|
-
function
|
|
948
|
-
const channel = clampUnit(value);
|
|
949
|
-
if (channel <= 0.04045) {
|
|
950
|
-
return channel / 12.92;
|
|
951
|
-
}
|
|
952
|
-
return ((channel + 0.055) / 1.055) ** 2.4;
|
|
953
|
-
}
|
|
954
|
-
function sampleTextureRgba(texture, uv = [0, 0], colorSpace = "linear") {
|
|
955
|
-
if (!texture || !Number.isFinite(texture.width) || !Number.isFinite(texture.height) || !texture.data || texture.width <= 0 || texture.height <= 0) {
|
|
956
|
-
return [1, 1, 1, 1];
|
|
957
|
-
}
|
|
958
|
-
const u = (uv[0] % 1 + 1) % 1;
|
|
959
|
-
const v = (uv[1] % 1 + 1) % 1;
|
|
960
|
-
const x = Math.min(texture.width - 1, Math.max(0, Math.round(u * (texture.width - 1))));
|
|
961
|
-
const y = Math.min(texture.height - 1, Math.max(0, Math.round((1 - v) * (texture.height - 1))));
|
|
962
|
-
const offset = (y * texture.width + x) * 4;
|
|
963
|
-
const data = texture.data;
|
|
964
|
-
const scale2 = resolveTextureSampleScale(data);
|
|
965
|
-
const defaultChannel = scale2 === 1 ? 1 : Math.round(1 / scale2);
|
|
966
|
-
const color = [
|
|
967
|
-
(data[offset] ?? defaultChannel) * scale2,
|
|
968
|
-
(data[offset + 1] ?? defaultChannel) * scale2,
|
|
969
|
-
(data[offset + 2] ?? defaultChannel) * scale2,
|
|
970
|
-
(data[offset + 3] ?? defaultChannel) * scale2
|
|
971
|
-
];
|
|
972
|
-
if (colorSpace === "srgb") {
|
|
973
|
-
return [srgbToLinear(color[0]), srgbToLinear(color[1]), srgbToLinear(color[2]), color[3]];
|
|
974
|
-
}
|
|
975
|
-
return color;
|
|
976
|
-
}
|
|
977
|
-
function resolveTextureSampleScale(data) {
|
|
978
|
-
if (data instanceof Uint8Array || data instanceof Uint8ClampedArray) {
|
|
979
|
-
return 1 / 255;
|
|
980
|
-
}
|
|
981
|
-
if (data instanceof Uint16Array) {
|
|
982
|
-
return 1 / 65535;
|
|
983
|
-
}
|
|
984
|
-
if (Array.isArray(data) && data.some((value) => Number(value) > 1)) {
|
|
985
|
-
return 1 / 255;
|
|
986
|
-
}
|
|
987
|
-
return 1;
|
|
988
|
-
}
|
|
989
|
-
function normalizeVectorOrFallback(vector, fallback) {
|
|
990
|
-
return normalize(Array.isArray(vector) ? vector : fallback, fallback);
|
|
991
|
-
}
|
|
992
|
-
function buildTriangleTangentBasis(v0, v1, v2, uv0, uv1, uv2, fallbackNormal) {
|
|
993
|
-
const edge1 = subtract(v1, v0);
|
|
994
|
-
const edge2 = subtract(v2, v0);
|
|
995
|
-
const deltaUv1 = [uv1[0] - uv0[0], uv1[1] - uv0[1]];
|
|
996
|
-
const deltaUv2 = [uv2[0] - uv0[0], uv2[1] - uv0[1]];
|
|
997
|
-
const determinant = deltaUv1[0] * deltaUv2[1] - deltaUv1[1] * deltaUv2[0];
|
|
998
|
-
if (Math.abs(determinant) < 1e-6) {
|
|
999
|
-
const tangentFallback = Math.abs(fallbackNormal[1]) < 0.999 ? [0, 1, 0] : [1, 0, 0];
|
|
1000
|
-
const tangent2 = normalize(cross(tangentFallback, fallbackNormal), [1, 0, 0]);
|
|
1001
|
-
const bitangent2 = normalize(cross(fallbackNormal, tangent2), [0, 0, 1]);
|
|
1002
|
-
return { tangent: tangent2, bitangent: bitangent2 };
|
|
1003
|
-
}
|
|
1004
|
-
const inverse = 1 / determinant;
|
|
1005
|
-
const tangent = normalize(
|
|
1006
|
-
[
|
|
1007
|
-
inverse * (edge1[0] * deltaUv2[1] - edge2[0] * deltaUv1[1]),
|
|
1008
|
-
inverse * (edge1[1] * deltaUv2[1] - edge2[1] * deltaUv1[1]),
|
|
1009
|
-
inverse * (edge1[2] * deltaUv2[1] - edge2[2] * deltaUv1[1])
|
|
1010
|
-
],
|
|
1011
|
-
[1, 0, 0]
|
|
1012
|
-
);
|
|
1013
|
-
const bitangent = normalize(
|
|
1014
|
-
[
|
|
1015
|
-
inverse * (-edge1[0] * deltaUv2[0] + edge2[0] * deltaUv1[0]),
|
|
1016
|
-
inverse * (-edge1[1] * deltaUv2[0] + edge2[1] * deltaUv1[0]),
|
|
1017
|
-
inverse * (-edge1[2] * deltaUv2[0] + edge2[2] * deltaUv1[0])
|
|
1018
|
-
],
|
|
1019
|
-
[0, 0, 1]
|
|
1020
|
-
);
|
|
1021
|
-
return { tangent, bitangent };
|
|
1022
|
-
}
|
|
1023
|
-
function applyNormalMap(normal, tangent, bitangent, normalTexture, uv) {
|
|
1024
|
-
if (!normalTexture) {
|
|
1025
|
-
return normalizeVectorOrFallback(normal, [0, 1, 0]);
|
|
1026
|
-
}
|
|
1027
|
-
const sample = sampleTextureRgba(normalTexture, uv, "linear");
|
|
1028
|
-
const strength = clampUnit(normalTexture.scale ?? 1);
|
|
1029
|
-
const tangentNormal = normalize(
|
|
1030
|
-
[
|
|
1031
|
-
(sample[0] * 2 - 1) * strength,
|
|
1032
|
-
(sample[1] * 2 - 1) * strength,
|
|
1033
|
-
1 + (sample[2] * 2 - 1 - 1) * strength
|
|
1034
|
-
],
|
|
1035
|
-
[0, 0, 1]
|
|
1036
|
-
);
|
|
1037
|
-
return normalize(
|
|
1038
|
-
[
|
|
1039
|
-
tangent[0] * tangentNormal[0] + bitangent[0] * tangentNormal[1] + normal[0] * tangentNormal[2],
|
|
1040
|
-
tangent[1] * tangentNormal[0] + bitangent[1] * tangentNormal[1] + normal[1] * tangentNormal[2],
|
|
1041
|
-
tangent[2] * tangentNormal[0] + bitangent[2] * tangentNormal[1] + normal[2] * tangentNormal[2]
|
|
1042
|
-
],
|
|
1043
|
-
normal
|
|
1044
|
-
);
|
|
1045
|
-
}
|
|
1046
|
-
function sampleBaseColor(mesh, uv) {
|
|
1047
|
-
const sample = mesh.baseColorTexture ? sampleTextureRgba(mesh.baseColorTexture, uv, "srgb") : [1, 1, 1, 1];
|
|
1048
|
-
return [
|
|
1049
|
-
clampUnit(mesh.color[0] * sample[0]),
|
|
1050
|
-
clampUnit(mesh.color[1] * sample[1]),
|
|
1051
|
-
clampUnit(mesh.color[2] * sample[2]),
|
|
1052
|
-
clampUnit((mesh.color[3] ?? 1) * sample[3])
|
|
1053
|
-
];
|
|
1054
|
-
}
|
|
1055
|
-
function sampleSurfaceMaterial(mesh, uv) {
|
|
1056
|
-
const textureSample = mesh.metallicRoughnessTexture ? sampleTextureRgba(mesh.metallicRoughnessTexture, uv, "linear") : [1, 1, 1, 1];
|
|
1057
|
-
return {
|
|
1058
|
-
roughness: clamp(mesh.roughness * textureSample[1], 0, 1),
|
|
1059
|
-
metallic: clamp(mesh.metallic * textureSample[2], 0, 1)
|
|
1060
|
-
};
|
|
1061
|
-
}
|
|
1062
|
-
function averageColors(colors) {
|
|
1063
|
-
const count = Math.max(colors.length, 1);
|
|
1064
|
-
return colors.reduce(
|
|
1065
|
-
(accumulator, color) => [
|
|
1066
|
-
accumulator[0] + color[0] / count,
|
|
1067
|
-
accumulator[1] + color[1] / count,
|
|
1068
|
-
accumulator[2] + color[2] / count,
|
|
1069
|
-
accumulator[3] + color[3] / count
|
|
1070
|
-
],
|
|
1071
|
-
[0, 0, 0, 0]
|
|
1072
|
-
);
|
|
1073
|
-
}
|
|
1074
|
-
function averageNumbers(values, fallback = 0) {
|
|
1075
|
-
if (!Array.isArray(values) || values.length === 0) {
|
|
1076
|
-
return fallback;
|
|
1077
|
-
}
|
|
1078
|
-
return values.reduce((sum, value) => sum + value, 0) / values.length;
|
|
1079
|
-
}
|
|
1080
|
-
function createMeshTriangleRecords(meshes) {
|
|
948
|
+
function createMeshTriangleRecords(meshes, gpuMaterialSource = null) {
|
|
1081
949
|
const source = Array.isArray(meshes) ? meshes : [];
|
|
950
|
+
const resolvedMaterialSource = gpuMaterialSource ?? createWavefrontGpuMaterialSource(source);
|
|
1082
951
|
let nextTriangleId = 0;
|
|
1083
952
|
return source.flatMap((meshInput, meshIndex) => {
|
|
1084
953
|
const mesh = normalizeWavefrontMesh(meshInput, meshIndex);
|
|
@@ -1097,16 +966,6 @@ function createMeshTriangleRecords(meshes) {
|
|
|
1097
966
|
const uv0 = mesh.uvs ? readVector2(mesh.uvs, a) : [0, 0];
|
|
1098
967
|
const uv1 = mesh.uvs ? readVector2(mesh.uvs, b) : [0, 0];
|
|
1099
968
|
const uv2 = mesh.uvs ? readVector2(mesh.uvs, c) : [0, 0];
|
|
1100
|
-
const tangentBasis = buildTriangleTangentBasis(v0, v1, v2, uv0, uv1, uv2, faceNormal);
|
|
1101
|
-
const shadedN0 = applyNormalMap(n0, tangentBasis.tangent, tangentBasis.bitangent, mesh.normalTexture, uv0);
|
|
1102
|
-
const shadedN1 = applyNormalMap(n1, tangentBasis.tangent, tangentBasis.bitangent, mesh.normalTexture, uv1);
|
|
1103
|
-
const shadedN2 = applyNormalMap(n2, tangentBasis.tangent, tangentBasis.bitangent, mesh.normalTexture, uv2);
|
|
1104
|
-
const sampledColors = [sampleBaseColor(mesh, uv0), sampleBaseColor(mesh, uv1), sampleBaseColor(mesh, uv2)];
|
|
1105
|
-
const sampledMaterials = [
|
|
1106
|
-
sampleSurfaceMaterial(mesh, uv0),
|
|
1107
|
-
sampleSurfaceMaterial(mesh, uv1),
|
|
1108
|
-
sampleSurfaceMaterial(mesh, uv2)
|
|
1109
|
-
];
|
|
1110
969
|
const bounds = triangleBounds(v0, v1, v2);
|
|
1111
970
|
triangles.push(
|
|
1112
971
|
Object.freeze({
|
|
@@ -1120,17 +979,17 @@ function createMeshTriangleRecords(meshes) {
|
|
|
1120
979
|
v0: Object.freeze(v0),
|
|
1121
980
|
v1: Object.freeze(v1),
|
|
1122
981
|
v2: Object.freeze(v2),
|
|
1123
|
-
n0: Object.freeze(
|
|
1124
|
-
n1: Object.freeze(
|
|
1125
|
-
n2: Object.freeze(
|
|
982
|
+
n0: Object.freeze(n0),
|
|
983
|
+
n1: Object.freeze(n1),
|
|
984
|
+
n2: Object.freeze(n2),
|
|
1126
985
|
uv0: Object.freeze(uv0),
|
|
1127
986
|
uv1: Object.freeze(uv1),
|
|
1128
987
|
uv2: Object.freeze(uv2),
|
|
1129
|
-
color:
|
|
988
|
+
color: mesh.color,
|
|
1130
989
|
emission: mesh.emission,
|
|
1131
990
|
material: Object.freeze([
|
|
1132
|
-
|
|
1133
|
-
|
|
991
|
+
mesh.roughness,
|
|
992
|
+
mesh.metallic,
|
|
1134
993
|
mesh.opacity,
|
|
1135
994
|
mesh.ior
|
|
1136
995
|
]),
|
|
@@ -1152,6 +1011,27 @@ function createMeshTriangleRecords(meshes) {
|
|
|
1152
1011
|
mesh.specularColor[2] ?? 1,
|
|
1153
1012
|
1
|
|
1154
1013
|
]),
|
|
1014
|
+
baseColorAtlas: Object.freeze(
|
|
1015
|
+
resolvedMaterialSource.baseColorAtlas.resolveRect(mesh.baseColorTexture)
|
|
1016
|
+
),
|
|
1017
|
+
metallicRoughnessAtlas: Object.freeze(
|
|
1018
|
+
resolvedMaterialSource.metallicRoughnessAtlas.resolveRect(mesh.metallicRoughnessTexture)
|
|
1019
|
+
),
|
|
1020
|
+
normalAtlas: Object.freeze(
|
|
1021
|
+
resolvedMaterialSource.normalAtlas.resolveRect(mesh.normalTexture)
|
|
1022
|
+
),
|
|
1023
|
+
occlusionAtlas: Object.freeze(
|
|
1024
|
+
resolvedMaterialSource.occlusionAtlas.resolveRect(mesh.occlusionTexture)
|
|
1025
|
+
),
|
|
1026
|
+
emissiveAtlas: Object.freeze(
|
|
1027
|
+
resolvedMaterialSource.emissiveAtlas.resolveRect(mesh.emissiveTexture)
|
|
1028
|
+
),
|
|
1029
|
+
textureSettings: Object.freeze([
|
|
1030
|
+
clampUnit(mesh.normalTexture?.scale ?? mesh.normalTexture?.strength ?? 1),
|
|
1031
|
+
clampUnit(mesh.occlusionTexture?.strength ?? 1),
|
|
1032
|
+
clampUnit(mesh.emissiveTexture?.strength ?? 1),
|
|
1033
|
+
0
|
|
1034
|
+
]),
|
|
1155
1035
|
bounds: Object.freeze({
|
|
1156
1036
|
min: Object.freeze(bounds.min),
|
|
1157
1037
|
max: Object.freeze(bounds.max)
|
|
@@ -1226,9 +1106,10 @@ function buildBvh(triangles, maxLeafTriangles = 4) {
|
|
|
1226
1106
|
triangles: Object.freeze(orderedTriangles)
|
|
1227
1107
|
});
|
|
1228
1108
|
}
|
|
1229
|
-
function createWavefrontMeshAcceleration(meshes = []) {
|
|
1109
|
+
function createWavefrontMeshAcceleration(meshes = [], gpuMaterialSource = null) {
|
|
1230
1110
|
const source = Array.isArray(meshes) ? meshes : [meshes];
|
|
1231
|
-
const
|
|
1111
|
+
const resolvedMaterialSource = gpuMaterialSource ?? createWavefrontGpuMaterialSource(source);
|
|
1112
|
+
const triangles = createMeshTriangleRecords(source, resolvedMaterialSource);
|
|
1232
1113
|
return buildBvh(triangles);
|
|
1233
1114
|
}
|
|
1234
1115
|
function estimateMeshSourceShape(meshes) {
|
|
@@ -1522,12 +1403,12 @@ function createWavefrontBvhBuildLevels(triangleCountInput) {
|
|
|
1522
1403
|
return Object.freeze(levels);
|
|
1523
1404
|
}
|
|
1524
1405
|
function resolveAccelerationBuildMode(options = {}) {
|
|
1525
|
-
const
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1406
|
+
const requestedMode = options.accelerationBuildMode ?? (options.displayQuality === true ? "cpu-upload" : "cpu-debug");
|
|
1407
|
+
const mode = requestedMode === "cpu-debug" ? "cpu-upload" : requestedMode;
|
|
1408
|
+
if (mode !== "gpu" && mode !== "cpu-upload") {
|
|
1409
|
+
throw new Error(
|
|
1410
|
+
'accelerationBuildMode must be either "gpu", "cpu-upload", or the legacy alias "cpu-debug".'
|
|
1411
|
+
);
|
|
1531
1412
|
}
|
|
1532
1413
|
return mode;
|
|
1533
1414
|
}
|
|
@@ -2013,7 +1894,7 @@ function createWavefrontPathTracingComputeConfig(options = {}) {
|
|
|
2013
1894
|
const meshSourceShape = estimateMeshSourceShape(meshes);
|
|
2014
1895
|
const gpuMaterialSource = meshes.length > 0 ? createWavefrontGpuMaterialSource(meshes) : createWavefrontGpuMaterialSource([]);
|
|
2015
1896
|
const gpuMeshSource = meshes.length > 0 ? createWavefrontGpuMeshSource(meshes, gpuMaterialSource) : createWavefrontGpuMeshSource([]);
|
|
2016
|
-
const meshAcceleration = accelerationBuildMode === "cpu-
|
|
1897
|
+
const meshAcceleration = accelerationBuildMode === "cpu-upload" ? createWavefrontMeshAcceleration(meshes, gpuMaterialSource) : Object.freeze({ nodes: Object.freeze([]), triangles: Object.freeze([]) });
|
|
2017
1898
|
const emissiveTriangleIndices = createWavefrontEmissiveTriangleIndexSource(
|
|
2018
1899
|
meshes,
|
|
2019
1900
|
options.emissiveTriangleCapacity
|
|
@@ -5825,9 +5706,22 @@ function nowMs() {
|
|
|
5825
5706
|
}
|
|
5826
5707
|
return Date.now();
|
|
5827
5708
|
}
|
|
5828
|
-
function
|
|
5709
|
+
function estimateAccelerationBuildWaitFactor(config) {
|
|
5710
|
+
if (config?.gpuAccelerationBuildRequired !== true) {
|
|
5711
|
+
return 1;
|
|
5712
|
+
}
|
|
5713
|
+
const bvhSortStageCount = Array.isArray(config?.bvhSortStages) ? config.bvhSortStages.length : 0;
|
|
5714
|
+
const bvhBuildLevelCount = Array.isArray(config?.bvhBuildLevels) ? config.bvhBuildLevels.length : 0;
|
|
5715
|
+
const accelerationStageCount = 2 + bvhSortStageCount + bvhBuildLevelCount;
|
|
5716
|
+
return Math.max(1, 1 + accelerationStageCount / 96);
|
|
5717
|
+
}
|
|
5718
|
+
function estimateSubmittedGpuWorkTiming(config, tileCount, overrideTimeoutMs = null, options = {}) {
|
|
5829
5719
|
if (Number.isFinite(overrideTimeoutMs)) {
|
|
5830
|
-
|
|
5720
|
+
const overrideMs = Math.max(1, Math.trunc(Number(overrideTimeoutMs)));
|
|
5721
|
+
return Object.freeze({
|
|
5722
|
+
timeoutMs: overrideMs,
|
|
5723
|
+
maxWaitMs: overrideMs
|
|
5724
|
+
});
|
|
5831
5725
|
}
|
|
5832
5726
|
const samplesPerPixel = Math.max(
|
|
5833
5727
|
1,
|
|
@@ -5838,10 +5732,26 @@ function estimateSubmittedGpuWorkTimeoutMs(config, tileCount, overrideTimeoutMs
|
|
|
5838
5732
|
const denoisePasses = config?.denoise ? samplesPerPixel < 4 ? 2 : 1 : 0;
|
|
5839
5733
|
const tiles = Math.max(1, Number(tileCount ?? 1));
|
|
5840
5734
|
const estimatedPasses = tiles * (samplesPerPixel * (maxDepth + 1 + deferredResolvePasses) + denoisePasses + 1);
|
|
5841
|
-
|
|
5735
|
+
const triangleCount = Math.max(0, Number(config?.triangleCount ?? 0));
|
|
5736
|
+
const geometryFactor = Math.max(1, triangleCount / 131072);
|
|
5737
|
+
const includeAccelerationBuild = options.includeAccelerationBuild === true;
|
|
5738
|
+
const accelerationFactor = includeAccelerationBuild ? estimateAccelerationBuildWaitFactor(config) : 1;
|
|
5739
|
+
const estimatedWindowMs = Math.round(
|
|
5740
|
+
(GPU_SUBMITTED_WORK_TIMEOUT_MS + estimatedPasses * 5) * geometryFactor * accelerationFactor
|
|
5741
|
+
);
|
|
5742
|
+
const timeoutMs = Math.min(
|
|
5842
5743
|
GPU_MAX_SUBMITTED_WORK_TIMEOUT_MS,
|
|
5843
|
-
GPU_SUBMITTED_WORK_TIMEOUT_MS
|
|
5744
|
+
Math.max(GPU_SUBMITTED_WORK_TIMEOUT_MS, estimatedWindowMs)
|
|
5844
5745
|
);
|
|
5746
|
+
const maxWaitMultiplier = includeAccelerationBuild ? 3 : 2;
|
|
5747
|
+
const maxWaitMs = Math.min(
|
|
5748
|
+
GPU_MAX_SUBMITTED_WORK_DEADLINE_MS,
|
|
5749
|
+
Math.max(timeoutMs, estimatedWindowMs * maxWaitMultiplier)
|
|
5750
|
+
);
|
|
5751
|
+
return Object.freeze({
|
|
5752
|
+
timeoutMs,
|
|
5753
|
+
maxWaitMs
|
|
5754
|
+
});
|
|
5845
5755
|
}
|
|
5846
5756
|
async function createWavefrontPathTracingComputeRenderer(options = {}) {
|
|
5847
5757
|
assertAnalyticDisplayQualityPolicy(options);
|
|
@@ -6817,6 +6727,10 @@ async function createWavefrontPathTracingComputeRenderer(options = {}) {
|
|
|
6817
6727
|
1,
|
|
6818
6728
|
Number.isFinite(options2.timeoutMs) ? Number(options2.timeoutMs) : GPU_SUBMITTED_WORK_TIMEOUT_MS
|
|
6819
6729
|
);
|
|
6730
|
+
const maxWaitMs = Math.max(
|
|
6731
|
+
timeoutMs,
|
|
6732
|
+
Number.isFinite(options2.maxWaitMs) ? Number(options2.maxWaitMs) : timeoutMs
|
|
6733
|
+
);
|
|
6820
6734
|
const allowTimeout = options2.allowTimeout !== false;
|
|
6821
6735
|
const completionPromise = device.queue.onSubmittedWorkDone().then(
|
|
6822
6736
|
() => ({ status: "done" }),
|
|
@@ -6829,43 +6743,57 @@ async function createWavefrontPathTracingComputeRenderer(options = {}) {
|
|
|
6829
6743
|
`WebGPU device lost while waiting for submitted work (${info?.reason ?? "unknown"}).`
|
|
6830
6744
|
);
|
|
6831
6745
|
}) : null;
|
|
6832
|
-
|
|
6833
|
-
|
|
6834
|
-
|
|
6835
|
-
|
|
6836
|
-
if (
|
|
6837
|
-
|
|
6746
|
+
const startedAtMs = nowMs();
|
|
6747
|
+
while (true) {
|
|
6748
|
+
const elapsedMs = Math.max(0, nowMs() - startedAtMs);
|
|
6749
|
+
const remainingMs = Math.max(0, maxWaitMs - elapsedMs);
|
|
6750
|
+
if (remainingMs <= 0) {
|
|
6751
|
+
if (!allowTimeout) {
|
|
6752
|
+
throw new Error(`Timed out after ${Math.round(maxWaitMs)} ms waiting for submitted GPU work.`);
|
|
6753
|
+
}
|
|
6754
|
+
console.warn(
|
|
6755
|
+
`[plasius.wavefront] Submitted GPU work did not report completion within ${Math.round(maxWaitMs)} ms; continuing.`
|
|
6756
|
+
);
|
|
6757
|
+
return false;
|
|
6838
6758
|
}
|
|
6839
|
-
|
|
6840
|
-
|
|
6841
|
-
|
|
6842
|
-
|
|
6843
|
-
|
|
6844
|
-
|
|
6845
|
-
|
|
6846
|
-
|
|
6847
|
-
|
|
6848
|
-
|
|
6849
|
-
|
|
6850
|
-
)
|
|
6851
|
-
|
|
6852
|
-
|
|
6853
|
-
|
|
6854
|
-
|
|
6759
|
+
const waitWindowMs = Math.max(1, Math.min(timeoutMs, remainingMs));
|
|
6760
|
+
let timeoutHandle = null;
|
|
6761
|
+
let resolveTimeoutPromise = null;
|
|
6762
|
+
let timeoutSettled = false;
|
|
6763
|
+
const settleTimeoutPromise = (value) => {
|
|
6764
|
+
if (timeoutSettled) {
|
|
6765
|
+
return;
|
|
6766
|
+
}
|
|
6767
|
+
timeoutSettled = true;
|
|
6768
|
+
resolveTimeoutPromise?.(value);
|
|
6769
|
+
};
|
|
6770
|
+
const timeoutPromise = new Promise((resolve) => {
|
|
6771
|
+
resolveTimeoutPromise = resolve;
|
|
6772
|
+
timeoutHandle = setTimeout(
|
|
6773
|
+
() => settleTimeoutPromise({ status: "timeout" }),
|
|
6774
|
+
waitWindowMs
|
|
6775
|
+
);
|
|
6776
|
+
});
|
|
6777
|
+
let result;
|
|
6778
|
+
try {
|
|
6779
|
+
result = await Promise.race(
|
|
6780
|
+
[completionPromise, timeoutPromise, lossPromise].filter(Boolean)
|
|
6781
|
+
);
|
|
6782
|
+
} finally {
|
|
6783
|
+
if (timeoutHandle !== null) {
|
|
6784
|
+
clearTimeout(timeoutHandle);
|
|
6785
|
+
settleTimeoutPromise({ status: "cancelled" });
|
|
6786
|
+
}
|
|
6855
6787
|
}
|
|
6856
|
-
|
|
6857
|
-
|
|
6858
|
-
|
|
6859
|
-
|
|
6788
|
+
if (result?.status === "done") {
|
|
6789
|
+
return true;
|
|
6790
|
+
}
|
|
6791
|
+
if (result?.status !== "timeout") {
|
|
6792
|
+
return true;
|
|
6860
6793
|
}
|
|
6861
|
-
console.warn(
|
|
6862
|
-
`[plasius.wavefront] Submitted GPU work did not report completion within ${timeoutMs} ms; continuing.`
|
|
6863
|
-
);
|
|
6864
|
-
return false;
|
|
6865
6794
|
}
|
|
6866
|
-
return true;
|
|
6867
6795
|
}
|
|
6868
|
-
function dispatchFrameAwaitingGpu(frameIndex, parallelism, renderedSamplesPerPixel = config.samplesPerPixel) {
|
|
6796
|
+
function dispatchFrameAwaitingGpu(frameIndex, parallelism, renderedSamplesPerPixel = config.samplesPerPixel, optionsForFrame = {}) {
|
|
6869
6797
|
const samplePassesPerSample = config.maxDepth + 1 + (config.deferredPathResolve ? 1 : 0);
|
|
6870
6798
|
const denoisePassCount = config.denoise ? renderedSamplesPerPixel < 4 ? 2 : 1 : 0;
|
|
6871
6799
|
const tailPassCount = denoisePassCount + 1;
|
|
@@ -6875,17 +6803,42 @@ async function createWavefrontPathTracingComputeRenderer(options = {}) {
|
|
|
6875
6803
|
Math.max(config.maxFramePassesPerSubmission - tailPassCount, 1) / Math.max(samplePassesPerSample, 1)
|
|
6876
6804
|
)
|
|
6877
6805
|
);
|
|
6878
|
-
|
|
6879
|
-
|
|
6880
|
-
|
|
6881
|
-
|
|
6806
|
+
const sampleRangeStart = clamp(
|
|
6807
|
+
readNonNegativeInteger("sampleRangeStart", optionsForFrame.sampleRangeStart, 0),
|
|
6808
|
+
0,
|
|
6809
|
+
renderedSamplesPerPixel
|
|
6810
|
+
);
|
|
6811
|
+
const sampleRangeEnd = clamp(
|
|
6812
|
+
readPositiveInteger("sampleRangeEnd", optionsForFrame.sampleRangeEnd, renderedSamplesPerPixel),
|
|
6813
|
+
sampleRangeStart,
|
|
6814
|
+
renderedSamplesPerPixel
|
|
6815
|
+
);
|
|
6816
|
+
const includeDenoise = optionsForFrame.includeDenoise === true;
|
|
6817
|
+
const includePresent = optionsForFrame.includePresent === true;
|
|
6818
|
+
const tileStartIndex = clamp(
|
|
6819
|
+
readNonNegativeInteger("tileStartIndex", optionsForFrame.tileStartIndex, 0),
|
|
6820
|
+
0,
|
|
6821
|
+
tiles.length
|
|
6822
|
+
);
|
|
6823
|
+
const tileEndIndex = clamp(
|
|
6824
|
+
readPositiveInteger("tileEndIndex", optionsForFrame.tileEndIndex, tiles.length),
|
|
6825
|
+
tileStartIndex,
|
|
6826
|
+
tiles.length
|
|
6827
|
+
);
|
|
6828
|
+
let submissionCount = Math.max(
|
|
6829
|
+
0,
|
|
6830
|
+
readNonNegativeInteger("startingSubmissionCount", optionsForFrame.startingSubmissionCount, 0)
|
|
6831
|
+
);
|
|
6832
|
+
let slot = Math.max(0, readNonNegativeInteger("startingSlot", optionsForFrame.startingSlot, 0));
|
|
6833
|
+
for (const tile of tiles.slice(tileStartIndex, tileEndIndex)) {
|
|
6834
|
+
for (let sampleStart = sampleRangeStart; sampleStart < sampleRangeEnd; sampleStart += sampleBatchSize) {
|
|
6835
|
+
const sampleEnd = Math.min(sampleRangeEnd, sampleStart + sampleBatchSize);
|
|
6882
6836
|
const batch = createGpuSubmissionBatcher({
|
|
6883
6837
|
device,
|
|
6884
6838
|
frameIndex,
|
|
6885
6839
|
maxFramePassesPerSubmission: config.maxFramePassesPerSubmission,
|
|
6886
6840
|
startingSubmissionCount: submissionCount
|
|
6887
6841
|
});
|
|
6888
|
-
let slot = 0;
|
|
6889
6842
|
for (let sampleIndex = sampleStart; sampleIndex < sampleEnd; sampleIndex += 1) {
|
|
6890
6843
|
const configOffset = writeFrameConfigSlot(slot, tile, frameIndex, {
|
|
6891
6844
|
sampleIndex,
|
|
@@ -6902,41 +6855,50 @@ async function createWavefrontPathTracingComputeRenderer(options = {}) {
|
|
|
6902
6855
|
encodeTileOutput(batch.reserve(1), tile, configOffset, parallelism);
|
|
6903
6856
|
}
|
|
6904
6857
|
}
|
|
6905
|
-
if (!config.deferredPathResolve &&
|
|
6858
|
+
if (!config.deferredPathResolve && sampleRangeEnd >= renderedSamplesPerPixel) {
|
|
6906
6859
|
const outputConfigOffset = writeFrameConfigSlot(slot, tile, frameIndex, {
|
|
6907
6860
|
sampleIndex: 0,
|
|
6908
6861
|
sampleWeight: 1 / renderedSamplesPerPixel
|
|
6909
6862
|
});
|
|
6863
|
+
slot += 1;
|
|
6910
6864
|
encodeTileOutput(batch.reserve(1), tile, outputConfigOffset, parallelism);
|
|
6911
6865
|
}
|
|
6912
6866
|
batch.flush();
|
|
6913
6867
|
submissionCount += batch.getSubmissionCount();
|
|
6914
6868
|
}
|
|
6915
6869
|
}
|
|
6916
|
-
|
|
6917
|
-
|
|
6918
|
-
|
|
6919
|
-
maxFramePassesPerSubmission: config.maxFramePassesPerSubmission,
|
|
6920
|
-
startingSubmissionCount: submissionCount
|
|
6921
|
-
});
|
|
6922
|
-
if (config.denoise) {
|
|
6923
|
-
const denoiseConfigOffset = writeFrameConfigSlot(
|
|
6924
|
-
0,
|
|
6925
|
-
{ x: 0, y: 0, width: config.width, height: config.height },
|
|
6870
|
+
if (includeDenoise || includePresent) {
|
|
6871
|
+
const tail = createGpuSubmissionBatcher({
|
|
6872
|
+
device,
|
|
6926
6873
|
frameIndex,
|
|
6927
|
-
|
|
6928
|
-
|
|
6929
|
-
|
|
6930
|
-
|
|
6931
|
-
denoiseConfigOffset
|
|
6932
|
-
|
|
6933
|
-
|
|
6934
|
-
|
|
6874
|
+
maxFramePassesPerSubmission: config.maxFramePassesPerSubmission,
|
|
6875
|
+
startingSubmissionCount: submissionCount
|
|
6876
|
+
});
|
|
6877
|
+
if (includeDenoise && config.denoise) {
|
|
6878
|
+
const denoiseConfigOffset = writeFrameConfigSlot(
|
|
6879
|
+
slot,
|
|
6880
|
+
{ x: 0, y: 0, width: config.width, height: config.height },
|
|
6881
|
+
frameIndex,
|
|
6882
|
+
{ sampleIndex: 0, sampleWeight: 1 / renderedSamplesPerPixel }
|
|
6883
|
+
);
|
|
6884
|
+
slot += 1;
|
|
6885
|
+
encodeDenoise(
|
|
6886
|
+
tail.reserve(denoisePassCount),
|
|
6887
|
+
denoiseConfigOffset,
|
|
6888
|
+
parallelism,
|
|
6889
|
+
renderedSamplesPerPixel
|
|
6890
|
+
);
|
|
6891
|
+
}
|
|
6892
|
+
if (includePresent) {
|
|
6893
|
+
encodePresent(tail.reserve(1));
|
|
6894
|
+
}
|
|
6895
|
+
tail.flush();
|
|
6896
|
+
submissionCount += tail.getSubmissionCount();
|
|
6935
6897
|
}
|
|
6936
|
-
|
|
6937
|
-
|
|
6938
|
-
|
|
6939
|
-
|
|
6898
|
+
return Object.freeze({
|
|
6899
|
+
submissionCount,
|
|
6900
|
+
slot
|
|
6901
|
+
});
|
|
6940
6902
|
}
|
|
6941
6903
|
async function readOutputProbe(optionsForProbe = {}) {
|
|
6942
6904
|
const mapMode = constants.map;
|
|
@@ -6982,24 +6944,59 @@ async function createWavefrontPathTracingComputeRenderer(options = {}) {
|
|
|
6982
6944
|
const awaitGPUCompletion = renderOptions.awaitGPUCompletion !== false;
|
|
6983
6945
|
const samplingPlan = resolveRenderedSamplesPerPixel(renderOptions, awaitGPUCompletion);
|
|
6984
6946
|
const useThrottledHighSamplePath = awaitGPUCompletion && samplingPlan.renderedSamplesPerPixel >= 8;
|
|
6985
|
-
const submittedWorkTimeoutMs = estimateSubmittedGpuWorkTimeoutMs(
|
|
6986
|
-
{ ...config, renderedSamplesPerPixel: samplingPlan.renderedSamplesPerPixel },
|
|
6987
|
-
tiles.length,
|
|
6988
|
-
renderOptions.submittedWorkTimeoutMs
|
|
6989
|
-
);
|
|
6990
6947
|
const frameStartTimeMs = nowMs();
|
|
6991
|
-
const submissionWaitOptions = awaitGPUCompletion ? { timeoutMs: submittedWorkTimeoutMs, allowTimeout: false } : { timeoutMs: submittedWorkTimeoutMs };
|
|
6992
6948
|
let frameStats;
|
|
6993
6949
|
if (useThrottledHighSamplePath) {
|
|
6994
6950
|
frame += 1;
|
|
6995
6951
|
const frameIndex = frame + config.frameIndex;
|
|
6996
6952
|
const parallelismCounters = createGpuParallelismCounters();
|
|
6997
6953
|
const accelerationBuildSubmitted = dispatchGpuAccelerationBuild(frameIndex, parallelismCounters);
|
|
6998
|
-
|
|
6999
|
-
|
|
7000
|
-
|
|
7001
|
-
|
|
7002
|
-
|
|
6954
|
+
let frameSubmissionCount = 0;
|
|
6955
|
+
let frameConfigSlot = 0;
|
|
6956
|
+
if (accelerationBuildSubmitted) {
|
|
6957
|
+
const accelerationWaitOptions = {
|
|
6958
|
+
...estimateSubmittedGpuWorkTiming(
|
|
6959
|
+
{ ...config, renderedSamplesPerPixel: 1 },
|
|
6960
|
+
1,
|
|
6961
|
+
renderOptions.submittedWorkTimeoutMs,
|
|
6962
|
+
{ includeAccelerationBuild: true }
|
|
6963
|
+
),
|
|
6964
|
+
allowTimeout: false
|
|
6965
|
+
};
|
|
6966
|
+
await waitForSubmittedGpuWork(accelerationWaitOptions);
|
|
6967
|
+
}
|
|
6968
|
+
for (let tileIndex = 0; tileIndex < tiles.length; tileIndex += 1) {
|
|
6969
|
+
const tileRangeDispatch = dispatchFrameAwaitingGpu(
|
|
6970
|
+
frameIndex,
|
|
6971
|
+
parallelismCounters,
|
|
6972
|
+
samplingPlan.renderedSamplesPerPixel,
|
|
6973
|
+
{
|
|
6974
|
+
sampleRangeStart: 0,
|
|
6975
|
+
sampleRangeEnd: samplingPlan.renderedSamplesPerPixel,
|
|
6976
|
+
tileStartIndex: tileIndex,
|
|
6977
|
+
tileEndIndex: tileIndex + 1,
|
|
6978
|
+
startingSubmissionCount: frameSubmissionCount,
|
|
6979
|
+
startingSlot: frameConfigSlot,
|
|
6980
|
+
includeDenoise: tileIndex + 1 >= tiles.length,
|
|
6981
|
+
includePresent: tileIndex + 1 >= tiles.length
|
|
6982
|
+
}
|
|
6983
|
+
);
|
|
6984
|
+
frameSubmissionCount = tileRangeDispatch.submissionCount;
|
|
6985
|
+
frameConfigSlot = tileRangeDispatch.slot;
|
|
6986
|
+
const tileWaitOptions = {
|
|
6987
|
+
...estimateSubmittedGpuWorkTiming(
|
|
6988
|
+
{ ...config, renderedSamplesPerPixel: samplingPlan.renderedSamplesPerPixel },
|
|
6989
|
+
1,
|
|
6990
|
+
renderOptions.submittedWorkTimeoutMs,
|
|
6991
|
+
{
|
|
6992
|
+
includeDenoise: tileIndex + 1 >= tiles.length && config.denoise,
|
|
6993
|
+
includePresent: tileIndex + 1 >= tiles.length
|
|
6994
|
+
}
|
|
6995
|
+
),
|
|
6996
|
+
allowTimeout: false
|
|
6997
|
+
};
|
|
6998
|
+
await waitForSubmittedGpuWork(tileWaitOptions);
|
|
6999
|
+
}
|
|
7003
7000
|
frameStats = createFrameStats({
|
|
7004
7001
|
frameIndex,
|
|
7005
7002
|
accelerationBuildSubmitted,
|
|
@@ -7011,10 +7008,24 @@ async function createWavefrontPathTracingComputeRenderer(options = {}) {
|
|
|
7011
7008
|
budgetConstrained: samplingPlan.budgetConstrained
|
|
7012
7009
|
});
|
|
7013
7010
|
} else {
|
|
7011
|
+
const submittedWorkTiming = estimateSubmittedGpuWorkTiming(
|
|
7012
|
+
{ ...config, renderedSamplesPerPixel: samplingPlan.renderedSamplesPerPixel },
|
|
7013
|
+
tiles.length,
|
|
7014
|
+
renderOptions.submittedWorkTimeoutMs,
|
|
7015
|
+
{ includeAccelerationBuild: config.gpuAccelerationBuildRequired && !accelerationBuilt }
|
|
7016
|
+
);
|
|
7017
|
+
const submissionWaitOptions = awaitGPUCompletion ? {
|
|
7018
|
+
timeoutMs: submittedWorkTiming.timeoutMs,
|
|
7019
|
+
maxWaitMs: submittedWorkTiming.maxWaitMs,
|
|
7020
|
+
allowTimeout: false
|
|
7021
|
+
} : {
|
|
7022
|
+
timeoutMs: submittedWorkTiming.timeoutMs,
|
|
7023
|
+
maxWaitMs: submittedWorkTiming.maxWaitMs
|
|
7024
|
+
};
|
|
7014
7025
|
frameStats = renderOnce(renderOptions, samplingPlan);
|
|
7015
|
-
|
|
7016
|
-
|
|
7017
|
-
|
|
7026
|
+
if (awaitGPUCompletion) {
|
|
7027
|
+
await waitForSubmittedGpuWork(submissionWaitOptions);
|
|
7028
|
+
}
|
|
7018
7029
|
}
|
|
7019
7030
|
const frameTimeMs = Math.max(0, nowMs() - frameStartTimeMs);
|
|
7020
7031
|
if (awaitGPUCompletion) {
|