@plasius/gpu-renderer 0.2.3 → 0.2.4
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 +18 -2
- package/README.md +30 -9
- package/dist/index.cjs +611 -68
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +611 -68
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/index.d.ts +83 -0
- package/src/wavefront-compute.js +640 -63
package/dist/index.js
CHANGED
|
@@ -22,11 +22,12 @@ var BVH_LEAF_REF_RECORD_BYTES = 16;
|
|
|
22
22
|
var EMISSIVE_TRIANGLE_INDEX_BYTES = 4;
|
|
23
23
|
var ENVIRONMENT_PORTAL_RECORD_BYTES = 96;
|
|
24
24
|
var ACCUMULATION_RECORD_BYTES = 16;
|
|
25
|
-
var
|
|
25
|
+
var PATH_VERTEX_RECORD_BYTES = 16;
|
|
26
|
+
var CONFIG_BUFFER_BYTES = 304;
|
|
26
27
|
var COUNTER_DISPATCH_ARGS_OFFSET = 16;
|
|
27
28
|
var INDIRECT_DISPATCH_ARGS_BYTES = 12;
|
|
28
29
|
var COUNTER_BUFFER_BYTES = 32;
|
|
29
|
-
var TRACE_STORAGE_BUFFER_BINDINGS =
|
|
30
|
+
var TRACE_STORAGE_BUFFER_BINDINGS = 10;
|
|
30
31
|
var MATERIAL_DIFFUSE = 0;
|
|
31
32
|
var MATERIAL_METAL = 1;
|
|
32
33
|
var MATERIAL_DIELECTRIC = 2;
|
|
@@ -49,7 +50,8 @@ var DEFAULT_ENVIRONMENT_LIGHTING = Object.freeze({
|
|
|
49
50
|
sunColor: Object.freeze([2.8, 2.65, 2.35, 1]),
|
|
50
51
|
intensity: 1,
|
|
51
52
|
mode: 0,
|
|
52
|
-
exposure: 1
|
|
53
|
+
exposure: 1,
|
|
54
|
+
sunlitBaseline: 0.16
|
|
53
55
|
});
|
|
54
56
|
var wavefrontPathTracingComputeLimits = Object.freeze({
|
|
55
57
|
workgroupSize: WORKGROUP_SIZE,
|
|
@@ -66,6 +68,7 @@ var wavefrontPathTracingComputeLimits = Object.freeze({
|
|
|
66
68
|
emissiveTriangleMetadataRecordBytes: BVH_NODE_RECORD_BYTES,
|
|
67
69
|
environmentPortalRecordBytes: ENVIRONMENT_PORTAL_RECORD_BYTES,
|
|
68
70
|
accumulationRecordBytes: ACCUMULATION_RECORD_BYTES,
|
|
71
|
+
pathVertexRecordBytes: PATH_VERTEX_RECORD_BYTES,
|
|
69
72
|
counterRecordBytes: COUNTER_BUFFER_BYTES,
|
|
70
73
|
indirectDispatchRecordBytes: INDIRECT_DISPATCH_ARGS_BYTES
|
|
71
74
|
});
|
|
@@ -142,6 +145,33 @@ function asColor(value, fallback = [1, 1, 1, 1]) {
|
|
|
142
145
|
clamp(readFiniteNumber("color[3]", value[3], fallback[3] ?? 1), 0, 1)
|
|
143
146
|
];
|
|
144
147
|
}
|
|
148
|
+
function resolveEnvironmentMap(input = null) {
|
|
149
|
+
const source = input && typeof input === "object" ? input : null;
|
|
150
|
+
const hasTexture = Boolean(source?.view || source?.texture || source?.data);
|
|
151
|
+
const width = readPositiveInteger("environmentMap.width", source?.width, 1);
|
|
152
|
+
const height = readPositiveInteger("environmentMap.height", source?.height, 1);
|
|
153
|
+
return Object.freeze({
|
|
154
|
+
enabled: hasTexture && source?.enabled !== false,
|
|
155
|
+
width,
|
|
156
|
+
height,
|
|
157
|
+
format: typeof source?.format === "string" ? source.format : "rgba16float",
|
|
158
|
+
projection: typeof source?.projection === "string" ? source.projection : "equirectangular",
|
|
159
|
+
texture: source?.texture ?? null,
|
|
160
|
+
view: source?.view ?? null,
|
|
161
|
+
sampler: source?.sampler ?? null,
|
|
162
|
+
data: source?.data ?? null,
|
|
163
|
+
intensity: Math.max(0, readFiniteNumber("environmentMap.intensity", source?.intensity ?? source?.radianceScale, 1)),
|
|
164
|
+
rotationRadians: readFiniteNumber("environmentMap.rotationRadians", source?.rotationRadians ?? source?.rotation, 0),
|
|
165
|
+
ambientStrength: Math.max(
|
|
166
|
+
0,
|
|
167
|
+
readFiniteNumber("environmentMap.ambientStrength", source?.ambientStrength, 0.32)
|
|
168
|
+
)
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
function resolveDeferredPathResolve(options = {}) {
|
|
172
|
+
const value = options.deferredPathResolve ?? options.deferredResolve ?? options.pathResolve?.deferred ?? true;
|
|
173
|
+
return value !== false;
|
|
174
|
+
}
|
|
145
175
|
function emissionPower(emission) {
|
|
146
176
|
return Math.max(0, emission?.[0] ?? 0) + Math.max(0, emission?.[1] ?? 0) + Math.max(0, emission?.[2] ?? 0);
|
|
147
177
|
}
|
|
@@ -789,7 +819,15 @@ function resolveEnvironmentLighting(input, environmentColor, ambientColor) {
|
|
|
789
819
|
sunColor: Object.freeze(asColor(source.sunColor, DEFAULT_ENVIRONMENT_LIGHTING.sunColor)),
|
|
790
820
|
intensity: Math.max(1e-4, readFiniteNumber("environmentLighting.intensity", source.intensity, DEFAULT_ENVIRONMENT_LIGHTING.intensity)),
|
|
791
821
|
mode: readNonNegativeInteger("environmentLighting.mode", source.mode, DEFAULT_ENVIRONMENT_LIGHTING.mode),
|
|
792
|
-
exposure: Math.max(1e-4, readFiniteNumber("environmentLighting.exposure", source.exposure, DEFAULT_ENVIRONMENT_LIGHTING.exposure))
|
|
822
|
+
exposure: Math.max(1e-4, readFiniteNumber("environmentLighting.exposure", source.exposure, DEFAULT_ENVIRONMENT_LIGHTING.exposure)),
|
|
823
|
+
sunlitBaseline: Math.max(
|
|
824
|
+
0,
|
|
825
|
+
readFiniteNumber(
|
|
826
|
+
"environmentLighting.sunlitBaseline",
|
|
827
|
+
source.sunlitBaseline ?? source.daylightBaseline,
|
|
828
|
+
DEFAULT_ENVIRONMENT_LIGHTING.sunlitBaseline
|
|
829
|
+
)
|
|
830
|
+
)
|
|
793
831
|
});
|
|
794
832
|
}
|
|
795
833
|
function evaluateReferenceEnvironmentRadiance(config, origin, direction) {
|
|
@@ -974,6 +1012,11 @@ function estimateWavefrontPathTracingMemory(options = {}) {
|
|
|
974
1012
|
options.tilePixelCapacity,
|
|
975
1013
|
DEFAULT_TILE_SIZE * DEFAULT_TILE_SIZE
|
|
976
1014
|
);
|
|
1015
|
+
const maxDepth = clamp(
|
|
1016
|
+
readPositiveInteger("maxDepth", options.maxDepth, DEFAULT_MAX_DEPTH),
|
|
1017
|
+
1,
|
|
1018
|
+
16
|
|
1019
|
+
);
|
|
977
1020
|
const sceneObjectCapacity = readPositiveInteger(
|
|
978
1021
|
"sceneObjectCapacity",
|
|
979
1022
|
options.sceneObjectCapacity,
|
|
@@ -999,6 +1042,7 @@ function estimateWavefrontPathTracingMemory(options = {}) {
|
|
|
999
1042
|
const queueBytes = tilePixelCapacity * RAY_RECORD_BYTES;
|
|
1000
1043
|
const hitBytes = tilePixelCapacity * HIT_RECORD_BYTES;
|
|
1001
1044
|
const accumulationBytes = tilePixelCapacity * ACCUMULATION_RECORD_BYTES;
|
|
1045
|
+
const pathVertexBytes = tilePixelCapacity * (maxDepth + 1) * PATH_VERTEX_RECORD_BYTES;
|
|
1002
1046
|
const sceneObjectBytes = sceneObjectCapacity * SCENE_OBJECT_RECORD_BYTES;
|
|
1003
1047
|
const triangleBytes = triangleCapacity * TRIANGLE_RECORD_BYTES;
|
|
1004
1048
|
const bvhNodeBytes = bvhNodeCapacity * BVH_NODE_RECORD_BYTES;
|
|
@@ -1010,6 +1054,7 @@ function estimateWavefrontPathTracingMemory(options = {}) {
|
|
|
1010
1054
|
queuePairBytes: queueBytes * 2,
|
|
1011
1055
|
hitBytes,
|
|
1012
1056
|
accumulationBytes,
|
|
1057
|
+
pathVertexBytes,
|
|
1013
1058
|
sceneObjectBytes,
|
|
1014
1059
|
triangleBytes,
|
|
1015
1060
|
bvhNodeBytes,
|
|
@@ -1019,7 +1064,7 @@ function estimateWavefrontPathTracingMemory(options = {}) {
|
|
|
1019
1064
|
configBytes: CONFIG_BUFFER_BYTES,
|
|
1020
1065
|
counterBytes: COUNTER_BUFFER_BYTES,
|
|
1021
1066
|
indirectDispatchBytes: INDIRECT_DISPATCH_ARGS_BYTES,
|
|
1022
|
-
totalHotBufferBytes: queueBytes * 2 + hitBytes + accumulationBytes + sceneObjectBytes + triangleBytes + bvhNodeBytes + bvhLeafReferenceBytes + emissiveTriangleMetadataBytes + environmentPortalBytes + CONFIG_BUFFER_BYTES + COUNTER_BUFFER_BYTES + INDIRECT_DISPATCH_ARGS_BYTES
|
|
1067
|
+
totalHotBufferBytes: queueBytes * 2 + hitBytes + accumulationBytes + pathVertexBytes + sceneObjectBytes + triangleBytes + bvhNodeBytes + bvhLeafReferenceBytes + emissiveTriangleMetadataBytes + environmentPortalBytes + CONFIG_BUFFER_BYTES + COUNTER_BUFFER_BYTES + INDIRECT_DISPATCH_ARGS_BYTES
|
|
1023
1068
|
});
|
|
1024
1069
|
}
|
|
1025
1070
|
function createWavefrontPathTracingComputeConfig(options = {}) {
|
|
@@ -1104,6 +1149,10 @@ function createWavefrontPathTracingComputeConfig(options = {}) {
|
|
|
1104
1149
|
options.environmentPortalMode ?? options.portalMode ?? options.environmentLighting?.environmentPortalMode,
|
|
1105
1150
|
environmentPortals.length > 0
|
|
1106
1151
|
);
|
|
1152
|
+
const environmentMap = resolveEnvironmentMap(
|
|
1153
|
+
options.environmentMap ?? options.environmentTexture ?? options.environmentLighting?.environmentMap
|
|
1154
|
+
);
|
|
1155
|
+
const deferredPathResolve = resolveDeferredPathResolve(options);
|
|
1107
1156
|
return Object.freeze({
|
|
1108
1157
|
mode: rendererWavefrontComputeMode,
|
|
1109
1158
|
width,
|
|
@@ -1138,12 +1187,15 @@ function createWavefrontPathTracingComputeConfig(options = {}) {
|
|
|
1138
1187
|
environmentPortalCount: environmentPortals.length,
|
|
1139
1188
|
environmentPortalCapacity,
|
|
1140
1189
|
environmentPortalMode,
|
|
1190
|
+
environmentMap,
|
|
1191
|
+
deferredPathResolve,
|
|
1141
1192
|
displayQuality: options.displayQuality === true,
|
|
1142
1193
|
requiresMeshBvhForDisplayQuality: true,
|
|
1143
1194
|
denoise: options.denoise !== false,
|
|
1144
1195
|
frameIndex: readNonNegativeInteger("frameIndex", options.frameIndex, 0),
|
|
1145
1196
|
memory: estimateWavefrontPathTracingMemory({
|
|
1146
1197
|
tilePixelCapacity,
|
|
1198
|
+
maxDepth,
|
|
1147
1199
|
sceneObjectCapacity,
|
|
1148
1200
|
triangleCapacity,
|
|
1149
1201
|
bvhNodeCapacity,
|
|
@@ -1340,6 +1392,18 @@ function createConfigPayload(config, tile, frameIndex, buildRange = {}) {
|
|
|
1340
1392
|
data.setUint32(260, config.environmentPortalMode ?? 0, true);
|
|
1341
1393
|
data.setUint32(264, 0, true);
|
|
1342
1394
|
data.setUint32(268, 0, true);
|
|
1395
|
+
writeVec4(floatView, 272, [
|
|
1396
|
+
config.environmentMap.enabled ? 1 : 0,
|
|
1397
|
+
config.environmentMap.intensity,
|
|
1398
|
+
config.environmentMap.rotationRadians,
|
|
1399
|
+
config.environmentMap.ambientStrength
|
|
1400
|
+
]);
|
|
1401
|
+
writeVec4(floatView, 288, [
|
|
1402
|
+
config.deferredPathResolve ? 1 : 0,
|
|
1403
|
+
config.environmentLighting.sunlitBaseline,
|
|
1404
|
+
0,
|
|
1405
|
+
0
|
|
1406
|
+
]);
|
|
1343
1407
|
return bytes;
|
|
1344
1408
|
}
|
|
1345
1409
|
function createTiles(width, height, tileSize) {
|
|
@@ -1585,6 +1649,136 @@ function alignTo(value, alignment) {
|
|
|
1585
1649
|
const resolvedAlignment = Math.max(1, alignment);
|
|
1586
1650
|
return Math.ceil(value / resolvedAlignment) * resolvedAlignment;
|
|
1587
1651
|
}
|
|
1652
|
+
function float32ToFloat16Bits(value) {
|
|
1653
|
+
const floatView = new Float32Array(1);
|
|
1654
|
+
const intView = new Uint32Array(floatView.buffer);
|
|
1655
|
+
floatView[0] = Number.isFinite(value) ? value : 0;
|
|
1656
|
+
const x = intView[0];
|
|
1657
|
+
const sign = x >> 16 & 32768;
|
|
1658
|
+
let mantissa = x & 8388607;
|
|
1659
|
+
let exponent = x >> 23 & 255;
|
|
1660
|
+
if (exponent === 255) {
|
|
1661
|
+
return sign | (mantissa ? 32256 : 31744);
|
|
1662
|
+
}
|
|
1663
|
+
exponent = exponent - 127 + 15;
|
|
1664
|
+
if (exponent >= 31) {
|
|
1665
|
+
return sign | 31744;
|
|
1666
|
+
}
|
|
1667
|
+
if (exponent <= 0) {
|
|
1668
|
+
if (exponent < -10) {
|
|
1669
|
+
return sign;
|
|
1670
|
+
}
|
|
1671
|
+
mantissa = (mantissa | 8388608) >> 1 - exponent;
|
|
1672
|
+
return sign | mantissa + 4096 >> 13;
|
|
1673
|
+
}
|
|
1674
|
+
return sign | exponent << 10 | mantissa + 4096 >> 13;
|
|
1675
|
+
}
|
|
1676
|
+
function environmentMapIntegerScale(data) {
|
|
1677
|
+
if (data instanceof Uint8Array) {
|
|
1678
|
+
return 1 / 255;
|
|
1679
|
+
}
|
|
1680
|
+
if (data instanceof Uint16Array) {
|
|
1681
|
+
return 1 / 65535;
|
|
1682
|
+
}
|
|
1683
|
+
return 1;
|
|
1684
|
+
}
|
|
1685
|
+
function readEnvironmentMapComponent(data, index, fallback, integerScale = 1) {
|
|
1686
|
+
if (!data || index >= data.length) {
|
|
1687
|
+
return fallback;
|
|
1688
|
+
}
|
|
1689
|
+
const value = Number(data[index]);
|
|
1690
|
+
return Number.isFinite(value) ? Math.max(0, value) * integerScale : fallback;
|
|
1691
|
+
}
|
|
1692
|
+
function createEnvironmentMapUploadBytes(environmentMap, fallbackColor) {
|
|
1693
|
+
const width = Math.max(1, environmentMap.width);
|
|
1694
|
+
const height = Math.max(1, environmentMap.height);
|
|
1695
|
+
const rowBytes = width * 8;
|
|
1696
|
+
const bytesPerRow = alignTo(rowBytes, 256);
|
|
1697
|
+
const bytes = new Uint8Array(bytesPerRow * height);
|
|
1698
|
+
const data = environmentMap.data;
|
|
1699
|
+
const integerScale = environmentMapIntegerScale(data);
|
|
1700
|
+
const view = new DataView(bytes.buffer);
|
|
1701
|
+
const writeComponent = (targetOffset, sourceOffset, fallback) => {
|
|
1702
|
+
view.setUint16(
|
|
1703
|
+
targetOffset,
|
|
1704
|
+
float32ToFloat16Bits(
|
|
1705
|
+
readEnvironmentMapComponent(data, sourceOffset, fallback, integerScale)
|
|
1706
|
+
),
|
|
1707
|
+
true
|
|
1708
|
+
);
|
|
1709
|
+
};
|
|
1710
|
+
for (let y = 0; y < height; y += 1) {
|
|
1711
|
+
for (let x = 0; x < width; x += 1) {
|
|
1712
|
+
const sourceOffset = (y * width + x) * 4;
|
|
1713
|
+
const targetOffset = y * bytesPerRow + x * 8;
|
|
1714
|
+
writeComponent(targetOffset, sourceOffset, fallbackColor[0]);
|
|
1715
|
+
writeComponent(targetOffset + 2, sourceOffset + 1, fallbackColor[1]);
|
|
1716
|
+
writeComponent(targetOffset + 4, sourceOffset + 2, fallbackColor[2]);
|
|
1717
|
+
writeComponent(targetOffset + 6, sourceOffset + 3, fallbackColor[3] ?? 1);
|
|
1718
|
+
}
|
|
1719
|
+
}
|
|
1720
|
+
return Object.freeze({
|
|
1721
|
+
bytes,
|
|
1722
|
+
bytesPerRow,
|
|
1723
|
+
width,
|
|
1724
|
+
height
|
|
1725
|
+
});
|
|
1726
|
+
}
|
|
1727
|
+
function createEnvironmentMapResource(device, constants, environmentMap, fallbackColor) {
|
|
1728
|
+
if (environmentMap.view) {
|
|
1729
|
+
return Object.freeze({
|
|
1730
|
+
view: environmentMap.view,
|
|
1731
|
+
sampler: environmentMap.sampler ?? device.createSampler({
|
|
1732
|
+
label: "plasius.wavefront.environmentMapSampler",
|
|
1733
|
+
addressModeU: "repeat",
|
|
1734
|
+
addressModeV: "clamp-to-edge",
|
|
1735
|
+
magFilter: "linear",
|
|
1736
|
+
minFilter: "linear"
|
|
1737
|
+
}),
|
|
1738
|
+
texture: null,
|
|
1739
|
+
ownsTexture: false
|
|
1740
|
+
});
|
|
1741
|
+
}
|
|
1742
|
+
if (environmentMap.texture && typeof environmentMap.texture.createView === "function") {
|
|
1743
|
+
return Object.freeze({
|
|
1744
|
+
view: environmentMap.texture.createView(),
|
|
1745
|
+
sampler: environmentMap.sampler ?? device.createSampler({
|
|
1746
|
+
label: "plasius.wavefront.environmentMapSampler",
|
|
1747
|
+
addressModeU: "repeat",
|
|
1748
|
+
addressModeV: "clamp-to-edge",
|
|
1749
|
+
magFilter: "linear",
|
|
1750
|
+
minFilter: "linear"
|
|
1751
|
+
}),
|
|
1752
|
+
texture: environmentMap.texture,
|
|
1753
|
+
ownsTexture: false
|
|
1754
|
+
});
|
|
1755
|
+
}
|
|
1756
|
+
const upload = createEnvironmentMapUploadBytes(environmentMap, fallbackColor);
|
|
1757
|
+
const texture = device.createTexture({
|
|
1758
|
+
label: environmentMap.enabled ? "plasius.wavefront.environmentMap" : "plasius.wavefront.environmentMapFallback",
|
|
1759
|
+
size: { width: upload.width, height: upload.height },
|
|
1760
|
+
format: "rgba16float",
|
|
1761
|
+
usage: constants.texture.TEXTURE_BINDING | constants.texture.COPY_DST
|
|
1762
|
+
});
|
|
1763
|
+
device.queue.writeTexture(
|
|
1764
|
+
{ texture },
|
|
1765
|
+
upload.bytes,
|
|
1766
|
+
{ bytesPerRow: upload.bytesPerRow, rowsPerImage: upload.height },
|
|
1767
|
+
{ width: upload.width, height: upload.height, depthOrArrayLayers: 1 }
|
|
1768
|
+
);
|
|
1769
|
+
return Object.freeze({
|
|
1770
|
+
view: texture.createView(),
|
|
1771
|
+
sampler: environmentMap.sampler ?? device.createSampler({
|
|
1772
|
+
label: "plasius.wavefront.environmentMapSampler",
|
|
1773
|
+
addressModeU: "repeat",
|
|
1774
|
+
addressModeV: "clamp-to-edge",
|
|
1775
|
+
magFilter: "linear",
|
|
1776
|
+
minFilter: "linear"
|
|
1777
|
+
}),
|
|
1778
|
+
texture,
|
|
1779
|
+
ownsTexture: true
|
|
1780
|
+
});
|
|
1781
|
+
}
|
|
1588
1782
|
async function getPipelineDiagnostics(shaderModule) {
|
|
1589
1783
|
if (typeof shaderModule?.compilationInfo !== "function") {
|
|
1590
1784
|
return "";
|
|
@@ -1793,6 +1987,8 @@ struct FrameConfig {
|
|
|
1793
1987
|
environmentPortalMode: u32,
|
|
1794
1988
|
_portalPad0: u32,
|
|
1795
1989
|
_portalPad1: u32,
|
|
1990
|
+
environmentMapSettings: vec4<f32>,
|
|
1991
|
+
pathResolveSettings: vec4<f32>,
|
|
1796
1992
|
};
|
|
1797
1993
|
|
|
1798
1994
|
struct Counters {
|
|
@@ -1852,6 +2048,9 @@ struct EnvironmentPortal {
|
|
|
1852
2048
|
@group(0) @binding(17) var finalDenoiseInputRadiance: texture_2d<f32>;
|
|
1853
2049
|
@group(0) @binding(18) var denoisedOutputImage: texture_storage_2d<rgba8unorm, write>;
|
|
1854
2050
|
@group(0) @binding(19) var<storage, read> environmentPortals: array<EnvironmentPortal>;
|
|
2051
|
+
@group(0) @binding(20) var environmentMapTexture: texture_2d<f32>;
|
|
2052
|
+
@group(0) @binding(21) var environmentMapSampler: sampler;
|
|
2053
|
+
@group(0) @binding(22) var<storage, read_write> pathVertices: array<vec4<f32>>;
|
|
1855
2054
|
|
|
1856
2055
|
fn hash_u32(value: u32) -> u32 {
|
|
1857
2056
|
var x = value;
|
|
@@ -1896,6 +2095,89 @@ fn max_component(value: vec3<f32>) -> f32 {
|
|
|
1896
2095
|
return max(max(value.x, value.y), value.z);
|
|
1897
2096
|
}
|
|
1898
2097
|
|
|
2098
|
+
fn environment_map_enabled() -> bool {
|
|
2099
|
+
return config.environmentMapSettings.x > 0.5;
|
|
2100
|
+
}
|
|
2101
|
+
|
|
2102
|
+
fn deferred_path_resolve_enabled() -> bool {
|
|
2103
|
+
return config.pathResolveSettings.x > 0.5;
|
|
2104
|
+
}
|
|
2105
|
+
|
|
2106
|
+
fn path_vertex_count_per_ray() -> u32 {
|
|
2107
|
+
return config.maxDepth + 1u;
|
|
2108
|
+
}
|
|
2109
|
+
|
|
2110
|
+
fn path_vertex_index(rayId: u32, depth: u32) -> u32 {
|
|
2111
|
+
return rayId * path_vertex_count_per_ray() + min(depth, config.maxDepth);
|
|
2112
|
+
}
|
|
2113
|
+
|
|
2114
|
+
fn clear_deferred_path(rayId: u32) {
|
|
2115
|
+
if (!deferred_path_resolve_enabled()) {
|
|
2116
|
+
return;
|
|
2117
|
+
}
|
|
2118
|
+
|
|
2119
|
+
for (var depth = 0u; depth <= config.maxDepth; depth = depth + 1u) {
|
|
2120
|
+
pathVertices[path_vertex_index(rayId, depth)] = vec4<f32>(0.0);
|
|
2121
|
+
if (depth == config.maxDepth) {
|
|
2122
|
+
break;
|
|
2123
|
+
}
|
|
2124
|
+
}
|
|
2125
|
+
}
|
|
2126
|
+
|
|
2127
|
+
fn record_deferred_path_response(ray: RayRecord, response: vec3<f32>) {
|
|
2128
|
+
if (!deferred_path_resolve_enabled() || ray.rayId >= config.tilePixelCount || ray.bounce >= config.maxDepth) {
|
|
2129
|
+
return;
|
|
2130
|
+
}
|
|
2131
|
+
pathVertices[path_vertex_index(ray.rayId, ray.bounce)] =
|
|
2132
|
+
vec4<f32>(max(response, vec3<f32>(0.0)), 1.0);
|
|
2133
|
+
}
|
|
2134
|
+
|
|
2135
|
+
fn record_deferred_terminal_source(ray: RayRecord, sourceRadiance: vec3<f32>) {
|
|
2136
|
+
if (!deferred_path_resolve_enabled() || ray.rayId >= config.tilePixelCount) {
|
|
2137
|
+
return;
|
|
2138
|
+
}
|
|
2139
|
+
pathVertices[path_vertex_index(ray.rayId, config.maxDepth)] =
|
|
2140
|
+
vec4<f32>(clamp_sample_radiance(sourceRadiance), 1.0);
|
|
2141
|
+
}
|
|
2142
|
+
|
|
2143
|
+
fn environment_map_uv(direction: vec3<f32>) -> vec2<f32> {
|
|
2144
|
+
let rayDirection = safe_normalize(direction, vec3<f32>(0.0, 1.0, 0.0));
|
|
2145
|
+
let rotationTurns = config.environmentMapSettings.z / 6.28318530718;
|
|
2146
|
+
let u = fract(atan2(rayDirection.z, rayDirection.x) / 6.28318530718 + 0.5 + rotationTurns);
|
|
2147
|
+
let v = acos(clamp(rayDirection.y, -1.0, 1.0)) / 3.14159265359;
|
|
2148
|
+
return vec2<f32>(u, clamp(v, 0.0, 1.0));
|
|
2149
|
+
}
|
|
2150
|
+
|
|
2151
|
+
fn environment_map_radiance(direction: vec3<f32>) -> vec3<f32> {
|
|
2152
|
+
let uv = environment_map_uv(direction);
|
|
2153
|
+
let texel = max(textureSampleLevel(environmentMapTexture, environmentMapSampler, uv, 0.0).rgb, vec3<f32>(0.0));
|
|
2154
|
+
return texel * max(config.environmentMapSettings.y, 0.0);
|
|
2155
|
+
}
|
|
2156
|
+
|
|
2157
|
+
fn procedural_environment_radiance(direction: vec3<f32>) -> vec3<f32> {
|
|
2158
|
+
let rayDirection = safe_normalize(direction, vec3<f32>(0.0, 1.0, 0.0));
|
|
2159
|
+
let upFactor = saturate(rayDirection.y * 0.5 + 0.5);
|
|
2160
|
+
let sunDirection = safe_normalize(
|
|
2161
|
+
config.environmentSunDirectionIntensity.xyz,
|
|
2162
|
+
vec3<f32>(0.0, 1.0, 0.0)
|
|
2163
|
+
);
|
|
2164
|
+
let sunGlow = pow(saturate(dot(rayDirection, sunDirection)), 192.0);
|
|
2165
|
+
let gradient =
|
|
2166
|
+
config.environmentHorizonColor.xyz * (1.0 - upFactor) +
|
|
2167
|
+
config.environmentZenithColor.xyz * upFactor;
|
|
2168
|
+
return (
|
|
2169
|
+
gradient +
|
|
2170
|
+
config.environmentSunColor.xyz * sunGlow
|
|
2171
|
+
) * max(config.environmentSunDirectionIntensity.w, 0.0001);
|
|
2172
|
+
}
|
|
2173
|
+
|
|
2174
|
+
fn base_environment_radiance(direction: vec3<f32>) -> vec3<f32> {
|
|
2175
|
+
if (environment_map_enabled()) {
|
|
2176
|
+
return environment_map_radiance(direction);
|
|
2177
|
+
}
|
|
2178
|
+
return procedural_environment_radiance(direction);
|
|
2179
|
+
}
|
|
2180
|
+
|
|
1899
2181
|
fn environment_portal_radiance_scale(origin: vec3<f32>, direction: vec3<f32>) -> vec3<f32> {
|
|
1900
2182
|
if (config.environmentPortalCount == 0u || config.environmentPortalMode == 0u) {
|
|
1901
2183
|
return vec3<f32>(1.0);
|
|
@@ -1935,22 +2217,9 @@ fn environment_portal_radiance_scale(origin: vec3<f32>, direction: vec3<f32>) ->
|
|
|
1935
2217
|
|
|
1936
2218
|
fn environment_radiance(origin: vec3<f32>, direction: vec3<f32>) -> vec3<f32> {
|
|
1937
2219
|
let rayDirection = safe_normalize(direction, vec3<f32>(0.0, 1.0, 0.0));
|
|
1938
|
-
let upFactor = saturate(rayDirection.y * 0.5 + 0.5);
|
|
1939
|
-
let sunDirection = safe_normalize(
|
|
1940
|
-
config.environmentSunDirectionIntensity.xyz,
|
|
1941
|
-
vec3<f32>(0.0, 1.0, 0.0)
|
|
1942
|
-
);
|
|
1943
|
-
let sunGlow = pow(saturate(dot(rayDirection, sunDirection)), 192.0);
|
|
1944
|
-
let gradient =
|
|
1945
|
-
config.environmentHorizonColor.xyz * (1.0 - upFactor) +
|
|
1946
|
-
config.environmentZenithColor.xyz * upFactor;
|
|
1947
2220
|
let portalScale = environment_portal_radiance_scale(origin, rayDirection);
|
|
1948
2221
|
let portalHit = max_component(portalScale) > 0.0001;
|
|
1949
|
-
return (
|
|
1950
|
-
gradient +
|
|
1951
|
-
config.environmentSunColor.xyz * sunGlow
|
|
1952
|
-
) *
|
|
1953
|
-
max(config.environmentSunDirectionIntensity.w, 0.0001) *
|
|
2222
|
+
return base_environment_radiance(rayDirection) *
|
|
1954
2223
|
select(vec3<f32>(1.0), portalScale, portalHit);
|
|
1955
2224
|
}
|
|
1956
2225
|
|
|
@@ -1966,16 +2235,59 @@ fn gated_environment_radiance(origin: vec3<f32>, direction: vec3<f32>) -> vec3<f
|
|
|
1966
2235
|
return environment_radiance(origin, direction);
|
|
1967
2236
|
}
|
|
1968
2237
|
|
|
1969
|
-
fn
|
|
2238
|
+
fn surface_path_response(hit: HitRecord) -> vec3<f32> {
|
|
2239
|
+
let color = clamp(hit.color.xyz, vec3<f32>(0.0), vec3<f32>(1.0));
|
|
2240
|
+
let opacity = clamp(hit.material.z, 0.0, 1.0);
|
|
2241
|
+
let materialEnergy = select(0.68, 0.92, hit.materialKind == 1u || hit.materialKind == 2u);
|
|
2242
|
+
let transparentEnergy = select(materialEnergy, 0.9, hit.hitType == 3u);
|
|
2243
|
+
return mix(vec3<f32>(1.0), color, max(opacity, 0.18)) * transparentEnergy;
|
|
2244
|
+
}
|
|
2245
|
+
|
|
2246
|
+
fn sunlit_baseline_radiance(normal: vec3<f32>) -> vec3<f32> {
|
|
2247
|
+
let baseline = max(config.pathResolveSettings.y, 0.0);
|
|
2248
|
+
if (baseline <= 0.000001) {
|
|
2249
|
+
return vec3<f32>(0.0);
|
|
2250
|
+
}
|
|
2251
|
+
let sunDirection = safe_normalize(
|
|
2252
|
+
config.environmentSunDirectionIntensity.xyz,
|
|
2253
|
+
vec3<f32>(0.0, 1.0, 0.0)
|
|
2254
|
+
);
|
|
2255
|
+
let sunFacing = saturate(dot(normal, sunDirection));
|
|
2256
|
+
let skyFacing = 0.35 + saturate(normal.y * 0.5 + 0.5) * 0.65;
|
|
2257
|
+
let directionalWeight = 0.38 + sunFacing * 0.62;
|
|
2258
|
+
let sunTint = max(config.environmentSunColor.xyz, vec3<f32>(0.0));
|
|
2259
|
+
return clamp_sample_radiance(sunTint * baseline * skyFacing * directionalWeight * 0.04);
|
|
2260
|
+
}
|
|
2261
|
+
|
|
2262
|
+
fn terminal_surface_environment_source(hit: HitRecord) -> vec3<f32> {
|
|
1970
2263
|
let normal = safe_normalize(hit.shadingNormal.xyz, vec3<f32>(0.0, 1.0, 0.0));
|
|
1971
|
-
let surfaceColor = max(hit.color.xyz, config.ambientColor.xyz);
|
|
1972
2264
|
let normalEnvironment = gated_environment_radiance(
|
|
1973
2265
|
hit.position.xyz + normal * 0.003,
|
|
1974
2266
|
normal
|
|
1975
2267
|
);
|
|
1976
|
-
let
|
|
2268
|
+
let sunlitFloor = sunlit_baseline_radiance(normal);
|
|
2269
|
+
let ambientFloor = select(
|
|
2270
|
+
max(config.ambientColor.xyz, sunlitFloor * 0.82),
|
|
2271
|
+
max(config.ambientColor.xyz * 0.35, sunlitFloor * 0.58),
|
|
2272
|
+
environment_map_enabled()
|
|
2273
|
+
);
|
|
2274
|
+
let environmentInfluence = select(
|
|
2275
|
+
max(0.12, config.pathResolveSettings.y * 0.42),
|
|
2276
|
+
max(config.environmentMapSettings.w, max(0.12, config.pathResolveSettings.y * 0.42)),
|
|
2277
|
+
environment_map_enabled()
|
|
2278
|
+
);
|
|
2279
|
+
let environmentFloor = max(ambientFloor, max(sunlitFloor, normalEnvironment * environmentInfluence));
|
|
1977
2280
|
let materialFloor = select(0.7, 1.0, hit.materialKind == 0u || hit.materialKind == 3u);
|
|
1978
|
-
return clamp_sample_radiance(
|
|
2281
|
+
return clamp_sample_radiance(environmentFloor * materialFloor);
|
|
2282
|
+
}
|
|
2283
|
+
|
|
2284
|
+
fn terminal_surface_environment_contribution(ray: RayRecord, hit: HitRecord) -> vec3<f32> {
|
|
2285
|
+
let surfaceColor = max(hit.color.xyz, config.ambientColor.xyz);
|
|
2286
|
+
return clamp_sample_radiance(
|
|
2287
|
+
ray.throughput.xyz *
|
|
2288
|
+
surfaceColor *
|
|
2289
|
+
terminal_surface_environment_source(hit)
|
|
2290
|
+
);
|
|
1979
2291
|
}
|
|
1980
2292
|
|
|
1981
2293
|
fn direct_environment_portal_irradiance(origin: vec3<f32>, normal: vec3<f32>) -> vec3<f32> {
|
|
@@ -2028,7 +2340,17 @@ fn surface_direct_environment_contribution(ray: RayRecord, hit: HitRecord) -> ve
|
|
|
2028
2340
|
|
|
2029
2341
|
let normalEnvironment = gated_environment_radiance(origin, normal);
|
|
2030
2342
|
let skyVisibility = 0.35 + saturate(normal.y * 0.5 + 0.5) * 0.45;
|
|
2031
|
-
let
|
|
2343
|
+
let sunlitFloor = sunlit_baseline_radiance(normal);
|
|
2344
|
+
let ambientIrradiance = max(
|
|
2345
|
+
select(config.ambientColor.xyz * 0.72, config.ambientColor.xyz * 0.28, environment_map_enabled()),
|
|
2346
|
+
sunlitFloor * select(0.72, 0.45, environment_map_enabled())
|
|
2347
|
+
);
|
|
2348
|
+
let environmentIrradianceScale = select(
|
|
2349
|
+
max(0.16, config.pathResolveSettings.y * 0.45),
|
|
2350
|
+
max(config.environmentMapSettings.w, max(0.16, config.pathResolveSettings.y * 0.45)),
|
|
2351
|
+
environment_map_enabled()
|
|
2352
|
+
);
|
|
2353
|
+
let skyIrradiance = max(ambientIrradiance, normalEnvironment * skyVisibility * environmentIrradianceScale);
|
|
2032
2354
|
|
|
2033
2355
|
let sunDirection = safe_normalize(
|
|
2034
2356
|
config.environmentSunDirectionIntensity.xyz,
|
|
@@ -2652,6 +2974,7 @@ fn generatePrimaryRays(@builtin(global_invocation_id) globalId: vec3<u32>) {
|
|
|
2652
2974
|
return;
|
|
2653
2975
|
}
|
|
2654
2976
|
activeQueue[index] = make_ray(index);
|
|
2977
|
+
clear_deferred_path(index);
|
|
2655
2978
|
if (u32(config.projectionAndSampling.w) == 0u) {
|
|
2656
2979
|
accumulation[index] = vec4<f32>(0.0);
|
|
2657
2980
|
}
|
|
@@ -2904,25 +3227,37 @@ fn resolveSurfaceRecords(@builtin(global_invocation_id) globalId: vec3<u32>) {
|
|
|
2904
3227
|
|
|
2905
3228
|
if (hit.hitType == 1u) {
|
|
2906
3229
|
let guidedLightWeight = select(1.0, 0.24, (ray.flags & RAY_FLAG_GUIDED_EMISSIVE) != 0u);
|
|
2907
|
-
|
|
2908
|
-
|
|
2909
|
-
|
|
2910
|
-
|
|
2911
|
-
|
|
3230
|
+
let sourceRadiance = max(hit.emission.xyz, hit.color.xyz) * guidedLightWeight;
|
|
3231
|
+
if (deferred_path_resolve_enabled()) {
|
|
3232
|
+
record_deferred_terminal_source(ray, sourceRadiance);
|
|
3233
|
+
} else {
|
|
3234
|
+
contribution = clamp_sample_radiance(ray.throughput.xyz * sourceRadiance);
|
|
3235
|
+
accumulation[ray.rayId] =
|
|
3236
|
+
accumulation[ray.rayId] + vec4<f32>(contribution * sample_weight(), 1.0);
|
|
3237
|
+
}
|
|
2912
3238
|
atomicAdd(&counters.terminatedCount, 1u);
|
|
2913
3239
|
return;
|
|
2914
3240
|
}
|
|
2915
3241
|
|
|
2916
3242
|
if (hit.hitType == 2u) {
|
|
2917
|
-
|
|
2918
|
-
|
|
2919
|
-
|
|
3243
|
+
if (deferred_path_resolve_enabled()) {
|
|
3244
|
+
record_deferred_terminal_source(ray, hit.color.xyz);
|
|
3245
|
+
} else {
|
|
3246
|
+
contribution = clamp_sample_radiance(ray.throughput.xyz * max(hit.color.xyz, config.ambientColor.xyz));
|
|
3247
|
+
accumulation[ray.rayId] =
|
|
3248
|
+
accumulation[ray.rayId] + vec4<f32>(contribution * sample_weight(), 1.0);
|
|
3249
|
+
}
|
|
2920
3250
|
atomicAdd(&counters.terminatedCount, 1u);
|
|
2921
3251
|
return;
|
|
2922
3252
|
}
|
|
2923
3253
|
|
|
3254
|
+
let response = surface_path_response(hit);
|
|
3255
|
+
record_deferred_path_response(ray, response);
|
|
3256
|
+
|
|
2924
3257
|
let shouldEstimateDirectEnvironment =
|
|
2925
|
-
(
|
|
3258
|
+
!deferred_path_resolve_enabled() &&
|
|
3259
|
+
(hit.materialKind == 0u || hit.materialKind == 1u) &&
|
|
3260
|
+
hit.material.z >= 0.95;
|
|
2926
3261
|
if (shouldEstimateDirectEnvironment) {
|
|
2927
3262
|
let directEnvironment = surface_direct_environment_contribution(ray, hit);
|
|
2928
3263
|
accumulation[ray.rayId] =
|
|
@@ -2930,9 +3265,13 @@ fn resolveSurfaceRecords(@builtin(global_invocation_id) globalId: vec3<u32>) {
|
|
|
2930
3265
|
}
|
|
2931
3266
|
|
|
2932
3267
|
if (ray.bounce + 1u >= config.maxDepth) {
|
|
2933
|
-
|
|
2934
|
-
|
|
2935
|
-
|
|
3268
|
+
if (deferred_path_resolve_enabled()) {
|
|
3269
|
+
record_deferred_terminal_source(ray, terminal_surface_environment_source(hit));
|
|
3270
|
+
} else {
|
|
3271
|
+
let terminalEnvironment = terminal_surface_environment_contribution(ray, hit);
|
|
3272
|
+
accumulation[ray.rayId] =
|
|
3273
|
+
accumulation[ray.rayId] + vec4<f32>(terminalEnvironment * sample_weight(), 1.0);
|
|
3274
|
+
}
|
|
2936
3275
|
atomicAdd(&counters.terminatedCount, 1u);
|
|
2937
3276
|
return;
|
|
2938
3277
|
}
|
|
@@ -2941,17 +3280,17 @@ fn resolveSurfaceRecords(@builtin(global_invocation_id) globalId: vec3<u32>) {
|
|
|
2941
3280
|
let scatter = scatter_direction(ray, hit, seed);
|
|
2942
3281
|
let nextIndex = atomicAdd(&counters.nextCount, 1u);
|
|
2943
3282
|
if (nextIndex >= config.tilePixelCount) {
|
|
2944
|
-
|
|
2945
|
-
|
|
2946
|
-
|
|
3283
|
+
if (deferred_path_resolve_enabled()) {
|
|
3284
|
+
record_deferred_terminal_source(ray, terminal_surface_environment_source(hit));
|
|
3285
|
+
} else {
|
|
3286
|
+
let overflowEnvironment = terminal_surface_environment_contribution(ray, hit);
|
|
3287
|
+
accumulation[ray.rayId] =
|
|
3288
|
+
accumulation[ray.rayId] + vec4<f32>(overflowEnvironment * sample_weight(), 1.0);
|
|
3289
|
+
}
|
|
2947
3290
|
atomicAdd(&counters.terminatedCount, 1u);
|
|
2948
3291
|
return;
|
|
2949
3292
|
}
|
|
2950
|
-
let
|
|
2951
|
-
let opacity = clamp(hit.material.z, 0.0, 1.0);
|
|
2952
|
-
let materialEnergy = select(0.68, 0.92, hit.materialKind == 1u || hit.materialKind == 2u);
|
|
2953
|
-
let transparentEnergy = select(materialEnergy, 0.9, hit.hitType == 3u);
|
|
2954
|
-
let throughput = ray.throughput.xyz * mix(vec3<f32>(1.0), color, max(opacity, 0.18)) * transparentEnergy;
|
|
3293
|
+
let throughput = ray.throughput.xyz * response;
|
|
2955
3294
|
nextQueue[nextIndex] = RayRecord(
|
|
2956
3295
|
ray.rayId,
|
|
2957
3296
|
ray.rayId,
|
|
@@ -2979,6 +3318,27 @@ fn compactAndSwapQueues(@builtin(global_invocation_id) globalId: vec3<u32>) {
|
|
|
2979
3318
|
write_active_dispatch_args(activeCount);
|
|
2980
3319
|
}
|
|
2981
3320
|
|
|
3321
|
+
fn resolve_deferred_path_radiance(rayId: u32) -> vec3<f32> {
|
|
3322
|
+
let terminal = pathVertices[path_vertex_index(rayId, config.maxDepth)];
|
|
3323
|
+
if (terminal.w <= 0.0) {
|
|
3324
|
+
return vec3<f32>(0.0);
|
|
3325
|
+
}
|
|
3326
|
+
|
|
3327
|
+
var radiance = terminal.xyz;
|
|
3328
|
+
var depth = config.maxDepth;
|
|
3329
|
+
loop {
|
|
3330
|
+
if (depth == 0u) {
|
|
3331
|
+
break;
|
|
3332
|
+
}
|
|
3333
|
+
depth = depth - 1u;
|
|
3334
|
+
let response = pathVertices[path_vertex_index(rayId, depth)];
|
|
3335
|
+
if (response.w > 0.0) {
|
|
3336
|
+
radiance = radiance * response.xyz;
|
|
3337
|
+
}
|
|
3338
|
+
}
|
|
3339
|
+
return clamp_sample_radiance(radiance);
|
|
3340
|
+
}
|
|
3341
|
+
|
|
2982
3342
|
@compute @workgroup_size(64)
|
|
2983
3343
|
fn accumulateTerminalRadiance(@builtin(global_invocation_id) globalId: vec3<u32>) {
|
|
2984
3344
|
let index = globalId.x;
|
|
@@ -2988,7 +3348,12 @@ fn accumulateTerminalRadiance(@builtin(global_invocation_id) globalId: vec3<u32>
|
|
|
2988
3348
|
let localX = index % config.tileWidth;
|
|
2989
3349
|
let localY = index / config.tileWidth;
|
|
2990
3350
|
let pixel = vec2<i32>(i32(config.tileX + localX), i32(config.tileY + localY));
|
|
2991
|
-
|
|
3351
|
+
var radiance = max(accumulation[index].xyz, vec3<f32>(0.0));
|
|
3352
|
+
if (deferred_path_resolve_enabled()) {
|
|
3353
|
+
let resolved = resolve_deferred_path_radiance(index) * sample_weight();
|
|
3354
|
+
radiance = clamp_sample_radiance(radiance + resolved);
|
|
3355
|
+
accumulation[index] = vec4<f32>(radiance, 1.0);
|
|
3356
|
+
}
|
|
2992
3357
|
|
|
2993
3358
|
textureStore(radianceImage, pixel, vec4<f32>(radiance, 1.0));
|
|
2994
3359
|
if (config.denoise == 0u) {
|
|
@@ -3126,6 +3491,125 @@ function createWavefrontDeviceDescriptor(adapter, options = {}) {
|
|
|
3126
3491
|
}
|
|
3127
3492
|
return Object.keys(descriptor).length > 0 ? descriptor : void 0;
|
|
3128
3493
|
}
|
|
3494
|
+
function readGpuLimit(adapter, device, name) {
|
|
3495
|
+
const adapterValue = Number(adapter?.limits?.[name]);
|
|
3496
|
+
if (Number.isFinite(adapterValue)) {
|
|
3497
|
+
return adapterValue;
|
|
3498
|
+
}
|
|
3499
|
+
const deviceValue = Number(device?.limits?.[name]);
|
|
3500
|
+
return Number.isFinite(deviceValue) ? deviceValue : null;
|
|
3501
|
+
}
|
|
3502
|
+
function createAdapterInfoSnapshot(adapter) {
|
|
3503
|
+
const info = adapter?.info;
|
|
3504
|
+
if (!info || typeof info !== "object") {
|
|
3505
|
+
return null;
|
|
3506
|
+
}
|
|
3507
|
+
return Object.freeze({
|
|
3508
|
+
vendor: typeof info.vendor === "string" ? info.vendor : "",
|
|
3509
|
+
architecture: typeof info.architecture === "string" ? info.architecture : "",
|
|
3510
|
+
device: typeof info.device === "string" ? info.device : "",
|
|
3511
|
+
description: typeof info.description === "string" ? info.description : ""
|
|
3512
|
+
});
|
|
3513
|
+
}
|
|
3514
|
+
function createGpuAdapterParallelismDiagnostics(adapter, device) {
|
|
3515
|
+
return Object.freeze({
|
|
3516
|
+
physicalCoreCount: null,
|
|
3517
|
+
physicalCoreCountAvailable: false,
|
|
3518
|
+
physicalCoreCountUnavailableReason: "WebGPU does not expose physical GPU core counts.",
|
|
3519
|
+
adapterInfo: createAdapterInfoSnapshot(adapter),
|
|
3520
|
+
adapterLimits: Object.freeze({
|
|
3521
|
+
maxComputeInvocationsPerWorkgroup: readGpuLimit(adapter, device, "maxComputeInvocationsPerWorkgroup"),
|
|
3522
|
+
maxComputeWorkgroupSizeX: readGpuLimit(adapter, device, "maxComputeWorkgroupSizeX"),
|
|
3523
|
+
maxComputeWorkgroupSizeY: readGpuLimit(adapter, device, "maxComputeWorkgroupSizeY"),
|
|
3524
|
+
maxComputeWorkgroupSizeZ: readGpuLimit(adapter, device, "maxComputeWorkgroupSizeZ"),
|
|
3525
|
+
maxComputeWorkgroupsPerDimension: readGpuLimit(adapter, device, "maxComputeWorkgroupsPerDimension"),
|
|
3526
|
+
maxStorageBuffersPerShaderStage: readGpuLimit(adapter, device, "maxStorageBuffersPerShaderStage"),
|
|
3527
|
+
maxStorageBufferBindingSize: readGpuLimit(adapter, device, "maxStorageBufferBindingSize")
|
|
3528
|
+
}),
|
|
3529
|
+
configuredWorkgroupSize: WORKGROUP_SIZE
|
|
3530
|
+
});
|
|
3531
|
+
}
|
|
3532
|
+
function createGpuParallelismCounters() {
|
|
3533
|
+
return {
|
|
3534
|
+
directDispatches: 0,
|
|
3535
|
+
directWorkgroups: 0,
|
|
3536
|
+
directShaderInvocations: 0,
|
|
3537
|
+
multiWorkgroupDispatches: 0,
|
|
3538
|
+
largestDirectWorkgroupsPerDispatch: 0,
|
|
3539
|
+
indirectDispatches: 0,
|
|
3540
|
+
estimatedIndirectWorkgroupsUpperBound: 0,
|
|
3541
|
+
estimatedIndirectShaderInvocationsUpperBound: 0,
|
|
3542
|
+
indirectDispatchesWithMultiWorkgroupCapacity: 0,
|
|
3543
|
+
largestEstimatedIndirectWorkgroupsPerDispatch: 0
|
|
3544
|
+
};
|
|
3545
|
+
}
|
|
3546
|
+
function countDispatchWorkgroups(groups) {
|
|
3547
|
+
return groups.reduce((product, value) => {
|
|
3548
|
+
const numeric = Number(value ?? 1);
|
|
3549
|
+
const count = Number.isFinite(numeric) ? Math.max(1, Math.trunc(numeric)) : 1;
|
|
3550
|
+
return product * count;
|
|
3551
|
+
}, 1);
|
|
3552
|
+
}
|
|
3553
|
+
function recordDirectDispatch(parallelism, groups, invocationsPerWorkgroup = WORKGROUP_SIZE) {
|
|
3554
|
+
const workgroups = countDispatchWorkgroups(groups);
|
|
3555
|
+
parallelism.directDispatches += 1;
|
|
3556
|
+
parallelism.directWorkgroups += workgroups;
|
|
3557
|
+
parallelism.directShaderInvocations += workgroups * invocationsPerWorkgroup;
|
|
3558
|
+
parallelism.largestDirectWorkgroupsPerDispatch = Math.max(
|
|
3559
|
+
parallelism.largestDirectWorkgroupsPerDispatch,
|
|
3560
|
+
workgroups
|
|
3561
|
+
);
|
|
3562
|
+
if (workgroups > 1) {
|
|
3563
|
+
parallelism.multiWorkgroupDispatches += 1;
|
|
3564
|
+
}
|
|
3565
|
+
}
|
|
3566
|
+
function recordIndirectDispatch(parallelism, estimatedWorkgroupsUpperBound, invocationsPerWorkgroup = WORKGROUP_SIZE) {
|
|
3567
|
+
const workgroups = Math.max(1, Math.trunc(Number(estimatedWorkgroupsUpperBound) || 1));
|
|
3568
|
+
parallelism.indirectDispatches += 1;
|
|
3569
|
+
parallelism.estimatedIndirectWorkgroupsUpperBound += workgroups;
|
|
3570
|
+
parallelism.estimatedIndirectShaderInvocationsUpperBound += workgroups * invocationsPerWorkgroup;
|
|
3571
|
+
parallelism.largestEstimatedIndirectWorkgroupsPerDispatch = Math.max(
|
|
3572
|
+
parallelism.largestEstimatedIndirectWorkgroupsPerDispatch,
|
|
3573
|
+
workgroups
|
|
3574
|
+
);
|
|
3575
|
+
if (workgroups > 1) {
|
|
3576
|
+
parallelism.indirectDispatchesWithMultiWorkgroupCapacity += 1;
|
|
3577
|
+
}
|
|
3578
|
+
}
|
|
3579
|
+
function createGpuParallelismDiagnostics(adapterDiagnostics, counters) {
|
|
3580
|
+
const totalEstimatedWorkgroupsUpperBound = counters.directWorkgroups + counters.estimatedIndirectWorkgroupsUpperBound;
|
|
3581
|
+
const totalEstimatedShaderInvocationsUpperBound = counters.directShaderInvocations + counters.estimatedIndirectShaderInvocationsUpperBound;
|
|
3582
|
+
const exposesMultiWorkgroupParallelism = counters.multiWorkgroupDispatches > 0 || counters.indirectDispatchesWithMultiWorkgroupCapacity > 0;
|
|
3583
|
+
return Object.freeze({
|
|
3584
|
+
...adapterDiagnostics,
|
|
3585
|
+
directDispatches: counters.directDispatches,
|
|
3586
|
+
directWorkgroups: counters.directWorkgroups,
|
|
3587
|
+
directShaderInvocations: counters.directShaderInvocations,
|
|
3588
|
+
multiWorkgroupDispatches: counters.multiWorkgroupDispatches,
|
|
3589
|
+
largestDirectWorkgroupsPerDispatch: counters.largestDirectWorkgroupsPerDispatch,
|
|
3590
|
+
indirectDispatches: counters.indirectDispatches,
|
|
3591
|
+
estimatedIndirectWorkgroupsUpperBound: counters.estimatedIndirectWorkgroupsUpperBound,
|
|
3592
|
+
estimatedIndirectShaderInvocationsUpperBound: counters.estimatedIndirectShaderInvocationsUpperBound,
|
|
3593
|
+
indirectDispatchesWithMultiWorkgroupCapacity: counters.indirectDispatchesWithMultiWorkgroupCapacity,
|
|
3594
|
+
largestEstimatedIndirectWorkgroupsPerDispatch: counters.largestEstimatedIndirectWorkgroupsPerDispatch,
|
|
3595
|
+
totalEstimatedWorkgroupsUpperBound,
|
|
3596
|
+
totalEstimatedShaderInvocationsUpperBound,
|
|
3597
|
+
exposesMultiWorkgroupParallelism,
|
|
3598
|
+
likelyUsesMoreThanOnePhysicalGpuCore: null,
|
|
3599
|
+
coreUtilizationStatus: "not-exposed-by-webgpu"
|
|
3600
|
+
});
|
|
3601
|
+
}
|
|
3602
|
+
function createEnvironmentMapSnapshot(environmentMap) {
|
|
3603
|
+
return Object.freeze({
|
|
3604
|
+
enabled: environmentMap.enabled,
|
|
3605
|
+
width: environmentMap.width,
|
|
3606
|
+
height: environmentMap.height,
|
|
3607
|
+
projection: environmentMap.projection,
|
|
3608
|
+
intensity: environmentMap.intensity,
|
|
3609
|
+
rotationRadians: environmentMap.rotationRadians,
|
|
3610
|
+
ambientStrength: environmentMap.ambientStrength
|
|
3611
|
+
});
|
|
3612
|
+
}
|
|
3129
3613
|
async function createWavefrontPathTracingComputeRenderer(options = {}) {
|
|
3130
3614
|
assertAnalyticDisplayQualityPolicy(options);
|
|
3131
3615
|
const constants = getGpuUsageConstants();
|
|
@@ -3145,6 +3629,7 @@ async function createWavefrontPathTracingComputeRenderer(options = {}) {
|
|
|
3145
3629
|
throw new Error("Unable to acquire a WebGPU adapter for wavefront path tracing.");
|
|
3146
3630
|
}
|
|
3147
3631
|
const device = await adapter.requestDevice(createWavefrontDeviceDescriptor(adapter, options));
|
|
3632
|
+
const gpuAdapterParallelism = createGpuAdapterParallelismDiagnostics(adapter, device);
|
|
3148
3633
|
const context = canvas.getContext("webgpu");
|
|
3149
3634
|
if (!context || typeof context.configure !== "function") {
|
|
3150
3635
|
throw new Error("Canvas WebGPU context does not support configure().");
|
|
@@ -3172,6 +3657,7 @@ async function createWavefrontPathTracingComputeRenderer(options = {}) {
|
|
|
3172
3657
|
const rayQueueBytes = config.tilePixelCapacity * RAY_RECORD_BYTES;
|
|
3173
3658
|
const hitBytes = config.tilePixelCapacity * HIT_RECORD_BYTES;
|
|
3174
3659
|
const accumulationBytes = config.tilePixelCapacity * ACCUMULATION_RECORD_BYTES;
|
|
3660
|
+
const pathVertexBytes = config.tilePixelCapacity * (config.maxDepth + 1) * PATH_VERTEX_RECORD_BYTES;
|
|
3175
3661
|
const activeQueue = createBuffer(device, bufferUsage, rayQueueBytes, "plasius.wavefront.activeQueue");
|
|
3176
3662
|
const nextQueue = createBuffer(device, bufferUsage, rayQueueBytes, "plasius.wavefront.nextQueue");
|
|
3177
3663
|
const hitBuffer = createBuffer(device, bufferUsage, hitBytes, "plasius.wavefront.hitBuffer");
|
|
@@ -3181,6 +3667,12 @@ async function createWavefrontPathTracingComputeRenderer(options = {}) {
|
|
|
3181
3667
|
accumulationBytes,
|
|
3182
3668
|
"plasius.wavefront.accumulation"
|
|
3183
3669
|
);
|
|
3670
|
+
const pathVertexBuffer = createBuffer(
|
|
3671
|
+
device,
|
|
3672
|
+
bufferUsage,
|
|
3673
|
+
pathVertexBytes,
|
|
3674
|
+
"plasius.wavefront.pathVertices"
|
|
3675
|
+
);
|
|
3184
3676
|
const sceneObjectBuffer = createBuffer(
|
|
3185
3677
|
device,
|
|
3186
3678
|
constants.buffer.STORAGE | constants.buffer.COPY_DST,
|
|
@@ -3235,9 +3727,10 @@ async function createWavefrontPathTracingComputeRenderer(options = {}) {
|
|
|
3235
3727
|
CONFIG_BUFFER_BYTES,
|
|
3236
3728
|
Number.isFinite(uniformOffsetAlignment) && uniformOffsetAlignment > 0 ? uniformOffsetAlignment : CONFIG_BUFFER_BYTES
|
|
3237
3729
|
);
|
|
3730
|
+
const outputConfigSlotCount = config.deferredPathResolve ? 0 : tiles.length;
|
|
3238
3731
|
const frameConfigSlotCount = Math.max(
|
|
3239
3732
|
1,
|
|
3240
|
-
tiles.length * config.samplesPerPixel +
|
|
3733
|
+
tiles.length * config.samplesPerPixel + outputConfigSlotCount + (config.denoise ? 1 : 0)
|
|
3241
3734
|
);
|
|
3242
3735
|
const configBuffer = createBuffer(
|
|
3243
3736
|
device,
|
|
@@ -3315,6 +3808,12 @@ async function createWavefrontPathTracingComputeRenderer(options = {}) {
|
|
|
3315
3808
|
magFilter: "nearest",
|
|
3316
3809
|
minFilter: "nearest"
|
|
3317
3810
|
});
|
|
3811
|
+
const environmentMapResource = createEnvironmentMapResource(
|
|
3812
|
+
device,
|
|
3813
|
+
constants,
|
|
3814
|
+
config.environmentMap,
|
|
3815
|
+
config.environmentColor
|
|
3816
|
+
);
|
|
3318
3817
|
const traceBindGroupLayout = device.createBindGroupLayout({
|
|
3319
3818
|
label: "plasius.wavefront.traceBindGroupLayout",
|
|
3320
3819
|
entries: [
|
|
@@ -3341,7 +3840,10 @@ async function createWavefrontPathTracingComputeRenderer(options = {}) {
|
|
|
3341
3840
|
visibility: constants.shader.COMPUTE,
|
|
3342
3841
|
storageTexture: { access: "write-only", format: "rgba16float" }
|
|
3343
3842
|
},
|
|
3344
|
-
{ binding: 19, visibility: constants.shader.COMPUTE, buffer: { type: "read-only-storage" } }
|
|
3843
|
+
{ binding: 19, visibility: constants.shader.COMPUTE, buffer: { type: "read-only-storage" } },
|
|
3844
|
+
{ binding: 20, visibility: constants.shader.COMPUTE, texture: { sampleType: "float" } },
|
|
3845
|
+
{ binding: 21, visibility: constants.shader.COMPUTE, sampler: { type: "filtering" } },
|
|
3846
|
+
{ binding: 22, visibility: constants.shader.COMPUTE, buffer: { type: "storage" } }
|
|
3345
3847
|
]
|
|
3346
3848
|
});
|
|
3347
3849
|
const accelerationBindGroupLayout = device.createBindGroupLayout({
|
|
@@ -3515,7 +4017,10 @@ async function createWavefrontPathTracingComputeRenderer(options = {}) {
|
|
|
3515
4017
|
{ binding: 8, resource: { buffer: triangleBuffer } },
|
|
3516
4018
|
{ binding: 9, resource: { buffer: bvhNodeBuffer } },
|
|
3517
4019
|
{ binding: 16, resource: radianceView },
|
|
3518
|
-
{ binding: 19, resource: { buffer: environmentPortalBuffer } }
|
|
4020
|
+
{ binding: 19, resource: { buffer: environmentPortalBuffer } },
|
|
4021
|
+
{ binding: 20, resource: environmentMapResource.view },
|
|
4022
|
+
{ binding: 21, resource: environmentMapResource.sampler },
|
|
4023
|
+
{ binding: 22, resource: { buffer: pathVertexBuffer } }
|
|
3519
4024
|
]
|
|
3520
4025
|
});
|
|
3521
4026
|
}
|
|
@@ -3605,6 +4110,10 @@ async function createWavefrontPathTracingComputeRenderer(options = {}) {
|
|
|
3605
4110
|
let accelerationBuilt = !config.gpuAccelerationBuildRequired;
|
|
3606
4111
|
let accelerationBuildCount = 0;
|
|
3607
4112
|
let activeCameraOptions = options.camera ?? DEFAULT_CAMERA;
|
|
4113
|
+
let lastGpuParallelism = createGpuParallelismDiagnostics(
|
|
4114
|
+
gpuAdapterParallelism,
|
|
4115
|
+
createGpuParallelismCounters()
|
|
4116
|
+
);
|
|
3608
4117
|
function createFrameConfigWriter(frameIndex) {
|
|
3609
4118
|
let slot = 0;
|
|
3610
4119
|
return (tile, buildRange = {}) => {
|
|
@@ -3621,7 +4130,7 @@ async function createWavefrontPathTracingComputeRenderer(options = {}) {
|
|
|
3621
4130
|
return offset;
|
|
3622
4131
|
};
|
|
3623
4132
|
}
|
|
3624
|
-
function dispatchGpuAccelerationBuild(frameIndex) {
|
|
4133
|
+
function dispatchGpuAccelerationBuild(frameIndex, parallelism) {
|
|
3625
4134
|
if (!config.gpuAccelerationBuildRequired || accelerationBuilt) {
|
|
3626
4135
|
return false;
|
|
3627
4136
|
}
|
|
@@ -3660,24 +4169,32 @@ async function createWavefrontPathTracingComputeRenderer(options = {}) {
|
|
|
3660
4169
|
});
|
|
3661
4170
|
passEncoder.setBindGroup(0, bvhBuildBindGroup, [0]);
|
|
3662
4171
|
passEncoder.setPipeline(pipelines.prepareMeshTrianglesAndLeaves);
|
|
3663
|
-
|
|
4172
|
+
const prepareWorkgroups = Math.ceil(config.bvhLeafSortCapacity / WORKGROUP_SIZE);
|
|
4173
|
+
passEncoder.dispatchWorkgroups(prepareWorkgroups);
|
|
4174
|
+
recordDirectDispatch(parallelism, [prepareWorkgroups]);
|
|
3664
4175
|
passEncoder.setPipeline(pipelines.sortBvhLeafRefs);
|
|
3665
4176
|
for (let stageIndex = 0; stageIndex < config.bvhSortStages.length; stageIndex += 1) {
|
|
3666
4177
|
passEncoder.setBindGroup(0, bvhBuildBindGroup, [
|
|
3667
4178
|
(stageIndex + 1) * configBufferStride
|
|
3668
4179
|
]);
|
|
3669
|
-
|
|
4180
|
+
const sortWorkgroups = Math.ceil(config.bvhLeafSortCapacity / WORKGROUP_SIZE);
|
|
4181
|
+
passEncoder.dispatchWorkgroups(sortWorkgroups);
|
|
4182
|
+
recordDirectDispatch(parallelism, [sortWorkgroups]);
|
|
3670
4183
|
}
|
|
3671
4184
|
passEncoder.setBindGroup(0, bvhBuildBindGroup, [0]);
|
|
3672
4185
|
passEncoder.setPipeline(pipelines.writeSortedBvhLeaves);
|
|
3673
|
-
|
|
4186
|
+
const leafWriteWorkgroups = Math.ceil(config.triangleCount / WORKGROUP_SIZE);
|
|
4187
|
+
passEncoder.dispatchWorkgroups(leafWriteWorkgroups);
|
|
4188
|
+
recordDirectDispatch(parallelism, [leafWriteWorkgroups]);
|
|
3674
4189
|
passEncoder.setPipeline(pipelines.buildBvhInternalLevel);
|
|
3675
4190
|
for (let levelIndex = 0; levelIndex < config.bvhBuildLevels.length; levelIndex += 1) {
|
|
3676
4191
|
const buildLevel = config.bvhBuildLevels[levelIndex];
|
|
3677
4192
|
passEncoder.setBindGroup(0, bvhBuildBindGroup, [
|
|
3678
4193
|
(buildLevelConfigStart + levelIndex) * configBufferStride
|
|
3679
4194
|
]);
|
|
3680
|
-
|
|
4195
|
+
const levelWorkgroups = Math.ceil(buildLevel.count / WORKGROUP_SIZE);
|
|
4196
|
+
passEncoder.dispatchWorkgroups(levelWorkgroups);
|
|
4197
|
+
recordDirectDispatch(parallelism, [levelWorkgroups]);
|
|
3681
4198
|
}
|
|
3682
4199
|
passEncoder.end();
|
|
3683
4200
|
device.queue.submit([encoder.finish()]);
|
|
@@ -3685,7 +4202,7 @@ async function createWavefrontPathTracingComputeRenderer(options = {}) {
|
|
|
3685
4202
|
accelerationBuildCount += 1;
|
|
3686
4203
|
return true;
|
|
3687
4204
|
}
|
|
3688
|
-
function encodeTileSample(encoder, tile, configOffset) {
|
|
4205
|
+
function encodeTileSample(encoder, tile, configOffset, parallelism) {
|
|
3689
4206
|
const generatePass = encoder.beginComputePass({
|
|
3690
4207
|
label: "plasius.wavefront.generatePrimaryRaysPass"
|
|
3691
4208
|
});
|
|
@@ -3693,6 +4210,7 @@ async function createWavefrontPathTracingComputeRenderer(options = {}) {
|
|
|
3693
4210
|
generatePass.setBindGroup(0, bindGroups[0], [configOffset]);
|
|
3694
4211
|
generatePass.setPipeline(pipelines.generatePrimaryRays);
|
|
3695
4212
|
generatePass.dispatchWorkgroups(tileWorkgroups);
|
|
4213
|
+
recordDirectDispatch(parallelism, [tileWorkgroups]);
|
|
3696
4214
|
generatePass.end();
|
|
3697
4215
|
for (let bounceIndex = 0; bounceIndex < config.maxDepth; bounceIndex += 1) {
|
|
3698
4216
|
encoder.copyBufferToBuffer(
|
|
@@ -3708,14 +4226,17 @@ async function createWavefrontPathTracingComputeRenderer(options = {}) {
|
|
|
3708
4226
|
passEncoder.setBindGroup(0, bindGroups[bounceIndex % 2], [configOffset]);
|
|
3709
4227
|
passEncoder.setPipeline(pipelines.intersectActiveQueue);
|
|
3710
4228
|
passEncoder.dispatchWorkgroupsIndirect(activeDispatchBuffer, 0);
|
|
4229
|
+
recordIndirectDispatch(parallelism, tileWorkgroups);
|
|
3711
4230
|
passEncoder.setPipeline(pipelines.resolveSurfaceRecords);
|
|
3712
4231
|
passEncoder.dispatchWorkgroupsIndirect(activeDispatchBuffer, 0);
|
|
4232
|
+
recordIndirectDispatch(parallelism, tileWorkgroups);
|
|
3713
4233
|
passEncoder.setPipeline(pipelines.compactAndSwapQueues);
|
|
3714
4234
|
passEncoder.dispatchWorkgroups(1);
|
|
4235
|
+
recordDirectDispatch(parallelism, [1], 1);
|
|
3715
4236
|
passEncoder.end();
|
|
3716
4237
|
}
|
|
3717
4238
|
}
|
|
3718
|
-
function encodeTileOutput(encoder, tile, configOffset) {
|
|
4239
|
+
function encodeTileOutput(encoder, tile, configOffset, parallelism) {
|
|
3719
4240
|
const passEncoder = encoder.beginComputePass({
|
|
3720
4241
|
label: "plasius.wavefront.outputPass"
|
|
3721
4242
|
});
|
|
@@ -3723,25 +4244,30 @@ async function createWavefrontPathTracingComputeRenderer(options = {}) {
|
|
|
3723
4244
|
passEncoder.setBindGroup(0, bindGroups[0], [configOffset]);
|
|
3724
4245
|
passEncoder.setPipeline(pipelines.accumulateTerminalRadiance);
|
|
3725
4246
|
passEncoder.dispatchWorkgroups(tileWorkgroups);
|
|
4247
|
+
recordDirectDispatch(parallelism, [tileWorkgroups]);
|
|
3726
4248
|
passEncoder.end();
|
|
3727
4249
|
}
|
|
3728
|
-
function encodeDenoise(encoder, configOffset) {
|
|
4250
|
+
function encodeDenoise(encoder, configOffset, parallelism) {
|
|
3729
4251
|
if (!config.denoise) {
|
|
3730
4252
|
return;
|
|
3731
4253
|
}
|
|
4254
|
+
const denoiseWorkgroupsX = Math.ceil(config.width / 8);
|
|
4255
|
+
const denoiseWorkgroupsY = Math.ceil(config.height / 8);
|
|
3732
4256
|
const radiancePass = encoder.beginComputePass({
|
|
3733
4257
|
label: "plasius.wavefront.denoiseRadiancePass"
|
|
3734
4258
|
});
|
|
3735
4259
|
radiancePass.setBindGroup(0, denoiseRadianceBindGroup, [configOffset]);
|
|
3736
4260
|
radiancePass.setPipeline(pipelines.denoiseLinearRadiance);
|
|
3737
|
-
radiancePass.dispatchWorkgroups(
|
|
4261
|
+
radiancePass.dispatchWorkgroups(denoiseWorkgroupsX, denoiseWorkgroupsY);
|
|
4262
|
+
recordDirectDispatch(parallelism, [denoiseWorkgroupsX, denoiseWorkgroupsY]);
|
|
3738
4263
|
radiancePass.end();
|
|
3739
4264
|
const resolvePass = encoder.beginComputePass({
|
|
3740
4265
|
label: "plasius.wavefront.denoiseResolvePass"
|
|
3741
4266
|
});
|
|
3742
4267
|
resolvePass.setBindGroup(0, denoiseResolveBindGroup, [configOffset]);
|
|
3743
4268
|
resolvePass.setPipeline(pipelines.resolveDenoisedOutputImage);
|
|
3744
|
-
resolvePass.dispatchWorkgroups(
|
|
4269
|
+
resolvePass.dispatchWorkgroups(denoiseWorkgroupsX, denoiseWorkgroupsY);
|
|
4270
|
+
recordDirectDispatch(parallelism, [denoiseWorkgroupsX, denoiseWorkgroupsY]);
|
|
3745
4271
|
resolvePass.end();
|
|
3746
4272
|
}
|
|
3747
4273
|
function encodePresent(encoder) {
|
|
@@ -3762,7 +4288,7 @@ async function createWavefrontPathTracingComputeRenderer(options = {}) {
|
|
|
3762
4288
|
passEncoder.draw(3);
|
|
3763
4289
|
passEncoder.end();
|
|
3764
4290
|
}
|
|
3765
|
-
function dispatchFrame(frameIndex) {
|
|
4291
|
+
function dispatchFrame(frameIndex, parallelism) {
|
|
3766
4292
|
const writeFrameConfig = createFrameConfigWriter(frameIndex);
|
|
3767
4293
|
let submissionCount = 0;
|
|
3768
4294
|
let encodedFramePasses = 0;
|
|
@@ -3793,20 +4319,25 @@ async function createWavefrontPathTracingComputeRenderer(options = {}) {
|
|
|
3793
4319
|
sampleIndex,
|
|
3794
4320
|
sampleWeight: 1 / config.samplesPerPixel
|
|
3795
4321
|
});
|
|
3796
|
-
encodeTileSample(reserveEncoder(), tile, configOffset);
|
|
4322
|
+
encodeTileSample(reserveEncoder(), tile, configOffset, parallelism);
|
|
4323
|
+
if (config.deferredPathResolve) {
|
|
4324
|
+
encodeTileOutput(reserveEncoder(), tile, configOffset, parallelism);
|
|
4325
|
+
}
|
|
4326
|
+
}
|
|
4327
|
+
if (!config.deferredPathResolve) {
|
|
4328
|
+
const outputConfigOffset = writeFrameConfig(tile, {
|
|
4329
|
+
sampleIndex: 0,
|
|
4330
|
+
sampleWeight: 1 / config.samplesPerPixel
|
|
4331
|
+
});
|
|
4332
|
+
encodeTileOutput(reserveEncoder(), tile, outputConfigOffset, parallelism);
|
|
3797
4333
|
}
|
|
3798
|
-
const outputConfigOffset = writeFrameConfig(tile, {
|
|
3799
|
-
sampleIndex: 0,
|
|
3800
|
-
sampleWeight: 1 / config.samplesPerPixel
|
|
3801
|
-
});
|
|
3802
|
-
encodeTileOutput(reserveEncoder(), tile, outputConfigOffset);
|
|
3803
4334
|
}
|
|
3804
4335
|
if (config.denoise) {
|
|
3805
4336
|
const denoiseConfigOffset = writeFrameConfig(
|
|
3806
4337
|
{ x: 0, y: 0, width: config.width, height: config.height },
|
|
3807
4338
|
{ sampleIndex: 0, sampleWeight: 1 / config.samplesPerPixel }
|
|
3808
4339
|
);
|
|
3809
|
-
encodeDenoise(reserveEncoder(), denoiseConfigOffset);
|
|
4340
|
+
encodeDenoise(reserveEncoder(), denoiseConfigOffset, parallelism);
|
|
3810
4341
|
}
|
|
3811
4342
|
encodePresent(reserveEncoder());
|
|
3812
4343
|
submitCurrentEncoder();
|
|
@@ -3815,8 +4346,10 @@ async function createWavefrontPathTracingComputeRenderer(options = {}) {
|
|
|
3815
4346
|
function renderOnce() {
|
|
3816
4347
|
frame += 1;
|
|
3817
4348
|
const frameIndex = frame + config.frameIndex;
|
|
3818
|
-
const
|
|
3819
|
-
const
|
|
4349
|
+
const parallelismCounters = createGpuParallelismCounters();
|
|
4350
|
+
const accelerationBuildSubmitted = dispatchGpuAccelerationBuild(frameIndex, parallelismCounters);
|
|
4351
|
+
const frameSubmissionCount = dispatchFrame(frameIndex, parallelismCounters);
|
|
4352
|
+
lastGpuParallelism = createGpuParallelismDiagnostics(gpuAdapterParallelism, parallelismCounters);
|
|
3820
4353
|
return Object.freeze({
|
|
3821
4354
|
frame,
|
|
3822
4355
|
width: config.width,
|
|
@@ -3833,6 +4366,8 @@ async function createWavefrontPathTracingComputeRenderer(options = {}) {
|
|
|
3833
4366
|
emissiveTriangleCount: config.emissiveTriangleCount,
|
|
3834
4367
|
environmentPortalCount: config.environmentPortalCount,
|
|
3835
4368
|
environmentPortalMode: config.environmentPortalMode,
|
|
4369
|
+
environmentMap: createEnvironmentMapSnapshot(config.environmentMap),
|
|
4370
|
+
deferredPathResolve: config.deferredPathResolve,
|
|
3836
4371
|
bvhNodeCount: config.bvhNodeCount,
|
|
3837
4372
|
displayQuality: config.displayQuality,
|
|
3838
4373
|
accelerationBuildMode: config.accelerationBuildMode,
|
|
@@ -3842,6 +4377,7 @@ async function createWavefrontPathTracingComputeRenderer(options = {}) {
|
|
|
3842
4377
|
accelerationBuildCount,
|
|
3843
4378
|
commandSubmissions: frameSubmissionCount + (accelerationBuildSubmitted ? 1 : 0),
|
|
3844
4379
|
frameConfigSlots: frameConfigSlotCount,
|
|
4380
|
+
gpuParallelism: lastGpuParallelism,
|
|
3845
4381
|
memory: config.memory
|
|
3846
4382
|
});
|
|
3847
4383
|
}
|
|
@@ -3950,6 +4486,8 @@ async function createWavefrontPathTracingComputeRenderer(options = {}) {
|
|
|
3950
4486
|
emissiveTriangleCount: config.emissiveTriangleCount,
|
|
3951
4487
|
environmentPortalCount: config.environmentPortalCount,
|
|
3952
4488
|
environmentPortalMode: config.environmentPortalMode,
|
|
4489
|
+
environmentMap: createEnvironmentMapSnapshot(config.environmentMap),
|
|
4490
|
+
deferredPathResolve: config.deferredPathResolve,
|
|
3953
4491
|
bvhNodeCount: config.bvhNodeCount,
|
|
3954
4492
|
displayQuality: config.displayQuality,
|
|
3955
4493
|
accelerationBuildMode: config.accelerationBuildMode,
|
|
@@ -3957,6 +4495,7 @@ async function createWavefrontPathTracingComputeRenderer(options = {}) {
|
|
|
3957
4495
|
accelerationBuilt,
|
|
3958
4496
|
accelerationBuildCount,
|
|
3959
4497
|
frameConfigSlots: frameConfigSlotCount,
|
|
4498
|
+
gpuParallelism: lastGpuParallelism,
|
|
3960
4499
|
memory: config.memory
|
|
3961
4500
|
});
|
|
3962
4501
|
}
|
|
@@ -3965,6 +4504,7 @@ async function createWavefrontPathTracingComputeRenderer(options = {}) {
|
|
|
3965
4504
|
nextQueue.destroy?.();
|
|
3966
4505
|
hitBuffer.destroy?.();
|
|
3967
4506
|
accumulationBuffer.destroy?.();
|
|
4507
|
+
pathVertexBuffer.destroy?.();
|
|
3968
4508
|
sceneObjectBuffer.destroy?.();
|
|
3969
4509
|
triangleBuffer.destroy?.();
|
|
3970
4510
|
bvhNodeBuffer.destroy?.();
|
|
@@ -3980,6 +4520,9 @@ async function createWavefrontPathTracingComputeRenderer(options = {}) {
|
|
|
3980
4520
|
radianceTexture.destroy?.();
|
|
3981
4521
|
denoiseScratchTexture.destroy?.();
|
|
3982
4522
|
outputTexture.destroy?.();
|
|
4523
|
+
if (environmentMapResource.ownsTexture) {
|
|
4524
|
+
environmentMapResource.texture?.destroy?.();
|
|
4525
|
+
}
|
|
3983
4526
|
context.unconfigure?.();
|
|
3984
4527
|
}
|
|
3985
4528
|
return Object.freeze({
|