@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.cjs
CHANGED
|
@@ -94,11 +94,12 @@ var BVH_LEAF_REF_RECORD_BYTES = 16;
|
|
|
94
94
|
var EMISSIVE_TRIANGLE_INDEX_BYTES = 4;
|
|
95
95
|
var ENVIRONMENT_PORTAL_RECORD_BYTES = 96;
|
|
96
96
|
var ACCUMULATION_RECORD_BYTES = 16;
|
|
97
|
-
var
|
|
97
|
+
var PATH_VERTEX_RECORD_BYTES = 16;
|
|
98
|
+
var CONFIG_BUFFER_BYTES = 304;
|
|
98
99
|
var COUNTER_DISPATCH_ARGS_OFFSET = 16;
|
|
99
100
|
var INDIRECT_DISPATCH_ARGS_BYTES = 12;
|
|
100
101
|
var COUNTER_BUFFER_BYTES = 32;
|
|
101
|
-
var TRACE_STORAGE_BUFFER_BINDINGS =
|
|
102
|
+
var TRACE_STORAGE_BUFFER_BINDINGS = 10;
|
|
102
103
|
var MATERIAL_DIFFUSE = 0;
|
|
103
104
|
var MATERIAL_METAL = 1;
|
|
104
105
|
var MATERIAL_DIELECTRIC = 2;
|
|
@@ -121,7 +122,8 @@ var DEFAULT_ENVIRONMENT_LIGHTING = Object.freeze({
|
|
|
121
122
|
sunColor: Object.freeze([2.8, 2.65, 2.35, 1]),
|
|
122
123
|
intensity: 1,
|
|
123
124
|
mode: 0,
|
|
124
|
-
exposure: 1
|
|
125
|
+
exposure: 1,
|
|
126
|
+
sunlitBaseline: 0.16
|
|
125
127
|
});
|
|
126
128
|
var wavefrontPathTracingComputeLimits = Object.freeze({
|
|
127
129
|
workgroupSize: WORKGROUP_SIZE,
|
|
@@ -138,6 +140,7 @@ var wavefrontPathTracingComputeLimits = Object.freeze({
|
|
|
138
140
|
emissiveTriangleMetadataRecordBytes: BVH_NODE_RECORD_BYTES,
|
|
139
141
|
environmentPortalRecordBytes: ENVIRONMENT_PORTAL_RECORD_BYTES,
|
|
140
142
|
accumulationRecordBytes: ACCUMULATION_RECORD_BYTES,
|
|
143
|
+
pathVertexRecordBytes: PATH_VERTEX_RECORD_BYTES,
|
|
141
144
|
counterRecordBytes: COUNTER_BUFFER_BYTES,
|
|
142
145
|
indirectDispatchRecordBytes: INDIRECT_DISPATCH_ARGS_BYTES
|
|
143
146
|
});
|
|
@@ -214,6 +217,33 @@ function asColor(value, fallback = [1, 1, 1, 1]) {
|
|
|
214
217
|
clamp(readFiniteNumber("color[3]", value[3], fallback[3] ?? 1), 0, 1)
|
|
215
218
|
];
|
|
216
219
|
}
|
|
220
|
+
function resolveEnvironmentMap(input = null) {
|
|
221
|
+
const source = input && typeof input === "object" ? input : null;
|
|
222
|
+
const hasTexture = Boolean(source?.view || source?.texture || source?.data);
|
|
223
|
+
const width = readPositiveInteger("environmentMap.width", source?.width, 1);
|
|
224
|
+
const height = readPositiveInteger("environmentMap.height", source?.height, 1);
|
|
225
|
+
return Object.freeze({
|
|
226
|
+
enabled: hasTexture && source?.enabled !== false,
|
|
227
|
+
width,
|
|
228
|
+
height,
|
|
229
|
+
format: typeof source?.format === "string" ? source.format : "rgba16float",
|
|
230
|
+
projection: typeof source?.projection === "string" ? source.projection : "equirectangular",
|
|
231
|
+
texture: source?.texture ?? null,
|
|
232
|
+
view: source?.view ?? null,
|
|
233
|
+
sampler: source?.sampler ?? null,
|
|
234
|
+
data: source?.data ?? null,
|
|
235
|
+
intensity: Math.max(0, readFiniteNumber("environmentMap.intensity", source?.intensity ?? source?.radianceScale, 1)),
|
|
236
|
+
rotationRadians: readFiniteNumber("environmentMap.rotationRadians", source?.rotationRadians ?? source?.rotation, 0),
|
|
237
|
+
ambientStrength: Math.max(
|
|
238
|
+
0,
|
|
239
|
+
readFiniteNumber("environmentMap.ambientStrength", source?.ambientStrength, 0.32)
|
|
240
|
+
)
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
function resolveDeferredPathResolve(options = {}) {
|
|
244
|
+
const value = options.deferredPathResolve ?? options.deferredResolve ?? options.pathResolve?.deferred ?? true;
|
|
245
|
+
return value !== false;
|
|
246
|
+
}
|
|
217
247
|
function emissionPower(emission) {
|
|
218
248
|
return Math.max(0, emission?.[0] ?? 0) + Math.max(0, emission?.[1] ?? 0) + Math.max(0, emission?.[2] ?? 0);
|
|
219
249
|
}
|
|
@@ -861,7 +891,15 @@ function resolveEnvironmentLighting(input, environmentColor, ambientColor) {
|
|
|
861
891
|
sunColor: Object.freeze(asColor(source.sunColor, DEFAULT_ENVIRONMENT_LIGHTING.sunColor)),
|
|
862
892
|
intensity: Math.max(1e-4, readFiniteNumber("environmentLighting.intensity", source.intensity, DEFAULT_ENVIRONMENT_LIGHTING.intensity)),
|
|
863
893
|
mode: readNonNegativeInteger("environmentLighting.mode", source.mode, DEFAULT_ENVIRONMENT_LIGHTING.mode),
|
|
864
|
-
exposure: Math.max(1e-4, readFiniteNumber("environmentLighting.exposure", source.exposure, DEFAULT_ENVIRONMENT_LIGHTING.exposure))
|
|
894
|
+
exposure: Math.max(1e-4, readFiniteNumber("environmentLighting.exposure", source.exposure, DEFAULT_ENVIRONMENT_LIGHTING.exposure)),
|
|
895
|
+
sunlitBaseline: Math.max(
|
|
896
|
+
0,
|
|
897
|
+
readFiniteNumber(
|
|
898
|
+
"environmentLighting.sunlitBaseline",
|
|
899
|
+
source.sunlitBaseline ?? source.daylightBaseline,
|
|
900
|
+
DEFAULT_ENVIRONMENT_LIGHTING.sunlitBaseline
|
|
901
|
+
)
|
|
902
|
+
)
|
|
865
903
|
});
|
|
866
904
|
}
|
|
867
905
|
function evaluateReferenceEnvironmentRadiance(config, origin, direction) {
|
|
@@ -1046,6 +1084,11 @@ function estimateWavefrontPathTracingMemory(options = {}) {
|
|
|
1046
1084
|
options.tilePixelCapacity,
|
|
1047
1085
|
DEFAULT_TILE_SIZE * DEFAULT_TILE_SIZE
|
|
1048
1086
|
);
|
|
1087
|
+
const maxDepth = clamp(
|
|
1088
|
+
readPositiveInteger("maxDepth", options.maxDepth, DEFAULT_MAX_DEPTH),
|
|
1089
|
+
1,
|
|
1090
|
+
16
|
|
1091
|
+
);
|
|
1049
1092
|
const sceneObjectCapacity = readPositiveInteger(
|
|
1050
1093
|
"sceneObjectCapacity",
|
|
1051
1094
|
options.sceneObjectCapacity,
|
|
@@ -1071,6 +1114,7 @@ function estimateWavefrontPathTracingMemory(options = {}) {
|
|
|
1071
1114
|
const queueBytes = tilePixelCapacity * RAY_RECORD_BYTES;
|
|
1072
1115
|
const hitBytes = tilePixelCapacity * HIT_RECORD_BYTES;
|
|
1073
1116
|
const accumulationBytes = tilePixelCapacity * ACCUMULATION_RECORD_BYTES;
|
|
1117
|
+
const pathVertexBytes = tilePixelCapacity * (maxDepth + 1) * PATH_VERTEX_RECORD_BYTES;
|
|
1074
1118
|
const sceneObjectBytes = sceneObjectCapacity * SCENE_OBJECT_RECORD_BYTES;
|
|
1075
1119
|
const triangleBytes = triangleCapacity * TRIANGLE_RECORD_BYTES;
|
|
1076
1120
|
const bvhNodeBytes = bvhNodeCapacity * BVH_NODE_RECORD_BYTES;
|
|
@@ -1082,6 +1126,7 @@ function estimateWavefrontPathTracingMemory(options = {}) {
|
|
|
1082
1126
|
queuePairBytes: queueBytes * 2,
|
|
1083
1127
|
hitBytes,
|
|
1084
1128
|
accumulationBytes,
|
|
1129
|
+
pathVertexBytes,
|
|
1085
1130
|
sceneObjectBytes,
|
|
1086
1131
|
triangleBytes,
|
|
1087
1132
|
bvhNodeBytes,
|
|
@@ -1091,7 +1136,7 @@ function estimateWavefrontPathTracingMemory(options = {}) {
|
|
|
1091
1136
|
configBytes: CONFIG_BUFFER_BYTES,
|
|
1092
1137
|
counterBytes: COUNTER_BUFFER_BYTES,
|
|
1093
1138
|
indirectDispatchBytes: INDIRECT_DISPATCH_ARGS_BYTES,
|
|
1094
|
-
totalHotBufferBytes: queueBytes * 2 + hitBytes + accumulationBytes + sceneObjectBytes + triangleBytes + bvhNodeBytes + bvhLeafReferenceBytes + emissiveTriangleMetadataBytes + environmentPortalBytes + CONFIG_BUFFER_BYTES + COUNTER_BUFFER_BYTES + INDIRECT_DISPATCH_ARGS_BYTES
|
|
1139
|
+
totalHotBufferBytes: queueBytes * 2 + hitBytes + accumulationBytes + pathVertexBytes + sceneObjectBytes + triangleBytes + bvhNodeBytes + bvhLeafReferenceBytes + emissiveTriangleMetadataBytes + environmentPortalBytes + CONFIG_BUFFER_BYTES + COUNTER_BUFFER_BYTES + INDIRECT_DISPATCH_ARGS_BYTES
|
|
1095
1140
|
});
|
|
1096
1141
|
}
|
|
1097
1142
|
function createWavefrontPathTracingComputeConfig(options = {}) {
|
|
@@ -1176,6 +1221,10 @@ function createWavefrontPathTracingComputeConfig(options = {}) {
|
|
|
1176
1221
|
options.environmentPortalMode ?? options.portalMode ?? options.environmentLighting?.environmentPortalMode,
|
|
1177
1222
|
environmentPortals.length > 0
|
|
1178
1223
|
);
|
|
1224
|
+
const environmentMap = resolveEnvironmentMap(
|
|
1225
|
+
options.environmentMap ?? options.environmentTexture ?? options.environmentLighting?.environmentMap
|
|
1226
|
+
);
|
|
1227
|
+
const deferredPathResolve = resolveDeferredPathResolve(options);
|
|
1179
1228
|
return Object.freeze({
|
|
1180
1229
|
mode: rendererWavefrontComputeMode,
|
|
1181
1230
|
width,
|
|
@@ -1210,12 +1259,15 @@ function createWavefrontPathTracingComputeConfig(options = {}) {
|
|
|
1210
1259
|
environmentPortalCount: environmentPortals.length,
|
|
1211
1260
|
environmentPortalCapacity,
|
|
1212
1261
|
environmentPortalMode,
|
|
1262
|
+
environmentMap,
|
|
1263
|
+
deferredPathResolve,
|
|
1213
1264
|
displayQuality: options.displayQuality === true,
|
|
1214
1265
|
requiresMeshBvhForDisplayQuality: true,
|
|
1215
1266
|
denoise: options.denoise !== false,
|
|
1216
1267
|
frameIndex: readNonNegativeInteger("frameIndex", options.frameIndex, 0),
|
|
1217
1268
|
memory: estimateWavefrontPathTracingMemory({
|
|
1218
1269
|
tilePixelCapacity,
|
|
1270
|
+
maxDepth,
|
|
1219
1271
|
sceneObjectCapacity,
|
|
1220
1272
|
triangleCapacity,
|
|
1221
1273
|
bvhNodeCapacity,
|
|
@@ -1412,6 +1464,18 @@ function createConfigPayload(config, tile, frameIndex, buildRange = {}) {
|
|
|
1412
1464
|
data.setUint32(260, config.environmentPortalMode ?? 0, true);
|
|
1413
1465
|
data.setUint32(264, 0, true);
|
|
1414
1466
|
data.setUint32(268, 0, true);
|
|
1467
|
+
writeVec4(floatView, 272, [
|
|
1468
|
+
config.environmentMap.enabled ? 1 : 0,
|
|
1469
|
+
config.environmentMap.intensity,
|
|
1470
|
+
config.environmentMap.rotationRadians,
|
|
1471
|
+
config.environmentMap.ambientStrength
|
|
1472
|
+
]);
|
|
1473
|
+
writeVec4(floatView, 288, [
|
|
1474
|
+
config.deferredPathResolve ? 1 : 0,
|
|
1475
|
+
config.environmentLighting.sunlitBaseline,
|
|
1476
|
+
0,
|
|
1477
|
+
0
|
|
1478
|
+
]);
|
|
1415
1479
|
return bytes;
|
|
1416
1480
|
}
|
|
1417
1481
|
function createTiles(width, height, tileSize) {
|
|
@@ -1657,6 +1721,136 @@ function alignTo(value, alignment) {
|
|
|
1657
1721
|
const resolvedAlignment = Math.max(1, alignment);
|
|
1658
1722
|
return Math.ceil(value / resolvedAlignment) * resolvedAlignment;
|
|
1659
1723
|
}
|
|
1724
|
+
function float32ToFloat16Bits(value) {
|
|
1725
|
+
const floatView = new Float32Array(1);
|
|
1726
|
+
const intView = new Uint32Array(floatView.buffer);
|
|
1727
|
+
floatView[0] = Number.isFinite(value) ? value : 0;
|
|
1728
|
+
const x = intView[0];
|
|
1729
|
+
const sign = x >> 16 & 32768;
|
|
1730
|
+
let mantissa = x & 8388607;
|
|
1731
|
+
let exponent = x >> 23 & 255;
|
|
1732
|
+
if (exponent === 255) {
|
|
1733
|
+
return sign | (mantissa ? 32256 : 31744);
|
|
1734
|
+
}
|
|
1735
|
+
exponent = exponent - 127 + 15;
|
|
1736
|
+
if (exponent >= 31) {
|
|
1737
|
+
return sign | 31744;
|
|
1738
|
+
}
|
|
1739
|
+
if (exponent <= 0) {
|
|
1740
|
+
if (exponent < -10) {
|
|
1741
|
+
return sign;
|
|
1742
|
+
}
|
|
1743
|
+
mantissa = (mantissa | 8388608) >> 1 - exponent;
|
|
1744
|
+
return sign | mantissa + 4096 >> 13;
|
|
1745
|
+
}
|
|
1746
|
+
return sign | exponent << 10 | mantissa + 4096 >> 13;
|
|
1747
|
+
}
|
|
1748
|
+
function environmentMapIntegerScale(data) {
|
|
1749
|
+
if (data instanceof Uint8Array) {
|
|
1750
|
+
return 1 / 255;
|
|
1751
|
+
}
|
|
1752
|
+
if (data instanceof Uint16Array) {
|
|
1753
|
+
return 1 / 65535;
|
|
1754
|
+
}
|
|
1755
|
+
return 1;
|
|
1756
|
+
}
|
|
1757
|
+
function readEnvironmentMapComponent(data, index, fallback, integerScale = 1) {
|
|
1758
|
+
if (!data || index >= data.length) {
|
|
1759
|
+
return fallback;
|
|
1760
|
+
}
|
|
1761
|
+
const value = Number(data[index]);
|
|
1762
|
+
return Number.isFinite(value) ? Math.max(0, value) * integerScale : fallback;
|
|
1763
|
+
}
|
|
1764
|
+
function createEnvironmentMapUploadBytes(environmentMap, fallbackColor) {
|
|
1765
|
+
const width = Math.max(1, environmentMap.width);
|
|
1766
|
+
const height = Math.max(1, environmentMap.height);
|
|
1767
|
+
const rowBytes = width * 8;
|
|
1768
|
+
const bytesPerRow = alignTo(rowBytes, 256);
|
|
1769
|
+
const bytes = new Uint8Array(bytesPerRow * height);
|
|
1770
|
+
const data = environmentMap.data;
|
|
1771
|
+
const integerScale = environmentMapIntegerScale(data);
|
|
1772
|
+
const view = new DataView(bytes.buffer);
|
|
1773
|
+
const writeComponent = (targetOffset, sourceOffset, fallback) => {
|
|
1774
|
+
view.setUint16(
|
|
1775
|
+
targetOffset,
|
|
1776
|
+
float32ToFloat16Bits(
|
|
1777
|
+
readEnvironmentMapComponent(data, sourceOffset, fallback, integerScale)
|
|
1778
|
+
),
|
|
1779
|
+
true
|
|
1780
|
+
);
|
|
1781
|
+
};
|
|
1782
|
+
for (let y = 0; y < height; y += 1) {
|
|
1783
|
+
for (let x = 0; x < width; x += 1) {
|
|
1784
|
+
const sourceOffset = (y * width + x) * 4;
|
|
1785
|
+
const targetOffset = y * bytesPerRow + x * 8;
|
|
1786
|
+
writeComponent(targetOffset, sourceOffset, fallbackColor[0]);
|
|
1787
|
+
writeComponent(targetOffset + 2, sourceOffset + 1, fallbackColor[1]);
|
|
1788
|
+
writeComponent(targetOffset + 4, sourceOffset + 2, fallbackColor[2]);
|
|
1789
|
+
writeComponent(targetOffset + 6, sourceOffset + 3, fallbackColor[3] ?? 1);
|
|
1790
|
+
}
|
|
1791
|
+
}
|
|
1792
|
+
return Object.freeze({
|
|
1793
|
+
bytes,
|
|
1794
|
+
bytesPerRow,
|
|
1795
|
+
width,
|
|
1796
|
+
height
|
|
1797
|
+
});
|
|
1798
|
+
}
|
|
1799
|
+
function createEnvironmentMapResource(device, constants, environmentMap, fallbackColor) {
|
|
1800
|
+
if (environmentMap.view) {
|
|
1801
|
+
return Object.freeze({
|
|
1802
|
+
view: environmentMap.view,
|
|
1803
|
+
sampler: environmentMap.sampler ?? device.createSampler({
|
|
1804
|
+
label: "plasius.wavefront.environmentMapSampler",
|
|
1805
|
+
addressModeU: "repeat",
|
|
1806
|
+
addressModeV: "clamp-to-edge",
|
|
1807
|
+
magFilter: "linear",
|
|
1808
|
+
minFilter: "linear"
|
|
1809
|
+
}),
|
|
1810
|
+
texture: null,
|
|
1811
|
+
ownsTexture: false
|
|
1812
|
+
});
|
|
1813
|
+
}
|
|
1814
|
+
if (environmentMap.texture && typeof environmentMap.texture.createView === "function") {
|
|
1815
|
+
return Object.freeze({
|
|
1816
|
+
view: environmentMap.texture.createView(),
|
|
1817
|
+
sampler: environmentMap.sampler ?? device.createSampler({
|
|
1818
|
+
label: "plasius.wavefront.environmentMapSampler",
|
|
1819
|
+
addressModeU: "repeat",
|
|
1820
|
+
addressModeV: "clamp-to-edge",
|
|
1821
|
+
magFilter: "linear",
|
|
1822
|
+
minFilter: "linear"
|
|
1823
|
+
}),
|
|
1824
|
+
texture: environmentMap.texture,
|
|
1825
|
+
ownsTexture: false
|
|
1826
|
+
});
|
|
1827
|
+
}
|
|
1828
|
+
const upload = createEnvironmentMapUploadBytes(environmentMap, fallbackColor);
|
|
1829
|
+
const texture = device.createTexture({
|
|
1830
|
+
label: environmentMap.enabled ? "plasius.wavefront.environmentMap" : "plasius.wavefront.environmentMapFallback",
|
|
1831
|
+
size: { width: upload.width, height: upload.height },
|
|
1832
|
+
format: "rgba16float",
|
|
1833
|
+
usage: constants.texture.TEXTURE_BINDING | constants.texture.COPY_DST
|
|
1834
|
+
});
|
|
1835
|
+
device.queue.writeTexture(
|
|
1836
|
+
{ texture },
|
|
1837
|
+
upload.bytes,
|
|
1838
|
+
{ bytesPerRow: upload.bytesPerRow, rowsPerImage: upload.height },
|
|
1839
|
+
{ width: upload.width, height: upload.height, depthOrArrayLayers: 1 }
|
|
1840
|
+
);
|
|
1841
|
+
return Object.freeze({
|
|
1842
|
+
view: texture.createView(),
|
|
1843
|
+
sampler: environmentMap.sampler ?? device.createSampler({
|
|
1844
|
+
label: "plasius.wavefront.environmentMapSampler",
|
|
1845
|
+
addressModeU: "repeat",
|
|
1846
|
+
addressModeV: "clamp-to-edge",
|
|
1847
|
+
magFilter: "linear",
|
|
1848
|
+
minFilter: "linear"
|
|
1849
|
+
}),
|
|
1850
|
+
texture,
|
|
1851
|
+
ownsTexture: true
|
|
1852
|
+
});
|
|
1853
|
+
}
|
|
1660
1854
|
async function getPipelineDiagnostics(shaderModule) {
|
|
1661
1855
|
if (typeof shaderModule?.compilationInfo !== "function") {
|
|
1662
1856
|
return "";
|
|
@@ -1865,6 +2059,8 @@ struct FrameConfig {
|
|
|
1865
2059
|
environmentPortalMode: u32,
|
|
1866
2060
|
_portalPad0: u32,
|
|
1867
2061
|
_portalPad1: u32,
|
|
2062
|
+
environmentMapSettings: vec4<f32>,
|
|
2063
|
+
pathResolveSettings: vec4<f32>,
|
|
1868
2064
|
};
|
|
1869
2065
|
|
|
1870
2066
|
struct Counters {
|
|
@@ -1924,6 +2120,9 @@ struct EnvironmentPortal {
|
|
|
1924
2120
|
@group(0) @binding(17) var finalDenoiseInputRadiance: texture_2d<f32>;
|
|
1925
2121
|
@group(0) @binding(18) var denoisedOutputImage: texture_storage_2d<rgba8unorm, write>;
|
|
1926
2122
|
@group(0) @binding(19) var<storage, read> environmentPortals: array<EnvironmentPortal>;
|
|
2123
|
+
@group(0) @binding(20) var environmentMapTexture: texture_2d<f32>;
|
|
2124
|
+
@group(0) @binding(21) var environmentMapSampler: sampler;
|
|
2125
|
+
@group(0) @binding(22) var<storage, read_write> pathVertices: array<vec4<f32>>;
|
|
1927
2126
|
|
|
1928
2127
|
fn hash_u32(value: u32) -> u32 {
|
|
1929
2128
|
var x = value;
|
|
@@ -1968,6 +2167,89 @@ fn max_component(value: vec3<f32>) -> f32 {
|
|
|
1968
2167
|
return max(max(value.x, value.y), value.z);
|
|
1969
2168
|
}
|
|
1970
2169
|
|
|
2170
|
+
fn environment_map_enabled() -> bool {
|
|
2171
|
+
return config.environmentMapSettings.x > 0.5;
|
|
2172
|
+
}
|
|
2173
|
+
|
|
2174
|
+
fn deferred_path_resolve_enabled() -> bool {
|
|
2175
|
+
return config.pathResolveSettings.x > 0.5;
|
|
2176
|
+
}
|
|
2177
|
+
|
|
2178
|
+
fn path_vertex_count_per_ray() -> u32 {
|
|
2179
|
+
return config.maxDepth + 1u;
|
|
2180
|
+
}
|
|
2181
|
+
|
|
2182
|
+
fn path_vertex_index(rayId: u32, depth: u32) -> u32 {
|
|
2183
|
+
return rayId * path_vertex_count_per_ray() + min(depth, config.maxDepth);
|
|
2184
|
+
}
|
|
2185
|
+
|
|
2186
|
+
fn clear_deferred_path(rayId: u32) {
|
|
2187
|
+
if (!deferred_path_resolve_enabled()) {
|
|
2188
|
+
return;
|
|
2189
|
+
}
|
|
2190
|
+
|
|
2191
|
+
for (var depth = 0u; depth <= config.maxDepth; depth = depth + 1u) {
|
|
2192
|
+
pathVertices[path_vertex_index(rayId, depth)] = vec4<f32>(0.0);
|
|
2193
|
+
if (depth == config.maxDepth) {
|
|
2194
|
+
break;
|
|
2195
|
+
}
|
|
2196
|
+
}
|
|
2197
|
+
}
|
|
2198
|
+
|
|
2199
|
+
fn record_deferred_path_response(ray: RayRecord, response: vec3<f32>) {
|
|
2200
|
+
if (!deferred_path_resolve_enabled() || ray.rayId >= config.tilePixelCount || ray.bounce >= config.maxDepth) {
|
|
2201
|
+
return;
|
|
2202
|
+
}
|
|
2203
|
+
pathVertices[path_vertex_index(ray.rayId, ray.bounce)] =
|
|
2204
|
+
vec4<f32>(max(response, vec3<f32>(0.0)), 1.0);
|
|
2205
|
+
}
|
|
2206
|
+
|
|
2207
|
+
fn record_deferred_terminal_source(ray: RayRecord, sourceRadiance: vec3<f32>) {
|
|
2208
|
+
if (!deferred_path_resolve_enabled() || ray.rayId >= config.tilePixelCount) {
|
|
2209
|
+
return;
|
|
2210
|
+
}
|
|
2211
|
+
pathVertices[path_vertex_index(ray.rayId, config.maxDepth)] =
|
|
2212
|
+
vec4<f32>(clamp_sample_radiance(sourceRadiance), 1.0);
|
|
2213
|
+
}
|
|
2214
|
+
|
|
2215
|
+
fn environment_map_uv(direction: vec3<f32>) -> vec2<f32> {
|
|
2216
|
+
let rayDirection = safe_normalize(direction, vec3<f32>(0.0, 1.0, 0.0));
|
|
2217
|
+
let rotationTurns = config.environmentMapSettings.z / 6.28318530718;
|
|
2218
|
+
let u = fract(atan2(rayDirection.z, rayDirection.x) / 6.28318530718 + 0.5 + rotationTurns);
|
|
2219
|
+
let v = acos(clamp(rayDirection.y, -1.0, 1.0)) / 3.14159265359;
|
|
2220
|
+
return vec2<f32>(u, clamp(v, 0.0, 1.0));
|
|
2221
|
+
}
|
|
2222
|
+
|
|
2223
|
+
fn environment_map_radiance(direction: vec3<f32>) -> vec3<f32> {
|
|
2224
|
+
let uv = environment_map_uv(direction);
|
|
2225
|
+
let texel = max(textureSampleLevel(environmentMapTexture, environmentMapSampler, uv, 0.0).rgb, vec3<f32>(0.0));
|
|
2226
|
+
return texel * max(config.environmentMapSettings.y, 0.0);
|
|
2227
|
+
}
|
|
2228
|
+
|
|
2229
|
+
fn procedural_environment_radiance(direction: vec3<f32>) -> vec3<f32> {
|
|
2230
|
+
let rayDirection = safe_normalize(direction, vec3<f32>(0.0, 1.0, 0.0));
|
|
2231
|
+
let upFactor = saturate(rayDirection.y * 0.5 + 0.5);
|
|
2232
|
+
let sunDirection = safe_normalize(
|
|
2233
|
+
config.environmentSunDirectionIntensity.xyz,
|
|
2234
|
+
vec3<f32>(0.0, 1.0, 0.0)
|
|
2235
|
+
);
|
|
2236
|
+
let sunGlow = pow(saturate(dot(rayDirection, sunDirection)), 192.0);
|
|
2237
|
+
let gradient =
|
|
2238
|
+
config.environmentHorizonColor.xyz * (1.0 - upFactor) +
|
|
2239
|
+
config.environmentZenithColor.xyz * upFactor;
|
|
2240
|
+
return (
|
|
2241
|
+
gradient +
|
|
2242
|
+
config.environmentSunColor.xyz * sunGlow
|
|
2243
|
+
) * max(config.environmentSunDirectionIntensity.w, 0.0001);
|
|
2244
|
+
}
|
|
2245
|
+
|
|
2246
|
+
fn base_environment_radiance(direction: vec3<f32>) -> vec3<f32> {
|
|
2247
|
+
if (environment_map_enabled()) {
|
|
2248
|
+
return environment_map_radiance(direction);
|
|
2249
|
+
}
|
|
2250
|
+
return procedural_environment_radiance(direction);
|
|
2251
|
+
}
|
|
2252
|
+
|
|
1971
2253
|
fn environment_portal_radiance_scale(origin: vec3<f32>, direction: vec3<f32>) -> vec3<f32> {
|
|
1972
2254
|
if (config.environmentPortalCount == 0u || config.environmentPortalMode == 0u) {
|
|
1973
2255
|
return vec3<f32>(1.0);
|
|
@@ -2007,22 +2289,9 @@ fn environment_portal_radiance_scale(origin: vec3<f32>, direction: vec3<f32>) ->
|
|
|
2007
2289
|
|
|
2008
2290
|
fn environment_radiance(origin: vec3<f32>, direction: vec3<f32>) -> vec3<f32> {
|
|
2009
2291
|
let rayDirection = safe_normalize(direction, vec3<f32>(0.0, 1.0, 0.0));
|
|
2010
|
-
let upFactor = saturate(rayDirection.y * 0.5 + 0.5);
|
|
2011
|
-
let sunDirection = safe_normalize(
|
|
2012
|
-
config.environmentSunDirectionIntensity.xyz,
|
|
2013
|
-
vec3<f32>(0.0, 1.0, 0.0)
|
|
2014
|
-
);
|
|
2015
|
-
let sunGlow = pow(saturate(dot(rayDirection, sunDirection)), 192.0);
|
|
2016
|
-
let gradient =
|
|
2017
|
-
config.environmentHorizonColor.xyz * (1.0 - upFactor) +
|
|
2018
|
-
config.environmentZenithColor.xyz * upFactor;
|
|
2019
2292
|
let portalScale = environment_portal_radiance_scale(origin, rayDirection);
|
|
2020
2293
|
let portalHit = max_component(portalScale) > 0.0001;
|
|
2021
|
-
return (
|
|
2022
|
-
gradient +
|
|
2023
|
-
config.environmentSunColor.xyz * sunGlow
|
|
2024
|
-
) *
|
|
2025
|
-
max(config.environmentSunDirectionIntensity.w, 0.0001) *
|
|
2294
|
+
return base_environment_radiance(rayDirection) *
|
|
2026
2295
|
select(vec3<f32>(1.0), portalScale, portalHit);
|
|
2027
2296
|
}
|
|
2028
2297
|
|
|
@@ -2038,16 +2307,59 @@ fn gated_environment_radiance(origin: vec3<f32>, direction: vec3<f32>) -> vec3<f
|
|
|
2038
2307
|
return environment_radiance(origin, direction);
|
|
2039
2308
|
}
|
|
2040
2309
|
|
|
2041
|
-
fn
|
|
2310
|
+
fn surface_path_response(hit: HitRecord) -> vec3<f32> {
|
|
2311
|
+
let color = clamp(hit.color.xyz, vec3<f32>(0.0), vec3<f32>(1.0));
|
|
2312
|
+
let opacity = clamp(hit.material.z, 0.0, 1.0);
|
|
2313
|
+
let materialEnergy = select(0.68, 0.92, hit.materialKind == 1u || hit.materialKind == 2u);
|
|
2314
|
+
let transparentEnergy = select(materialEnergy, 0.9, hit.hitType == 3u);
|
|
2315
|
+
return mix(vec3<f32>(1.0), color, max(opacity, 0.18)) * transparentEnergy;
|
|
2316
|
+
}
|
|
2317
|
+
|
|
2318
|
+
fn sunlit_baseline_radiance(normal: vec3<f32>) -> vec3<f32> {
|
|
2319
|
+
let baseline = max(config.pathResolveSettings.y, 0.0);
|
|
2320
|
+
if (baseline <= 0.000001) {
|
|
2321
|
+
return vec3<f32>(0.0);
|
|
2322
|
+
}
|
|
2323
|
+
let sunDirection = safe_normalize(
|
|
2324
|
+
config.environmentSunDirectionIntensity.xyz,
|
|
2325
|
+
vec3<f32>(0.0, 1.0, 0.0)
|
|
2326
|
+
);
|
|
2327
|
+
let sunFacing = saturate(dot(normal, sunDirection));
|
|
2328
|
+
let skyFacing = 0.35 + saturate(normal.y * 0.5 + 0.5) * 0.65;
|
|
2329
|
+
let directionalWeight = 0.38 + sunFacing * 0.62;
|
|
2330
|
+
let sunTint = max(config.environmentSunColor.xyz, vec3<f32>(0.0));
|
|
2331
|
+
return clamp_sample_radiance(sunTint * baseline * skyFacing * directionalWeight * 0.04);
|
|
2332
|
+
}
|
|
2333
|
+
|
|
2334
|
+
fn terminal_surface_environment_source(hit: HitRecord) -> vec3<f32> {
|
|
2042
2335
|
let normal = safe_normalize(hit.shadingNormal.xyz, vec3<f32>(0.0, 1.0, 0.0));
|
|
2043
|
-
let surfaceColor = max(hit.color.xyz, config.ambientColor.xyz);
|
|
2044
2336
|
let normalEnvironment = gated_environment_radiance(
|
|
2045
2337
|
hit.position.xyz + normal * 0.003,
|
|
2046
2338
|
normal
|
|
2047
2339
|
);
|
|
2048
|
-
let
|
|
2340
|
+
let sunlitFloor = sunlit_baseline_radiance(normal);
|
|
2341
|
+
let ambientFloor = select(
|
|
2342
|
+
max(config.ambientColor.xyz, sunlitFloor * 0.82),
|
|
2343
|
+
max(config.ambientColor.xyz * 0.35, sunlitFloor * 0.58),
|
|
2344
|
+
environment_map_enabled()
|
|
2345
|
+
);
|
|
2346
|
+
let environmentInfluence = select(
|
|
2347
|
+
max(0.12, config.pathResolveSettings.y * 0.42),
|
|
2348
|
+
max(config.environmentMapSettings.w, max(0.12, config.pathResolveSettings.y * 0.42)),
|
|
2349
|
+
environment_map_enabled()
|
|
2350
|
+
);
|
|
2351
|
+
let environmentFloor = max(ambientFloor, max(sunlitFloor, normalEnvironment * environmentInfluence));
|
|
2049
2352
|
let materialFloor = select(0.7, 1.0, hit.materialKind == 0u || hit.materialKind == 3u);
|
|
2050
|
-
return clamp_sample_radiance(
|
|
2353
|
+
return clamp_sample_radiance(environmentFloor * materialFloor);
|
|
2354
|
+
}
|
|
2355
|
+
|
|
2356
|
+
fn terminal_surface_environment_contribution(ray: RayRecord, hit: HitRecord) -> vec3<f32> {
|
|
2357
|
+
let surfaceColor = max(hit.color.xyz, config.ambientColor.xyz);
|
|
2358
|
+
return clamp_sample_radiance(
|
|
2359
|
+
ray.throughput.xyz *
|
|
2360
|
+
surfaceColor *
|
|
2361
|
+
terminal_surface_environment_source(hit)
|
|
2362
|
+
);
|
|
2051
2363
|
}
|
|
2052
2364
|
|
|
2053
2365
|
fn direct_environment_portal_irradiance(origin: vec3<f32>, normal: vec3<f32>) -> vec3<f32> {
|
|
@@ -2100,7 +2412,17 @@ fn surface_direct_environment_contribution(ray: RayRecord, hit: HitRecord) -> ve
|
|
|
2100
2412
|
|
|
2101
2413
|
let normalEnvironment = gated_environment_radiance(origin, normal);
|
|
2102
2414
|
let skyVisibility = 0.35 + saturate(normal.y * 0.5 + 0.5) * 0.45;
|
|
2103
|
-
let
|
|
2415
|
+
let sunlitFloor = sunlit_baseline_radiance(normal);
|
|
2416
|
+
let ambientIrradiance = max(
|
|
2417
|
+
select(config.ambientColor.xyz * 0.72, config.ambientColor.xyz * 0.28, environment_map_enabled()),
|
|
2418
|
+
sunlitFloor * select(0.72, 0.45, environment_map_enabled())
|
|
2419
|
+
);
|
|
2420
|
+
let environmentIrradianceScale = select(
|
|
2421
|
+
max(0.16, config.pathResolveSettings.y * 0.45),
|
|
2422
|
+
max(config.environmentMapSettings.w, max(0.16, config.pathResolveSettings.y * 0.45)),
|
|
2423
|
+
environment_map_enabled()
|
|
2424
|
+
);
|
|
2425
|
+
let skyIrradiance = max(ambientIrradiance, normalEnvironment * skyVisibility * environmentIrradianceScale);
|
|
2104
2426
|
|
|
2105
2427
|
let sunDirection = safe_normalize(
|
|
2106
2428
|
config.environmentSunDirectionIntensity.xyz,
|
|
@@ -2724,6 +3046,7 @@ fn generatePrimaryRays(@builtin(global_invocation_id) globalId: vec3<u32>) {
|
|
|
2724
3046
|
return;
|
|
2725
3047
|
}
|
|
2726
3048
|
activeQueue[index] = make_ray(index);
|
|
3049
|
+
clear_deferred_path(index);
|
|
2727
3050
|
if (u32(config.projectionAndSampling.w) == 0u) {
|
|
2728
3051
|
accumulation[index] = vec4<f32>(0.0);
|
|
2729
3052
|
}
|
|
@@ -2976,25 +3299,37 @@ fn resolveSurfaceRecords(@builtin(global_invocation_id) globalId: vec3<u32>) {
|
|
|
2976
3299
|
|
|
2977
3300
|
if (hit.hitType == 1u) {
|
|
2978
3301
|
let guidedLightWeight = select(1.0, 0.24, (ray.flags & RAY_FLAG_GUIDED_EMISSIVE) != 0u);
|
|
2979
|
-
|
|
2980
|
-
|
|
2981
|
-
|
|
2982
|
-
|
|
2983
|
-
|
|
3302
|
+
let sourceRadiance = max(hit.emission.xyz, hit.color.xyz) * guidedLightWeight;
|
|
3303
|
+
if (deferred_path_resolve_enabled()) {
|
|
3304
|
+
record_deferred_terminal_source(ray, sourceRadiance);
|
|
3305
|
+
} else {
|
|
3306
|
+
contribution = clamp_sample_radiance(ray.throughput.xyz * sourceRadiance);
|
|
3307
|
+
accumulation[ray.rayId] =
|
|
3308
|
+
accumulation[ray.rayId] + vec4<f32>(contribution * sample_weight(), 1.0);
|
|
3309
|
+
}
|
|
2984
3310
|
atomicAdd(&counters.terminatedCount, 1u);
|
|
2985
3311
|
return;
|
|
2986
3312
|
}
|
|
2987
3313
|
|
|
2988
3314
|
if (hit.hitType == 2u) {
|
|
2989
|
-
|
|
2990
|
-
|
|
2991
|
-
|
|
3315
|
+
if (deferred_path_resolve_enabled()) {
|
|
3316
|
+
record_deferred_terminal_source(ray, hit.color.xyz);
|
|
3317
|
+
} else {
|
|
3318
|
+
contribution = clamp_sample_radiance(ray.throughput.xyz * max(hit.color.xyz, config.ambientColor.xyz));
|
|
3319
|
+
accumulation[ray.rayId] =
|
|
3320
|
+
accumulation[ray.rayId] + vec4<f32>(contribution * sample_weight(), 1.0);
|
|
3321
|
+
}
|
|
2992
3322
|
atomicAdd(&counters.terminatedCount, 1u);
|
|
2993
3323
|
return;
|
|
2994
3324
|
}
|
|
2995
3325
|
|
|
3326
|
+
let response = surface_path_response(hit);
|
|
3327
|
+
record_deferred_path_response(ray, response);
|
|
3328
|
+
|
|
2996
3329
|
let shouldEstimateDirectEnvironment =
|
|
2997
|
-
(
|
|
3330
|
+
!deferred_path_resolve_enabled() &&
|
|
3331
|
+
(hit.materialKind == 0u || hit.materialKind == 1u) &&
|
|
3332
|
+
hit.material.z >= 0.95;
|
|
2998
3333
|
if (shouldEstimateDirectEnvironment) {
|
|
2999
3334
|
let directEnvironment = surface_direct_environment_contribution(ray, hit);
|
|
3000
3335
|
accumulation[ray.rayId] =
|
|
@@ -3002,9 +3337,13 @@ fn resolveSurfaceRecords(@builtin(global_invocation_id) globalId: vec3<u32>) {
|
|
|
3002
3337
|
}
|
|
3003
3338
|
|
|
3004
3339
|
if (ray.bounce + 1u >= config.maxDepth) {
|
|
3005
|
-
|
|
3006
|
-
|
|
3007
|
-
|
|
3340
|
+
if (deferred_path_resolve_enabled()) {
|
|
3341
|
+
record_deferred_terminal_source(ray, terminal_surface_environment_source(hit));
|
|
3342
|
+
} else {
|
|
3343
|
+
let terminalEnvironment = terminal_surface_environment_contribution(ray, hit);
|
|
3344
|
+
accumulation[ray.rayId] =
|
|
3345
|
+
accumulation[ray.rayId] + vec4<f32>(terminalEnvironment * sample_weight(), 1.0);
|
|
3346
|
+
}
|
|
3008
3347
|
atomicAdd(&counters.terminatedCount, 1u);
|
|
3009
3348
|
return;
|
|
3010
3349
|
}
|
|
@@ -3013,17 +3352,17 @@ fn resolveSurfaceRecords(@builtin(global_invocation_id) globalId: vec3<u32>) {
|
|
|
3013
3352
|
let scatter = scatter_direction(ray, hit, seed);
|
|
3014
3353
|
let nextIndex = atomicAdd(&counters.nextCount, 1u);
|
|
3015
3354
|
if (nextIndex >= config.tilePixelCount) {
|
|
3016
|
-
|
|
3017
|
-
|
|
3018
|
-
|
|
3355
|
+
if (deferred_path_resolve_enabled()) {
|
|
3356
|
+
record_deferred_terminal_source(ray, terminal_surface_environment_source(hit));
|
|
3357
|
+
} else {
|
|
3358
|
+
let overflowEnvironment = terminal_surface_environment_contribution(ray, hit);
|
|
3359
|
+
accumulation[ray.rayId] =
|
|
3360
|
+
accumulation[ray.rayId] + vec4<f32>(overflowEnvironment * sample_weight(), 1.0);
|
|
3361
|
+
}
|
|
3019
3362
|
atomicAdd(&counters.terminatedCount, 1u);
|
|
3020
3363
|
return;
|
|
3021
3364
|
}
|
|
3022
|
-
let
|
|
3023
|
-
let opacity = clamp(hit.material.z, 0.0, 1.0);
|
|
3024
|
-
let materialEnergy = select(0.68, 0.92, hit.materialKind == 1u || hit.materialKind == 2u);
|
|
3025
|
-
let transparentEnergy = select(materialEnergy, 0.9, hit.hitType == 3u);
|
|
3026
|
-
let throughput = ray.throughput.xyz * mix(vec3<f32>(1.0), color, max(opacity, 0.18)) * transparentEnergy;
|
|
3365
|
+
let throughput = ray.throughput.xyz * response;
|
|
3027
3366
|
nextQueue[nextIndex] = RayRecord(
|
|
3028
3367
|
ray.rayId,
|
|
3029
3368
|
ray.rayId,
|
|
@@ -3051,6 +3390,27 @@ fn compactAndSwapQueues(@builtin(global_invocation_id) globalId: vec3<u32>) {
|
|
|
3051
3390
|
write_active_dispatch_args(activeCount);
|
|
3052
3391
|
}
|
|
3053
3392
|
|
|
3393
|
+
fn resolve_deferred_path_radiance(rayId: u32) -> vec3<f32> {
|
|
3394
|
+
let terminal = pathVertices[path_vertex_index(rayId, config.maxDepth)];
|
|
3395
|
+
if (terminal.w <= 0.0) {
|
|
3396
|
+
return vec3<f32>(0.0);
|
|
3397
|
+
}
|
|
3398
|
+
|
|
3399
|
+
var radiance = terminal.xyz;
|
|
3400
|
+
var depth = config.maxDepth;
|
|
3401
|
+
loop {
|
|
3402
|
+
if (depth == 0u) {
|
|
3403
|
+
break;
|
|
3404
|
+
}
|
|
3405
|
+
depth = depth - 1u;
|
|
3406
|
+
let response = pathVertices[path_vertex_index(rayId, depth)];
|
|
3407
|
+
if (response.w > 0.0) {
|
|
3408
|
+
radiance = radiance * response.xyz;
|
|
3409
|
+
}
|
|
3410
|
+
}
|
|
3411
|
+
return clamp_sample_radiance(radiance);
|
|
3412
|
+
}
|
|
3413
|
+
|
|
3054
3414
|
@compute @workgroup_size(64)
|
|
3055
3415
|
fn accumulateTerminalRadiance(@builtin(global_invocation_id) globalId: vec3<u32>) {
|
|
3056
3416
|
let index = globalId.x;
|
|
@@ -3060,7 +3420,12 @@ fn accumulateTerminalRadiance(@builtin(global_invocation_id) globalId: vec3<u32>
|
|
|
3060
3420
|
let localX = index % config.tileWidth;
|
|
3061
3421
|
let localY = index / config.tileWidth;
|
|
3062
3422
|
let pixel = vec2<i32>(i32(config.tileX + localX), i32(config.tileY + localY));
|
|
3063
|
-
|
|
3423
|
+
var radiance = max(accumulation[index].xyz, vec3<f32>(0.0));
|
|
3424
|
+
if (deferred_path_resolve_enabled()) {
|
|
3425
|
+
let resolved = resolve_deferred_path_radiance(index) * sample_weight();
|
|
3426
|
+
radiance = clamp_sample_radiance(radiance + resolved);
|
|
3427
|
+
accumulation[index] = vec4<f32>(radiance, 1.0);
|
|
3428
|
+
}
|
|
3064
3429
|
|
|
3065
3430
|
textureStore(radianceImage, pixel, vec4<f32>(radiance, 1.0));
|
|
3066
3431
|
if (config.denoise == 0u) {
|
|
@@ -3198,6 +3563,125 @@ function createWavefrontDeviceDescriptor(adapter, options = {}) {
|
|
|
3198
3563
|
}
|
|
3199
3564
|
return Object.keys(descriptor).length > 0 ? descriptor : void 0;
|
|
3200
3565
|
}
|
|
3566
|
+
function readGpuLimit(adapter, device, name) {
|
|
3567
|
+
const adapterValue = Number(adapter?.limits?.[name]);
|
|
3568
|
+
if (Number.isFinite(adapterValue)) {
|
|
3569
|
+
return adapterValue;
|
|
3570
|
+
}
|
|
3571
|
+
const deviceValue = Number(device?.limits?.[name]);
|
|
3572
|
+
return Number.isFinite(deviceValue) ? deviceValue : null;
|
|
3573
|
+
}
|
|
3574
|
+
function createAdapterInfoSnapshot(adapter) {
|
|
3575
|
+
const info = adapter?.info;
|
|
3576
|
+
if (!info || typeof info !== "object") {
|
|
3577
|
+
return null;
|
|
3578
|
+
}
|
|
3579
|
+
return Object.freeze({
|
|
3580
|
+
vendor: typeof info.vendor === "string" ? info.vendor : "",
|
|
3581
|
+
architecture: typeof info.architecture === "string" ? info.architecture : "",
|
|
3582
|
+
device: typeof info.device === "string" ? info.device : "",
|
|
3583
|
+
description: typeof info.description === "string" ? info.description : ""
|
|
3584
|
+
});
|
|
3585
|
+
}
|
|
3586
|
+
function createGpuAdapterParallelismDiagnostics(adapter, device) {
|
|
3587
|
+
return Object.freeze({
|
|
3588
|
+
physicalCoreCount: null,
|
|
3589
|
+
physicalCoreCountAvailable: false,
|
|
3590
|
+
physicalCoreCountUnavailableReason: "WebGPU does not expose physical GPU core counts.",
|
|
3591
|
+
adapterInfo: createAdapterInfoSnapshot(adapter),
|
|
3592
|
+
adapterLimits: Object.freeze({
|
|
3593
|
+
maxComputeInvocationsPerWorkgroup: readGpuLimit(adapter, device, "maxComputeInvocationsPerWorkgroup"),
|
|
3594
|
+
maxComputeWorkgroupSizeX: readGpuLimit(adapter, device, "maxComputeWorkgroupSizeX"),
|
|
3595
|
+
maxComputeWorkgroupSizeY: readGpuLimit(adapter, device, "maxComputeWorkgroupSizeY"),
|
|
3596
|
+
maxComputeWorkgroupSizeZ: readGpuLimit(adapter, device, "maxComputeWorkgroupSizeZ"),
|
|
3597
|
+
maxComputeWorkgroupsPerDimension: readGpuLimit(adapter, device, "maxComputeWorkgroupsPerDimension"),
|
|
3598
|
+
maxStorageBuffersPerShaderStage: readGpuLimit(adapter, device, "maxStorageBuffersPerShaderStage"),
|
|
3599
|
+
maxStorageBufferBindingSize: readGpuLimit(adapter, device, "maxStorageBufferBindingSize")
|
|
3600
|
+
}),
|
|
3601
|
+
configuredWorkgroupSize: WORKGROUP_SIZE
|
|
3602
|
+
});
|
|
3603
|
+
}
|
|
3604
|
+
function createGpuParallelismCounters() {
|
|
3605
|
+
return {
|
|
3606
|
+
directDispatches: 0,
|
|
3607
|
+
directWorkgroups: 0,
|
|
3608
|
+
directShaderInvocations: 0,
|
|
3609
|
+
multiWorkgroupDispatches: 0,
|
|
3610
|
+
largestDirectWorkgroupsPerDispatch: 0,
|
|
3611
|
+
indirectDispatches: 0,
|
|
3612
|
+
estimatedIndirectWorkgroupsUpperBound: 0,
|
|
3613
|
+
estimatedIndirectShaderInvocationsUpperBound: 0,
|
|
3614
|
+
indirectDispatchesWithMultiWorkgroupCapacity: 0,
|
|
3615
|
+
largestEstimatedIndirectWorkgroupsPerDispatch: 0
|
|
3616
|
+
};
|
|
3617
|
+
}
|
|
3618
|
+
function countDispatchWorkgroups(groups) {
|
|
3619
|
+
return groups.reduce((product, value) => {
|
|
3620
|
+
const numeric = Number(value ?? 1);
|
|
3621
|
+
const count = Number.isFinite(numeric) ? Math.max(1, Math.trunc(numeric)) : 1;
|
|
3622
|
+
return product * count;
|
|
3623
|
+
}, 1);
|
|
3624
|
+
}
|
|
3625
|
+
function recordDirectDispatch(parallelism, groups, invocationsPerWorkgroup = WORKGROUP_SIZE) {
|
|
3626
|
+
const workgroups = countDispatchWorkgroups(groups);
|
|
3627
|
+
parallelism.directDispatches += 1;
|
|
3628
|
+
parallelism.directWorkgroups += workgroups;
|
|
3629
|
+
parallelism.directShaderInvocations += workgroups * invocationsPerWorkgroup;
|
|
3630
|
+
parallelism.largestDirectWorkgroupsPerDispatch = Math.max(
|
|
3631
|
+
parallelism.largestDirectWorkgroupsPerDispatch,
|
|
3632
|
+
workgroups
|
|
3633
|
+
);
|
|
3634
|
+
if (workgroups > 1) {
|
|
3635
|
+
parallelism.multiWorkgroupDispatches += 1;
|
|
3636
|
+
}
|
|
3637
|
+
}
|
|
3638
|
+
function recordIndirectDispatch(parallelism, estimatedWorkgroupsUpperBound, invocationsPerWorkgroup = WORKGROUP_SIZE) {
|
|
3639
|
+
const workgroups = Math.max(1, Math.trunc(Number(estimatedWorkgroupsUpperBound) || 1));
|
|
3640
|
+
parallelism.indirectDispatches += 1;
|
|
3641
|
+
parallelism.estimatedIndirectWorkgroupsUpperBound += workgroups;
|
|
3642
|
+
parallelism.estimatedIndirectShaderInvocationsUpperBound += workgroups * invocationsPerWorkgroup;
|
|
3643
|
+
parallelism.largestEstimatedIndirectWorkgroupsPerDispatch = Math.max(
|
|
3644
|
+
parallelism.largestEstimatedIndirectWorkgroupsPerDispatch,
|
|
3645
|
+
workgroups
|
|
3646
|
+
);
|
|
3647
|
+
if (workgroups > 1) {
|
|
3648
|
+
parallelism.indirectDispatchesWithMultiWorkgroupCapacity += 1;
|
|
3649
|
+
}
|
|
3650
|
+
}
|
|
3651
|
+
function createGpuParallelismDiagnostics(adapterDiagnostics, counters) {
|
|
3652
|
+
const totalEstimatedWorkgroupsUpperBound = counters.directWorkgroups + counters.estimatedIndirectWorkgroupsUpperBound;
|
|
3653
|
+
const totalEstimatedShaderInvocationsUpperBound = counters.directShaderInvocations + counters.estimatedIndirectShaderInvocationsUpperBound;
|
|
3654
|
+
const exposesMultiWorkgroupParallelism = counters.multiWorkgroupDispatches > 0 || counters.indirectDispatchesWithMultiWorkgroupCapacity > 0;
|
|
3655
|
+
return Object.freeze({
|
|
3656
|
+
...adapterDiagnostics,
|
|
3657
|
+
directDispatches: counters.directDispatches,
|
|
3658
|
+
directWorkgroups: counters.directWorkgroups,
|
|
3659
|
+
directShaderInvocations: counters.directShaderInvocations,
|
|
3660
|
+
multiWorkgroupDispatches: counters.multiWorkgroupDispatches,
|
|
3661
|
+
largestDirectWorkgroupsPerDispatch: counters.largestDirectWorkgroupsPerDispatch,
|
|
3662
|
+
indirectDispatches: counters.indirectDispatches,
|
|
3663
|
+
estimatedIndirectWorkgroupsUpperBound: counters.estimatedIndirectWorkgroupsUpperBound,
|
|
3664
|
+
estimatedIndirectShaderInvocationsUpperBound: counters.estimatedIndirectShaderInvocationsUpperBound,
|
|
3665
|
+
indirectDispatchesWithMultiWorkgroupCapacity: counters.indirectDispatchesWithMultiWorkgroupCapacity,
|
|
3666
|
+
largestEstimatedIndirectWorkgroupsPerDispatch: counters.largestEstimatedIndirectWorkgroupsPerDispatch,
|
|
3667
|
+
totalEstimatedWorkgroupsUpperBound,
|
|
3668
|
+
totalEstimatedShaderInvocationsUpperBound,
|
|
3669
|
+
exposesMultiWorkgroupParallelism,
|
|
3670
|
+
likelyUsesMoreThanOnePhysicalGpuCore: null,
|
|
3671
|
+
coreUtilizationStatus: "not-exposed-by-webgpu"
|
|
3672
|
+
});
|
|
3673
|
+
}
|
|
3674
|
+
function createEnvironmentMapSnapshot(environmentMap) {
|
|
3675
|
+
return Object.freeze({
|
|
3676
|
+
enabled: environmentMap.enabled,
|
|
3677
|
+
width: environmentMap.width,
|
|
3678
|
+
height: environmentMap.height,
|
|
3679
|
+
projection: environmentMap.projection,
|
|
3680
|
+
intensity: environmentMap.intensity,
|
|
3681
|
+
rotationRadians: environmentMap.rotationRadians,
|
|
3682
|
+
ambientStrength: environmentMap.ambientStrength
|
|
3683
|
+
});
|
|
3684
|
+
}
|
|
3201
3685
|
async function createWavefrontPathTracingComputeRenderer(options = {}) {
|
|
3202
3686
|
assertAnalyticDisplayQualityPolicy(options);
|
|
3203
3687
|
const constants = getGpuUsageConstants();
|
|
@@ -3217,6 +3701,7 @@ async function createWavefrontPathTracingComputeRenderer(options = {}) {
|
|
|
3217
3701
|
throw new Error("Unable to acquire a WebGPU adapter for wavefront path tracing.");
|
|
3218
3702
|
}
|
|
3219
3703
|
const device = await adapter.requestDevice(createWavefrontDeviceDescriptor(adapter, options));
|
|
3704
|
+
const gpuAdapterParallelism = createGpuAdapterParallelismDiagnostics(adapter, device);
|
|
3220
3705
|
const context = canvas.getContext("webgpu");
|
|
3221
3706
|
if (!context || typeof context.configure !== "function") {
|
|
3222
3707
|
throw new Error("Canvas WebGPU context does not support configure().");
|
|
@@ -3244,6 +3729,7 @@ async function createWavefrontPathTracingComputeRenderer(options = {}) {
|
|
|
3244
3729
|
const rayQueueBytes = config.tilePixelCapacity * RAY_RECORD_BYTES;
|
|
3245
3730
|
const hitBytes = config.tilePixelCapacity * HIT_RECORD_BYTES;
|
|
3246
3731
|
const accumulationBytes = config.tilePixelCapacity * ACCUMULATION_RECORD_BYTES;
|
|
3732
|
+
const pathVertexBytes = config.tilePixelCapacity * (config.maxDepth + 1) * PATH_VERTEX_RECORD_BYTES;
|
|
3247
3733
|
const activeQueue = createBuffer(device, bufferUsage, rayQueueBytes, "plasius.wavefront.activeQueue");
|
|
3248
3734
|
const nextQueue = createBuffer(device, bufferUsage, rayQueueBytes, "plasius.wavefront.nextQueue");
|
|
3249
3735
|
const hitBuffer = createBuffer(device, bufferUsage, hitBytes, "plasius.wavefront.hitBuffer");
|
|
@@ -3253,6 +3739,12 @@ async function createWavefrontPathTracingComputeRenderer(options = {}) {
|
|
|
3253
3739
|
accumulationBytes,
|
|
3254
3740
|
"plasius.wavefront.accumulation"
|
|
3255
3741
|
);
|
|
3742
|
+
const pathVertexBuffer = createBuffer(
|
|
3743
|
+
device,
|
|
3744
|
+
bufferUsage,
|
|
3745
|
+
pathVertexBytes,
|
|
3746
|
+
"plasius.wavefront.pathVertices"
|
|
3747
|
+
);
|
|
3256
3748
|
const sceneObjectBuffer = createBuffer(
|
|
3257
3749
|
device,
|
|
3258
3750
|
constants.buffer.STORAGE | constants.buffer.COPY_DST,
|
|
@@ -3307,9 +3799,10 @@ async function createWavefrontPathTracingComputeRenderer(options = {}) {
|
|
|
3307
3799
|
CONFIG_BUFFER_BYTES,
|
|
3308
3800
|
Number.isFinite(uniformOffsetAlignment) && uniformOffsetAlignment > 0 ? uniformOffsetAlignment : CONFIG_BUFFER_BYTES
|
|
3309
3801
|
);
|
|
3802
|
+
const outputConfigSlotCount = config.deferredPathResolve ? 0 : tiles.length;
|
|
3310
3803
|
const frameConfigSlotCount = Math.max(
|
|
3311
3804
|
1,
|
|
3312
|
-
tiles.length * config.samplesPerPixel +
|
|
3805
|
+
tiles.length * config.samplesPerPixel + outputConfigSlotCount + (config.denoise ? 1 : 0)
|
|
3313
3806
|
);
|
|
3314
3807
|
const configBuffer = createBuffer(
|
|
3315
3808
|
device,
|
|
@@ -3387,6 +3880,12 @@ async function createWavefrontPathTracingComputeRenderer(options = {}) {
|
|
|
3387
3880
|
magFilter: "nearest",
|
|
3388
3881
|
minFilter: "nearest"
|
|
3389
3882
|
});
|
|
3883
|
+
const environmentMapResource = createEnvironmentMapResource(
|
|
3884
|
+
device,
|
|
3885
|
+
constants,
|
|
3886
|
+
config.environmentMap,
|
|
3887
|
+
config.environmentColor
|
|
3888
|
+
);
|
|
3390
3889
|
const traceBindGroupLayout = device.createBindGroupLayout({
|
|
3391
3890
|
label: "plasius.wavefront.traceBindGroupLayout",
|
|
3392
3891
|
entries: [
|
|
@@ -3413,7 +3912,10 @@ async function createWavefrontPathTracingComputeRenderer(options = {}) {
|
|
|
3413
3912
|
visibility: constants.shader.COMPUTE,
|
|
3414
3913
|
storageTexture: { access: "write-only", format: "rgba16float" }
|
|
3415
3914
|
},
|
|
3416
|
-
{ binding: 19, visibility: constants.shader.COMPUTE, buffer: { type: "read-only-storage" } }
|
|
3915
|
+
{ binding: 19, visibility: constants.shader.COMPUTE, buffer: { type: "read-only-storage" } },
|
|
3916
|
+
{ binding: 20, visibility: constants.shader.COMPUTE, texture: { sampleType: "float" } },
|
|
3917
|
+
{ binding: 21, visibility: constants.shader.COMPUTE, sampler: { type: "filtering" } },
|
|
3918
|
+
{ binding: 22, visibility: constants.shader.COMPUTE, buffer: { type: "storage" } }
|
|
3417
3919
|
]
|
|
3418
3920
|
});
|
|
3419
3921
|
const accelerationBindGroupLayout = device.createBindGroupLayout({
|
|
@@ -3587,7 +4089,10 @@ async function createWavefrontPathTracingComputeRenderer(options = {}) {
|
|
|
3587
4089
|
{ binding: 8, resource: { buffer: triangleBuffer } },
|
|
3588
4090
|
{ binding: 9, resource: { buffer: bvhNodeBuffer } },
|
|
3589
4091
|
{ binding: 16, resource: radianceView },
|
|
3590
|
-
{ binding: 19, resource: { buffer: environmentPortalBuffer } }
|
|
4092
|
+
{ binding: 19, resource: { buffer: environmentPortalBuffer } },
|
|
4093
|
+
{ binding: 20, resource: environmentMapResource.view },
|
|
4094
|
+
{ binding: 21, resource: environmentMapResource.sampler },
|
|
4095
|
+
{ binding: 22, resource: { buffer: pathVertexBuffer } }
|
|
3591
4096
|
]
|
|
3592
4097
|
});
|
|
3593
4098
|
}
|
|
@@ -3677,6 +4182,10 @@ async function createWavefrontPathTracingComputeRenderer(options = {}) {
|
|
|
3677
4182
|
let accelerationBuilt = !config.gpuAccelerationBuildRequired;
|
|
3678
4183
|
let accelerationBuildCount = 0;
|
|
3679
4184
|
let activeCameraOptions = options.camera ?? DEFAULT_CAMERA;
|
|
4185
|
+
let lastGpuParallelism = createGpuParallelismDiagnostics(
|
|
4186
|
+
gpuAdapterParallelism,
|
|
4187
|
+
createGpuParallelismCounters()
|
|
4188
|
+
);
|
|
3680
4189
|
function createFrameConfigWriter(frameIndex) {
|
|
3681
4190
|
let slot = 0;
|
|
3682
4191
|
return (tile, buildRange = {}) => {
|
|
@@ -3693,7 +4202,7 @@ async function createWavefrontPathTracingComputeRenderer(options = {}) {
|
|
|
3693
4202
|
return offset;
|
|
3694
4203
|
};
|
|
3695
4204
|
}
|
|
3696
|
-
function dispatchGpuAccelerationBuild(frameIndex) {
|
|
4205
|
+
function dispatchGpuAccelerationBuild(frameIndex, parallelism) {
|
|
3697
4206
|
if (!config.gpuAccelerationBuildRequired || accelerationBuilt) {
|
|
3698
4207
|
return false;
|
|
3699
4208
|
}
|
|
@@ -3732,24 +4241,32 @@ async function createWavefrontPathTracingComputeRenderer(options = {}) {
|
|
|
3732
4241
|
});
|
|
3733
4242
|
passEncoder.setBindGroup(0, bvhBuildBindGroup, [0]);
|
|
3734
4243
|
passEncoder.setPipeline(pipelines.prepareMeshTrianglesAndLeaves);
|
|
3735
|
-
|
|
4244
|
+
const prepareWorkgroups = Math.ceil(config.bvhLeafSortCapacity / WORKGROUP_SIZE);
|
|
4245
|
+
passEncoder.dispatchWorkgroups(prepareWorkgroups);
|
|
4246
|
+
recordDirectDispatch(parallelism, [prepareWorkgroups]);
|
|
3736
4247
|
passEncoder.setPipeline(pipelines.sortBvhLeafRefs);
|
|
3737
4248
|
for (let stageIndex = 0; stageIndex < config.bvhSortStages.length; stageIndex += 1) {
|
|
3738
4249
|
passEncoder.setBindGroup(0, bvhBuildBindGroup, [
|
|
3739
4250
|
(stageIndex + 1) * configBufferStride
|
|
3740
4251
|
]);
|
|
3741
|
-
|
|
4252
|
+
const sortWorkgroups = Math.ceil(config.bvhLeafSortCapacity / WORKGROUP_SIZE);
|
|
4253
|
+
passEncoder.dispatchWorkgroups(sortWorkgroups);
|
|
4254
|
+
recordDirectDispatch(parallelism, [sortWorkgroups]);
|
|
3742
4255
|
}
|
|
3743
4256
|
passEncoder.setBindGroup(0, bvhBuildBindGroup, [0]);
|
|
3744
4257
|
passEncoder.setPipeline(pipelines.writeSortedBvhLeaves);
|
|
3745
|
-
|
|
4258
|
+
const leafWriteWorkgroups = Math.ceil(config.triangleCount / WORKGROUP_SIZE);
|
|
4259
|
+
passEncoder.dispatchWorkgroups(leafWriteWorkgroups);
|
|
4260
|
+
recordDirectDispatch(parallelism, [leafWriteWorkgroups]);
|
|
3746
4261
|
passEncoder.setPipeline(pipelines.buildBvhInternalLevel);
|
|
3747
4262
|
for (let levelIndex = 0; levelIndex < config.bvhBuildLevels.length; levelIndex += 1) {
|
|
3748
4263
|
const buildLevel = config.bvhBuildLevels[levelIndex];
|
|
3749
4264
|
passEncoder.setBindGroup(0, bvhBuildBindGroup, [
|
|
3750
4265
|
(buildLevelConfigStart + levelIndex) * configBufferStride
|
|
3751
4266
|
]);
|
|
3752
|
-
|
|
4267
|
+
const levelWorkgroups = Math.ceil(buildLevel.count / WORKGROUP_SIZE);
|
|
4268
|
+
passEncoder.dispatchWorkgroups(levelWorkgroups);
|
|
4269
|
+
recordDirectDispatch(parallelism, [levelWorkgroups]);
|
|
3753
4270
|
}
|
|
3754
4271
|
passEncoder.end();
|
|
3755
4272
|
device.queue.submit([encoder.finish()]);
|
|
@@ -3757,7 +4274,7 @@ async function createWavefrontPathTracingComputeRenderer(options = {}) {
|
|
|
3757
4274
|
accelerationBuildCount += 1;
|
|
3758
4275
|
return true;
|
|
3759
4276
|
}
|
|
3760
|
-
function encodeTileSample(encoder, tile, configOffset) {
|
|
4277
|
+
function encodeTileSample(encoder, tile, configOffset, parallelism) {
|
|
3761
4278
|
const generatePass = encoder.beginComputePass({
|
|
3762
4279
|
label: "plasius.wavefront.generatePrimaryRaysPass"
|
|
3763
4280
|
});
|
|
@@ -3765,6 +4282,7 @@ async function createWavefrontPathTracingComputeRenderer(options = {}) {
|
|
|
3765
4282
|
generatePass.setBindGroup(0, bindGroups[0], [configOffset]);
|
|
3766
4283
|
generatePass.setPipeline(pipelines.generatePrimaryRays);
|
|
3767
4284
|
generatePass.dispatchWorkgroups(tileWorkgroups);
|
|
4285
|
+
recordDirectDispatch(parallelism, [tileWorkgroups]);
|
|
3768
4286
|
generatePass.end();
|
|
3769
4287
|
for (let bounceIndex = 0; bounceIndex < config.maxDepth; bounceIndex += 1) {
|
|
3770
4288
|
encoder.copyBufferToBuffer(
|
|
@@ -3780,14 +4298,17 @@ async function createWavefrontPathTracingComputeRenderer(options = {}) {
|
|
|
3780
4298
|
passEncoder.setBindGroup(0, bindGroups[bounceIndex % 2], [configOffset]);
|
|
3781
4299
|
passEncoder.setPipeline(pipelines.intersectActiveQueue);
|
|
3782
4300
|
passEncoder.dispatchWorkgroupsIndirect(activeDispatchBuffer, 0);
|
|
4301
|
+
recordIndirectDispatch(parallelism, tileWorkgroups);
|
|
3783
4302
|
passEncoder.setPipeline(pipelines.resolveSurfaceRecords);
|
|
3784
4303
|
passEncoder.dispatchWorkgroupsIndirect(activeDispatchBuffer, 0);
|
|
4304
|
+
recordIndirectDispatch(parallelism, tileWorkgroups);
|
|
3785
4305
|
passEncoder.setPipeline(pipelines.compactAndSwapQueues);
|
|
3786
4306
|
passEncoder.dispatchWorkgroups(1);
|
|
4307
|
+
recordDirectDispatch(parallelism, [1], 1);
|
|
3787
4308
|
passEncoder.end();
|
|
3788
4309
|
}
|
|
3789
4310
|
}
|
|
3790
|
-
function encodeTileOutput(encoder, tile, configOffset) {
|
|
4311
|
+
function encodeTileOutput(encoder, tile, configOffset, parallelism) {
|
|
3791
4312
|
const passEncoder = encoder.beginComputePass({
|
|
3792
4313
|
label: "plasius.wavefront.outputPass"
|
|
3793
4314
|
});
|
|
@@ -3795,25 +4316,30 @@ async function createWavefrontPathTracingComputeRenderer(options = {}) {
|
|
|
3795
4316
|
passEncoder.setBindGroup(0, bindGroups[0], [configOffset]);
|
|
3796
4317
|
passEncoder.setPipeline(pipelines.accumulateTerminalRadiance);
|
|
3797
4318
|
passEncoder.dispatchWorkgroups(tileWorkgroups);
|
|
4319
|
+
recordDirectDispatch(parallelism, [tileWorkgroups]);
|
|
3798
4320
|
passEncoder.end();
|
|
3799
4321
|
}
|
|
3800
|
-
function encodeDenoise(encoder, configOffset) {
|
|
4322
|
+
function encodeDenoise(encoder, configOffset, parallelism) {
|
|
3801
4323
|
if (!config.denoise) {
|
|
3802
4324
|
return;
|
|
3803
4325
|
}
|
|
4326
|
+
const denoiseWorkgroupsX = Math.ceil(config.width / 8);
|
|
4327
|
+
const denoiseWorkgroupsY = Math.ceil(config.height / 8);
|
|
3804
4328
|
const radiancePass = encoder.beginComputePass({
|
|
3805
4329
|
label: "plasius.wavefront.denoiseRadiancePass"
|
|
3806
4330
|
});
|
|
3807
4331
|
radiancePass.setBindGroup(0, denoiseRadianceBindGroup, [configOffset]);
|
|
3808
4332
|
radiancePass.setPipeline(pipelines.denoiseLinearRadiance);
|
|
3809
|
-
radiancePass.dispatchWorkgroups(
|
|
4333
|
+
radiancePass.dispatchWorkgroups(denoiseWorkgroupsX, denoiseWorkgroupsY);
|
|
4334
|
+
recordDirectDispatch(parallelism, [denoiseWorkgroupsX, denoiseWorkgroupsY]);
|
|
3810
4335
|
radiancePass.end();
|
|
3811
4336
|
const resolvePass = encoder.beginComputePass({
|
|
3812
4337
|
label: "plasius.wavefront.denoiseResolvePass"
|
|
3813
4338
|
});
|
|
3814
4339
|
resolvePass.setBindGroup(0, denoiseResolveBindGroup, [configOffset]);
|
|
3815
4340
|
resolvePass.setPipeline(pipelines.resolveDenoisedOutputImage);
|
|
3816
|
-
resolvePass.dispatchWorkgroups(
|
|
4341
|
+
resolvePass.dispatchWorkgroups(denoiseWorkgroupsX, denoiseWorkgroupsY);
|
|
4342
|
+
recordDirectDispatch(parallelism, [denoiseWorkgroupsX, denoiseWorkgroupsY]);
|
|
3817
4343
|
resolvePass.end();
|
|
3818
4344
|
}
|
|
3819
4345
|
function encodePresent(encoder) {
|
|
@@ -3834,7 +4360,7 @@ async function createWavefrontPathTracingComputeRenderer(options = {}) {
|
|
|
3834
4360
|
passEncoder.draw(3);
|
|
3835
4361
|
passEncoder.end();
|
|
3836
4362
|
}
|
|
3837
|
-
function dispatchFrame(frameIndex) {
|
|
4363
|
+
function dispatchFrame(frameIndex, parallelism) {
|
|
3838
4364
|
const writeFrameConfig = createFrameConfigWriter(frameIndex);
|
|
3839
4365
|
let submissionCount = 0;
|
|
3840
4366
|
let encodedFramePasses = 0;
|
|
@@ -3865,20 +4391,25 @@ async function createWavefrontPathTracingComputeRenderer(options = {}) {
|
|
|
3865
4391
|
sampleIndex,
|
|
3866
4392
|
sampleWeight: 1 / config.samplesPerPixel
|
|
3867
4393
|
});
|
|
3868
|
-
encodeTileSample(reserveEncoder(), tile, configOffset);
|
|
4394
|
+
encodeTileSample(reserveEncoder(), tile, configOffset, parallelism);
|
|
4395
|
+
if (config.deferredPathResolve) {
|
|
4396
|
+
encodeTileOutput(reserveEncoder(), tile, configOffset, parallelism);
|
|
4397
|
+
}
|
|
4398
|
+
}
|
|
4399
|
+
if (!config.deferredPathResolve) {
|
|
4400
|
+
const outputConfigOffset = writeFrameConfig(tile, {
|
|
4401
|
+
sampleIndex: 0,
|
|
4402
|
+
sampleWeight: 1 / config.samplesPerPixel
|
|
4403
|
+
});
|
|
4404
|
+
encodeTileOutput(reserveEncoder(), tile, outputConfigOffset, parallelism);
|
|
3869
4405
|
}
|
|
3870
|
-
const outputConfigOffset = writeFrameConfig(tile, {
|
|
3871
|
-
sampleIndex: 0,
|
|
3872
|
-
sampleWeight: 1 / config.samplesPerPixel
|
|
3873
|
-
});
|
|
3874
|
-
encodeTileOutput(reserveEncoder(), tile, outputConfigOffset);
|
|
3875
4406
|
}
|
|
3876
4407
|
if (config.denoise) {
|
|
3877
4408
|
const denoiseConfigOffset = writeFrameConfig(
|
|
3878
4409
|
{ x: 0, y: 0, width: config.width, height: config.height },
|
|
3879
4410
|
{ sampleIndex: 0, sampleWeight: 1 / config.samplesPerPixel }
|
|
3880
4411
|
);
|
|
3881
|
-
encodeDenoise(reserveEncoder(), denoiseConfigOffset);
|
|
4412
|
+
encodeDenoise(reserveEncoder(), denoiseConfigOffset, parallelism);
|
|
3882
4413
|
}
|
|
3883
4414
|
encodePresent(reserveEncoder());
|
|
3884
4415
|
submitCurrentEncoder();
|
|
@@ -3887,8 +4418,10 @@ async function createWavefrontPathTracingComputeRenderer(options = {}) {
|
|
|
3887
4418
|
function renderOnce() {
|
|
3888
4419
|
frame += 1;
|
|
3889
4420
|
const frameIndex = frame + config.frameIndex;
|
|
3890
|
-
const
|
|
3891
|
-
const
|
|
4421
|
+
const parallelismCounters = createGpuParallelismCounters();
|
|
4422
|
+
const accelerationBuildSubmitted = dispatchGpuAccelerationBuild(frameIndex, parallelismCounters);
|
|
4423
|
+
const frameSubmissionCount = dispatchFrame(frameIndex, parallelismCounters);
|
|
4424
|
+
lastGpuParallelism = createGpuParallelismDiagnostics(gpuAdapterParallelism, parallelismCounters);
|
|
3892
4425
|
return Object.freeze({
|
|
3893
4426
|
frame,
|
|
3894
4427
|
width: config.width,
|
|
@@ -3905,6 +4438,8 @@ async function createWavefrontPathTracingComputeRenderer(options = {}) {
|
|
|
3905
4438
|
emissiveTriangleCount: config.emissiveTriangleCount,
|
|
3906
4439
|
environmentPortalCount: config.environmentPortalCount,
|
|
3907
4440
|
environmentPortalMode: config.environmentPortalMode,
|
|
4441
|
+
environmentMap: createEnvironmentMapSnapshot(config.environmentMap),
|
|
4442
|
+
deferredPathResolve: config.deferredPathResolve,
|
|
3908
4443
|
bvhNodeCount: config.bvhNodeCount,
|
|
3909
4444
|
displayQuality: config.displayQuality,
|
|
3910
4445
|
accelerationBuildMode: config.accelerationBuildMode,
|
|
@@ -3914,6 +4449,7 @@ async function createWavefrontPathTracingComputeRenderer(options = {}) {
|
|
|
3914
4449
|
accelerationBuildCount,
|
|
3915
4450
|
commandSubmissions: frameSubmissionCount + (accelerationBuildSubmitted ? 1 : 0),
|
|
3916
4451
|
frameConfigSlots: frameConfigSlotCount,
|
|
4452
|
+
gpuParallelism: lastGpuParallelism,
|
|
3917
4453
|
memory: config.memory
|
|
3918
4454
|
});
|
|
3919
4455
|
}
|
|
@@ -4022,6 +4558,8 @@ async function createWavefrontPathTracingComputeRenderer(options = {}) {
|
|
|
4022
4558
|
emissiveTriangleCount: config.emissiveTriangleCount,
|
|
4023
4559
|
environmentPortalCount: config.environmentPortalCount,
|
|
4024
4560
|
environmentPortalMode: config.environmentPortalMode,
|
|
4561
|
+
environmentMap: createEnvironmentMapSnapshot(config.environmentMap),
|
|
4562
|
+
deferredPathResolve: config.deferredPathResolve,
|
|
4025
4563
|
bvhNodeCount: config.bvhNodeCount,
|
|
4026
4564
|
displayQuality: config.displayQuality,
|
|
4027
4565
|
accelerationBuildMode: config.accelerationBuildMode,
|
|
@@ -4029,6 +4567,7 @@ async function createWavefrontPathTracingComputeRenderer(options = {}) {
|
|
|
4029
4567
|
accelerationBuilt,
|
|
4030
4568
|
accelerationBuildCount,
|
|
4031
4569
|
frameConfigSlots: frameConfigSlotCount,
|
|
4570
|
+
gpuParallelism: lastGpuParallelism,
|
|
4032
4571
|
memory: config.memory
|
|
4033
4572
|
});
|
|
4034
4573
|
}
|
|
@@ -4037,6 +4576,7 @@ async function createWavefrontPathTracingComputeRenderer(options = {}) {
|
|
|
4037
4576
|
nextQueue.destroy?.();
|
|
4038
4577
|
hitBuffer.destroy?.();
|
|
4039
4578
|
accumulationBuffer.destroy?.();
|
|
4579
|
+
pathVertexBuffer.destroy?.();
|
|
4040
4580
|
sceneObjectBuffer.destroy?.();
|
|
4041
4581
|
triangleBuffer.destroy?.();
|
|
4042
4582
|
bvhNodeBuffer.destroy?.();
|
|
@@ -4052,6 +4592,9 @@ async function createWavefrontPathTracingComputeRenderer(options = {}) {
|
|
|
4052
4592
|
radianceTexture.destroy?.();
|
|
4053
4593
|
denoiseScratchTexture.destroy?.();
|
|
4054
4594
|
outputTexture.destroy?.();
|
|
4595
|
+
if (environmentMapResource.ownsTexture) {
|
|
4596
|
+
environmentMapResource.texture?.destroy?.();
|
|
4597
|
+
}
|
|
4055
4598
|
context.unconfigure?.();
|
|
4056
4599
|
}
|
|
4057
4600
|
return Object.freeze({
|