@plasius/gpu-renderer 0.2.2 → 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 +28 -5
- package/README.md +36 -11
- package/dist/index.cjs +749 -71
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +749 -71
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/index.d.ts +88 -0
- package/src/wavefront-compute.js +785 -66
package/src/wavefront-compute.js
CHANGED
|
@@ -3,6 +3,7 @@ const DEFAULT_HEIGHT = 720;
|
|
|
3
3
|
const DEFAULT_MAX_DEPTH = 6;
|
|
4
4
|
const DEFAULT_TILE_SIZE = 128;
|
|
5
5
|
const DEFAULT_SAMPLES_PER_PIXEL = 1;
|
|
6
|
+
const DEFAULT_MAX_FRAME_PASSES_PER_SUBMISSION = 256;
|
|
6
7
|
const DEFAULT_SCENE_OBJECT_CAPACITY = 128;
|
|
7
8
|
const DEFAULT_ENVIRONMENT_PORTAL_CAPACITY = 32;
|
|
8
9
|
const WORKGROUP_SIZE = 64;
|
|
@@ -20,11 +21,12 @@ const BVH_LEAF_REF_RECORD_BYTES = 16;
|
|
|
20
21
|
const EMISSIVE_TRIANGLE_INDEX_BYTES = 4;
|
|
21
22
|
const ENVIRONMENT_PORTAL_RECORD_BYTES = 96;
|
|
22
23
|
const ACCUMULATION_RECORD_BYTES = 16;
|
|
23
|
-
const
|
|
24
|
+
const PATH_VERTEX_RECORD_BYTES = 16;
|
|
25
|
+
const CONFIG_BUFFER_BYTES = 304;
|
|
24
26
|
const COUNTER_DISPATCH_ARGS_OFFSET = 16;
|
|
25
27
|
const INDIRECT_DISPATCH_ARGS_BYTES = 12;
|
|
26
28
|
const COUNTER_BUFFER_BYTES = 32;
|
|
27
|
-
const TRACE_STORAGE_BUFFER_BINDINGS =
|
|
29
|
+
const TRACE_STORAGE_BUFFER_BINDINGS = 10;
|
|
28
30
|
const HIT_TYPE_SURFACE = 0;
|
|
29
31
|
const HIT_TYPE_EMISSIVE = 1;
|
|
30
32
|
const MATERIAL_DIFFUSE = 0;
|
|
@@ -52,6 +54,7 @@ const DEFAULT_ENVIRONMENT_LIGHTING = Object.freeze({
|
|
|
52
54
|
intensity: 1,
|
|
53
55
|
mode: 0,
|
|
54
56
|
exposure: 1,
|
|
57
|
+
sunlitBaseline: 0.16,
|
|
55
58
|
});
|
|
56
59
|
|
|
57
60
|
export const wavefrontPathTracingComputeLimits = Object.freeze({
|
|
@@ -69,6 +72,7 @@ export const wavefrontPathTracingComputeLimits = Object.freeze({
|
|
|
69
72
|
emissiveTriangleMetadataRecordBytes: BVH_NODE_RECORD_BYTES,
|
|
70
73
|
environmentPortalRecordBytes: ENVIRONMENT_PORTAL_RECORD_BYTES,
|
|
71
74
|
accumulationRecordBytes: ACCUMULATION_RECORD_BYTES,
|
|
75
|
+
pathVertexRecordBytes: PATH_VERTEX_RECORD_BYTES,
|
|
72
76
|
counterRecordBytes: COUNTER_BUFFER_BYTES,
|
|
73
77
|
indirectDispatchRecordBytes: INDIRECT_DISPATCH_ARGS_BYTES,
|
|
74
78
|
});
|
|
@@ -160,6 +164,39 @@ function asColor(value, fallback = [1, 1, 1, 1]) {
|
|
|
160
164
|
];
|
|
161
165
|
}
|
|
162
166
|
|
|
167
|
+
function resolveEnvironmentMap(input = null) {
|
|
168
|
+
const source = input && typeof input === "object" ? input : null;
|
|
169
|
+
const hasTexture = Boolean(source?.view || source?.texture || source?.data);
|
|
170
|
+
const width = readPositiveInteger("environmentMap.width", source?.width, 1);
|
|
171
|
+
const height = readPositiveInteger("environmentMap.height", source?.height, 1);
|
|
172
|
+
return Object.freeze({
|
|
173
|
+
enabled: hasTexture && source?.enabled !== false,
|
|
174
|
+
width,
|
|
175
|
+
height,
|
|
176
|
+
format: typeof source?.format === "string" ? source.format : "rgba16float",
|
|
177
|
+
projection: typeof source?.projection === "string" ? source.projection : "equirectangular",
|
|
178
|
+
texture: source?.texture ?? null,
|
|
179
|
+
view: source?.view ?? null,
|
|
180
|
+
sampler: source?.sampler ?? null,
|
|
181
|
+
data: source?.data ?? null,
|
|
182
|
+
intensity: Math.max(0, readFiniteNumber("environmentMap.intensity", source?.intensity ?? source?.radianceScale, 1)),
|
|
183
|
+
rotationRadians: readFiniteNumber("environmentMap.rotationRadians", source?.rotationRadians ?? source?.rotation, 0),
|
|
184
|
+
ambientStrength: Math.max(
|
|
185
|
+
0,
|
|
186
|
+
readFiniteNumber("environmentMap.ambientStrength", source?.ambientStrength, 0.32)
|
|
187
|
+
),
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
function resolveDeferredPathResolve(options = {}) {
|
|
192
|
+
const value =
|
|
193
|
+
options.deferredPathResolve ??
|
|
194
|
+
options.deferredResolve ??
|
|
195
|
+
options.pathResolve?.deferred ??
|
|
196
|
+
true;
|
|
197
|
+
return value !== false;
|
|
198
|
+
}
|
|
199
|
+
|
|
163
200
|
function emissionPower(emission) {
|
|
164
201
|
return Math.max(0, emission?.[0] ?? 0) + Math.max(0, emission?.[1] ?? 0) + Math.max(0, emission?.[2] ?? 0);
|
|
165
202
|
}
|
|
@@ -913,6 +950,14 @@ function resolveEnvironmentLighting(input, environmentColor, ambientColor) {
|
|
|
913
950
|
intensity: Math.max(0.0001, readFiniteNumber("environmentLighting.intensity", source.intensity, DEFAULT_ENVIRONMENT_LIGHTING.intensity)),
|
|
914
951
|
mode: readNonNegativeInteger("environmentLighting.mode", source.mode, DEFAULT_ENVIRONMENT_LIGHTING.mode),
|
|
915
952
|
exposure: Math.max(0.0001, readFiniteNumber("environmentLighting.exposure", source.exposure, DEFAULT_ENVIRONMENT_LIGHTING.exposure)),
|
|
953
|
+
sunlitBaseline: Math.max(
|
|
954
|
+
0,
|
|
955
|
+
readFiniteNumber(
|
|
956
|
+
"environmentLighting.sunlitBaseline",
|
|
957
|
+
source.sunlitBaseline ?? source.daylightBaseline,
|
|
958
|
+
DEFAULT_ENVIRONMENT_LIGHTING.sunlitBaseline
|
|
959
|
+
)
|
|
960
|
+
),
|
|
916
961
|
});
|
|
917
962
|
}
|
|
918
963
|
|
|
@@ -1116,6 +1161,11 @@ export function estimateWavefrontPathTracingMemory(options = {}) {
|
|
|
1116
1161
|
options.tilePixelCapacity,
|
|
1117
1162
|
DEFAULT_TILE_SIZE * DEFAULT_TILE_SIZE
|
|
1118
1163
|
);
|
|
1164
|
+
const maxDepth = clamp(
|
|
1165
|
+
readPositiveInteger("maxDepth", options.maxDepth, DEFAULT_MAX_DEPTH),
|
|
1166
|
+
1,
|
|
1167
|
+
16
|
|
1168
|
+
);
|
|
1119
1169
|
const sceneObjectCapacity = readPositiveInteger(
|
|
1120
1170
|
"sceneObjectCapacity",
|
|
1121
1171
|
options.sceneObjectCapacity,
|
|
@@ -1141,6 +1191,7 @@ export function estimateWavefrontPathTracingMemory(options = {}) {
|
|
|
1141
1191
|
const queueBytes = tilePixelCapacity * RAY_RECORD_BYTES;
|
|
1142
1192
|
const hitBytes = tilePixelCapacity * HIT_RECORD_BYTES;
|
|
1143
1193
|
const accumulationBytes = tilePixelCapacity * ACCUMULATION_RECORD_BYTES;
|
|
1194
|
+
const pathVertexBytes = tilePixelCapacity * (maxDepth + 1) * PATH_VERTEX_RECORD_BYTES;
|
|
1144
1195
|
const sceneObjectBytes = sceneObjectCapacity * SCENE_OBJECT_RECORD_BYTES;
|
|
1145
1196
|
const triangleBytes = triangleCapacity * TRIANGLE_RECORD_BYTES;
|
|
1146
1197
|
const bvhNodeBytes = bvhNodeCapacity * BVH_NODE_RECORD_BYTES;
|
|
@@ -1155,6 +1206,7 @@ export function estimateWavefrontPathTracingMemory(options = {}) {
|
|
|
1155
1206
|
queuePairBytes: queueBytes * 2,
|
|
1156
1207
|
hitBytes,
|
|
1157
1208
|
accumulationBytes,
|
|
1209
|
+
pathVertexBytes,
|
|
1158
1210
|
sceneObjectBytes,
|
|
1159
1211
|
triangleBytes,
|
|
1160
1212
|
bvhNodeBytes,
|
|
@@ -1168,6 +1220,7 @@ export function estimateWavefrontPathTracingMemory(options = {}) {
|
|
|
1168
1220
|
queueBytes * 2 +
|
|
1169
1221
|
hitBytes +
|
|
1170
1222
|
accumulationBytes +
|
|
1223
|
+
pathVertexBytes +
|
|
1171
1224
|
sceneObjectBytes +
|
|
1172
1225
|
triangleBytes +
|
|
1173
1226
|
bvhNodeBytes +
|
|
@@ -1193,6 +1246,15 @@ export function createWavefrontPathTracingComputeConfig(options = {}) {
|
|
|
1193
1246
|
1,
|
|
1194
1247
|
64
|
|
1195
1248
|
);
|
|
1249
|
+
const maxFramePassesPerSubmission = clamp(
|
|
1250
|
+
readPositiveInteger(
|
|
1251
|
+
"maxFramePassesPerSubmission",
|
|
1252
|
+
options.maxFramePassesPerSubmission,
|
|
1253
|
+
DEFAULT_MAX_FRAME_PASSES_PER_SUBMISSION
|
|
1254
|
+
),
|
|
1255
|
+
1,
|
|
1256
|
+
4096
|
|
1257
|
+
);
|
|
1196
1258
|
const tilePixelCapacity = readPositiveInteger(
|
|
1197
1259
|
"tilePixelCapacity",
|
|
1198
1260
|
options.tilePixelCapacity,
|
|
@@ -1276,6 +1338,12 @@ export function createWavefrontPathTracingComputeConfig(options = {}) {
|
|
|
1276
1338
|
options.environmentLighting?.environmentPortalMode,
|
|
1277
1339
|
environmentPortals.length > 0
|
|
1278
1340
|
);
|
|
1341
|
+
const environmentMap = resolveEnvironmentMap(
|
|
1342
|
+
options.environmentMap ??
|
|
1343
|
+
options.environmentTexture ??
|
|
1344
|
+
options.environmentLighting?.environmentMap
|
|
1345
|
+
);
|
|
1346
|
+
const deferredPathResolve = resolveDeferredPathResolve(options);
|
|
1279
1347
|
|
|
1280
1348
|
return Object.freeze({
|
|
1281
1349
|
mode: rendererWavefrontComputeMode,
|
|
@@ -1284,6 +1352,7 @@ export function createWavefrontPathTracingComputeConfig(options = {}) {
|
|
|
1284
1352
|
maxDepth,
|
|
1285
1353
|
tileSize,
|
|
1286
1354
|
samplesPerPixel,
|
|
1355
|
+
maxFramePassesPerSubmission,
|
|
1287
1356
|
tilePixelCapacity,
|
|
1288
1357
|
sceneObjects,
|
|
1289
1358
|
sceneObjectCount: sceneObjects.length,
|
|
@@ -1310,12 +1379,15 @@ export function createWavefrontPathTracingComputeConfig(options = {}) {
|
|
|
1310
1379
|
environmentPortalCount: environmentPortals.length,
|
|
1311
1380
|
environmentPortalCapacity,
|
|
1312
1381
|
environmentPortalMode,
|
|
1382
|
+
environmentMap,
|
|
1383
|
+
deferredPathResolve,
|
|
1313
1384
|
displayQuality: options.displayQuality === true,
|
|
1314
1385
|
requiresMeshBvhForDisplayQuality: true,
|
|
1315
1386
|
denoise: options.denoise !== false,
|
|
1316
1387
|
frameIndex: readNonNegativeInteger("frameIndex", options.frameIndex, 0),
|
|
1317
1388
|
memory: estimateWavefrontPathTracingMemory({
|
|
1318
1389
|
tilePixelCapacity,
|
|
1390
|
+
maxDepth,
|
|
1319
1391
|
sceneObjectCapacity,
|
|
1320
1392
|
triangleCapacity,
|
|
1321
1393
|
bvhNodeCapacity,
|
|
@@ -1539,6 +1611,18 @@ function createConfigPayload(config, tile, frameIndex, buildRange = {}) {
|
|
|
1539
1611
|
data.setUint32(260, config.environmentPortalMode ?? 0, true);
|
|
1540
1612
|
data.setUint32(264, 0, true);
|
|
1541
1613
|
data.setUint32(268, 0, true);
|
|
1614
|
+
writeVec4(floatView, 272, [
|
|
1615
|
+
config.environmentMap.enabled ? 1 : 0,
|
|
1616
|
+
config.environmentMap.intensity,
|
|
1617
|
+
config.environmentMap.rotationRadians,
|
|
1618
|
+
config.environmentMap.ambientStrength,
|
|
1619
|
+
]);
|
|
1620
|
+
writeVec4(floatView, 288, [
|
|
1621
|
+
config.deferredPathResolve ? 1 : 0,
|
|
1622
|
+
config.environmentLighting.sunlitBaseline,
|
|
1623
|
+
0,
|
|
1624
|
+
0,
|
|
1625
|
+
]);
|
|
1542
1626
|
return bytes;
|
|
1543
1627
|
}
|
|
1544
1628
|
|
|
@@ -1811,6 +1895,149 @@ function alignTo(value, alignment) {
|
|
|
1811
1895
|
return Math.ceil(value / resolvedAlignment) * resolvedAlignment;
|
|
1812
1896
|
}
|
|
1813
1897
|
|
|
1898
|
+
function float32ToFloat16Bits(value) {
|
|
1899
|
+
const floatView = new Float32Array(1);
|
|
1900
|
+
const intView = new Uint32Array(floatView.buffer);
|
|
1901
|
+
floatView[0] = Number.isFinite(value) ? value : 0;
|
|
1902
|
+
const x = intView[0];
|
|
1903
|
+
const sign = (x >> 16) & 0x8000;
|
|
1904
|
+
let mantissa = x & 0x7fffff;
|
|
1905
|
+
let exponent = (x >> 23) & 0xff;
|
|
1906
|
+
|
|
1907
|
+
if (exponent === 0xff) {
|
|
1908
|
+
return sign | (mantissa ? 0x7e00 : 0x7c00);
|
|
1909
|
+
}
|
|
1910
|
+
|
|
1911
|
+
exponent = exponent - 127 + 15;
|
|
1912
|
+
if (exponent >= 0x1f) {
|
|
1913
|
+
return sign | 0x7c00;
|
|
1914
|
+
}
|
|
1915
|
+
if (exponent <= 0) {
|
|
1916
|
+
if (exponent < -10) {
|
|
1917
|
+
return sign;
|
|
1918
|
+
}
|
|
1919
|
+
mantissa = (mantissa | 0x800000) >> (1 - exponent);
|
|
1920
|
+
return sign | ((mantissa + 0x1000) >> 13);
|
|
1921
|
+
}
|
|
1922
|
+
return sign | (exponent << 10) | ((mantissa + 0x1000) >> 13);
|
|
1923
|
+
}
|
|
1924
|
+
|
|
1925
|
+
function environmentMapIntegerScale(data) {
|
|
1926
|
+
if (data instanceof Uint8Array) {
|
|
1927
|
+
return 1 / 255;
|
|
1928
|
+
}
|
|
1929
|
+
if (data instanceof Uint16Array) {
|
|
1930
|
+
return 1 / 65535;
|
|
1931
|
+
}
|
|
1932
|
+
return 1;
|
|
1933
|
+
}
|
|
1934
|
+
|
|
1935
|
+
function readEnvironmentMapComponent(data, index, fallback, integerScale = 1) {
|
|
1936
|
+
if (!data || index >= data.length) {
|
|
1937
|
+
return fallback;
|
|
1938
|
+
}
|
|
1939
|
+
const value = Number(data[index]);
|
|
1940
|
+
return Number.isFinite(value) ? Math.max(0, value) * integerScale : fallback;
|
|
1941
|
+
}
|
|
1942
|
+
|
|
1943
|
+
function createEnvironmentMapUploadBytes(environmentMap, fallbackColor) {
|
|
1944
|
+
const width = Math.max(1, environmentMap.width);
|
|
1945
|
+
const height = Math.max(1, environmentMap.height);
|
|
1946
|
+
const rowBytes = width * 8;
|
|
1947
|
+
const bytesPerRow = alignTo(rowBytes, 256);
|
|
1948
|
+
const bytes = new Uint8Array(bytesPerRow * height);
|
|
1949
|
+
const data = environmentMap.data;
|
|
1950
|
+
const integerScale = environmentMapIntegerScale(data);
|
|
1951
|
+
const view = new DataView(bytes.buffer);
|
|
1952
|
+
const writeComponent = (targetOffset, sourceOffset, fallback) => {
|
|
1953
|
+
view.setUint16(
|
|
1954
|
+
targetOffset,
|
|
1955
|
+
float32ToFloat16Bits(
|
|
1956
|
+
readEnvironmentMapComponent(data, sourceOffset, fallback, integerScale)
|
|
1957
|
+
),
|
|
1958
|
+
true
|
|
1959
|
+
);
|
|
1960
|
+
};
|
|
1961
|
+
|
|
1962
|
+
for (let y = 0; y < height; y += 1) {
|
|
1963
|
+
for (let x = 0; x < width; x += 1) {
|
|
1964
|
+
const sourceOffset = (y * width + x) * 4;
|
|
1965
|
+
const targetOffset = y * bytesPerRow + x * 8;
|
|
1966
|
+
writeComponent(targetOffset, sourceOffset, fallbackColor[0]);
|
|
1967
|
+
writeComponent(targetOffset + 2, sourceOffset + 1, fallbackColor[1]);
|
|
1968
|
+
writeComponent(targetOffset + 4, sourceOffset + 2, fallbackColor[2]);
|
|
1969
|
+
writeComponent(targetOffset + 6, sourceOffset + 3, fallbackColor[3] ?? 1);
|
|
1970
|
+
}
|
|
1971
|
+
}
|
|
1972
|
+
|
|
1973
|
+
return Object.freeze({
|
|
1974
|
+
bytes,
|
|
1975
|
+
bytesPerRow,
|
|
1976
|
+
width,
|
|
1977
|
+
height,
|
|
1978
|
+
});
|
|
1979
|
+
}
|
|
1980
|
+
|
|
1981
|
+
function createEnvironmentMapResource(device, constants, environmentMap, fallbackColor) {
|
|
1982
|
+
if (environmentMap.view) {
|
|
1983
|
+
return Object.freeze({
|
|
1984
|
+
view: environmentMap.view,
|
|
1985
|
+
sampler: environmentMap.sampler ?? device.createSampler({
|
|
1986
|
+
label: "plasius.wavefront.environmentMapSampler",
|
|
1987
|
+
addressModeU: "repeat",
|
|
1988
|
+
addressModeV: "clamp-to-edge",
|
|
1989
|
+
magFilter: "linear",
|
|
1990
|
+
minFilter: "linear",
|
|
1991
|
+
}),
|
|
1992
|
+
texture: null,
|
|
1993
|
+
ownsTexture: false,
|
|
1994
|
+
});
|
|
1995
|
+
}
|
|
1996
|
+
|
|
1997
|
+
if (environmentMap.texture && typeof environmentMap.texture.createView === "function") {
|
|
1998
|
+
return Object.freeze({
|
|
1999
|
+
view: environmentMap.texture.createView(),
|
|
2000
|
+
sampler: environmentMap.sampler ?? device.createSampler({
|
|
2001
|
+
label: "plasius.wavefront.environmentMapSampler",
|
|
2002
|
+
addressModeU: "repeat",
|
|
2003
|
+
addressModeV: "clamp-to-edge",
|
|
2004
|
+
magFilter: "linear",
|
|
2005
|
+
minFilter: "linear",
|
|
2006
|
+
}),
|
|
2007
|
+
texture: environmentMap.texture,
|
|
2008
|
+
ownsTexture: false,
|
|
2009
|
+
});
|
|
2010
|
+
}
|
|
2011
|
+
|
|
2012
|
+
const upload = createEnvironmentMapUploadBytes(environmentMap, fallbackColor);
|
|
2013
|
+
const texture = device.createTexture({
|
|
2014
|
+
label: environmentMap.enabled
|
|
2015
|
+
? "plasius.wavefront.environmentMap"
|
|
2016
|
+
: "plasius.wavefront.environmentMapFallback",
|
|
2017
|
+
size: { width: upload.width, height: upload.height },
|
|
2018
|
+
format: "rgba16float",
|
|
2019
|
+
usage: constants.texture.TEXTURE_BINDING | constants.texture.COPY_DST,
|
|
2020
|
+
});
|
|
2021
|
+
device.queue.writeTexture(
|
|
2022
|
+
{ texture },
|
|
2023
|
+
upload.bytes,
|
|
2024
|
+
{ bytesPerRow: upload.bytesPerRow, rowsPerImage: upload.height },
|
|
2025
|
+
{ width: upload.width, height: upload.height, depthOrArrayLayers: 1 }
|
|
2026
|
+
);
|
|
2027
|
+
return Object.freeze({
|
|
2028
|
+
view: texture.createView(),
|
|
2029
|
+
sampler: environmentMap.sampler ?? device.createSampler({
|
|
2030
|
+
label: "plasius.wavefront.environmentMapSampler",
|
|
2031
|
+
addressModeU: "repeat",
|
|
2032
|
+
addressModeV: "clamp-to-edge",
|
|
2033
|
+
magFilter: "linear",
|
|
2034
|
+
minFilter: "linear",
|
|
2035
|
+
}),
|
|
2036
|
+
texture,
|
|
2037
|
+
ownsTexture: true,
|
|
2038
|
+
});
|
|
2039
|
+
}
|
|
2040
|
+
|
|
1814
2041
|
async function getPipelineDiagnostics(shaderModule) {
|
|
1815
2042
|
if (typeof shaderModule?.compilationInfo !== "function") {
|
|
1816
2043
|
return "";
|
|
@@ -2024,6 +2251,8 @@ struct FrameConfig {
|
|
|
2024
2251
|
environmentPortalMode: u32,
|
|
2025
2252
|
_portalPad0: u32,
|
|
2026
2253
|
_portalPad1: u32,
|
|
2254
|
+
environmentMapSettings: vec4<f32>,
|
|
2255
|
+
pathResolveSettings: vec4<f32>,
|
|
2027
2256
|
};
|
|
2028
2257
|
|
|
2029
2258
|
struct Counters {
|
|
@@ -2083,6 +2312,9 @@ struct EnvironmentPortal {
|
|
|
2083
2312
|
@group(0) @binding(17) var finalDenoiseInputRadiance: texture_2d<f32>;
|
|
2084
2313
|
@group(0) @binding(18) var denoisedOutputImage: texture_storage_2d<rgba8unorm, write>;
|
|
2085
2314
|
@group(0) @binding(19) var<storage, read> environmentPortals: array<EnvironmentPortal>;
|
|
2315
|
+
@group(0) @binding(20) var environmentMapTexture: texture_2d<f32>;
|
|
2316
|
+
@group(0) @binding(21) var environmentMapSampler: sampler;
|
|
2317
|
+
@group(0) @binding(22) var<storage, read_write> pathVertices: array<vec4<f32>>;
|
|
2086
2318
|
|
|
2087
2319
|
fn hash_u32(value: u32) -> u32 {
|
|
2088
2320
|
var x = value;
|
|
@@ -2127,6 +2359,89 @@ fn max_component(value: vec3<f32>) -> f32 {
|
|
|
2127
2359
|
return max(max(value.x, value.y), value.z);
|
|
2128
2360
|
}
|
|
2129
2361
|
|
|
2362
|
+
fn environment_map_enabled() -> bool {
|
|
2363
|
+
return config.environmentMapSettings.x > 0.5;
|
|
2364
|
+
}
|
|
2365
|
+
|
|
2366
|
+
fn deferred_path_resolve_enabled() -> bool {
|
|
2367
|
+
return config.pathResolveSettings.x > 0.5;
|
|
2368
|
+
}
|
|
2369
|
+
|
|
2370
|
+
fn path_vertex_count_per_ray() -> u32 {
|
|
2371
|
+
return config.maxDepth + 1u;
|
|
2372
|
+
}
|
|
2373
|
+
|
|
2374
|
+
fn path_vertex_index(rayId: u32, depth: u32) -> u32 {
|
|
2375
|
+
return rayId * path_vertex_count_per_ray() + min(depth, config.maxDepth);
|
|
2376
|
+
}
|
|
2377
|
+
|
|
2378
|
+
fn clear_deferred_path(rayId: u32) {
|
|
2379
|
+
if (!deferred_path_resolve_enabled()) {
|
|
2380
|
+
return;
|
|
2381
|
+
}
|
|
2382
|
+
|
|
2383
|
+
for (var depth = 0u; depth <= config.maxDepth; depth = depth + 1u) {
|
|
2384
|
+
pathVertices[path_vertex_index(rayId, depth)] = vec4<f32>(0.0);
|
|
2385
|
+
if (depth == config.maxDepth) {
|
|
2386
|
+
break;
|
|
2387
|
+
}
|
|
2388
|
+
}
|
|
2389
|
+
}
|
|
2390
|
+
|
|
2391
|
+
fn record_deferred_path_response(ray: RayRecord, response: vec3<f32>) {
|
|
2392
|
+
if (!deferred_path_resolve_enabled() || ray.rayId >= config.tilePixelCount || ray.bounce >= config.maxDepth) {
|
|
2393
|
+
return;
|
|
2394
|
+
}
|
|
2395
|
+
pathVertices[path_vertex_index(ray.rayId, ray.bounce)] =
|
|
2396
|
+
vec4<f32>(max(response, vec3<f32>(0.0)), 1.0);
|
|
2397
|
+
}
|
|
2398
|
+
|
|
2399
|
+
fn record_deferred_terminal_source(ray: RayRecord, sourceRadiance: vec3<f32>) {
|
|
2400
|
+
if (!deferred_path_resolve_enabled() || ray.rayId >= config.tilePixelCount) {
|
|
2401
|
+
return;
|
|
2402
|
+
}
|
|
2403
|
+
pathVertices[path_vertex_index(ray.rayId, config.maxDepth)] =
|
|
2404
|
+
vec4<f32>(clamp_sample_radiance(sourceRadiance), 1.0);
|
|
2405
|
+
}
|
|
2406
|
+
|
|
2407
|
+
fn environment_map_uv(direction: vec3<f32>) -> vec2<f32> {
|
|
2408
|
+
let rayDirection = safe_normalize(direction, vec3<f32>(0.0, 1.0, 0.0));
|
|
2409
|
+
let rotationTurns = config.environmentMapSettings.z / 6.28318530718;
|
|
2410
|
+
let u = fract(atan2(rayDirection.z, rayDirection.x) / 6.28318530718 + 0.5 + rotationTurns);
|
|
2411
|
+
let v = acos(clamp(rayDirection.y, -1.0, 1.0)) / 3.14159265359;
|
|
2412
|
+
return vec2<f32>(u, clamp(v, 0.0, 1.0));
|
|
2413
|
+
}
|
|
2414
|
+
|
|
2415
|
+
fn environment_map_radiance(direction: vec3<f32>) -> vec3<f32> {
|
|
2416
|
+
let uv = environment_map_uv(direction);
|
|
2417
|
+
let texel = max(textureSampleLevel(environmentMapTexture, environmentMapSampler, uv, 0.0).rgb, vec3<f32>(0.0));
|
|
2418
|
+
return texel * max(config.environmentMapSettings.y, 0.0);
|
|
2419
|
+
}
|
|
2420
|
+
|
|
2421
|
+
fn procedural_environment_radiance(direction: vec3<f32>) -> vec3<f32> {
|
|
2422
|
+
let rayDirection = safe_normalize(direction, vec3<f32>(0.0, 1.0, 0.0));
|
|
2423
|
+
let upFactor = saturate(rayDirection.y * 0.5 + 0.5);
|
|
2424
|
+
let sunDirection = safe_normalize(
|
|
2425
|
+
config.environmentSunDirectionIntensity.xyz,
|
|
2426
|
+
vec3<f32>(0.0, 1.0, 0.0)
|
|
2427
|
+
);
|
|
2428
|
+
let sunGlow = pow(saturate(dot(rayDirection, sunDirection)), 192.0);
|
|
2429
|
+
let gradient =
|
|
2430
|
+
config.environmentHorizonColor.xyz * (1.0 - upFactor) +
|
|
2431
|
+
config.environmentZenithColor.xyz * upFactor;
|
|
2432
|
+
return (
|
|
2433
|
+
gradient +
|
|
2434
|
+
config.environmentSunColor.xyz * sunGlow
|
|
2435
|
+
) * max(config.environmentSunDirectionIntensity.w, 0.0001);
|
|
2436
|
+
}
|
|
2437
|
+
|
|
2438
|
+
fn base_environment_radiance(direction: vec3<f32>) -> vec3<f32> {
|
|
2439
|
+
if (environment_map_enabled()) {
|
|
2440
|
+
return environment_map_radiance(direction);
|
|
2441
|
+
}
|
|
2442
|
+
return procedural_environment_radiance(direction);
|
|
2443
|
+
}
|
|
2444
|
+
|
|
2130
2445
|
fn environment_portal_radiance_scale(origin: vec3<f32>, direction: vec3<f32>) -> vec3<f32> {
|
|
2131
2446
|
if (config.environmentPortalCount == 0u || config.environmentPortalMode == 0u) {
|
|
2132
2447
|
return vec3<f32>(1.0);
|
|
@@ -2166,22 +2481,9 @@ fn environment_portal_radiance_scale(origin: vec3<f32>, direction: vec3<f32>) ->
|
|
|
2166
2481
|
|
|
2167
2482
|
fn environment_radiance(origin: vec3<f32>, direction: vec3<f32>) -> vec3<f32> {
|
|
2168
2483
|
let rayDirection = safe_normalize(direction, vec3<f32>(0.0, 1.0, 0.0));
|
|
2169
|
-
let upFactor = saturate(rayDirection.y * 0.5 + 0.5);
|
|
2170
|
-
let sunDirection = safe_normalize(
|
|
2171
|
-
config.environmentSunDirectionIntensity.xyz,
|
|
2172
|
-
vec3<f32>(0.0, 1.0, 0.0)
|
|
2173
|
-
);
|
|
2174
|
-
let sunGlow = pow(saturate(dot(rayDirection, sunDirection)), 192.0);
|
|
2175
|
-
let gradient =
|
|
2176
|
-
config.environmentHorizonColor.xyz * (1.0 - upFactor) +
|
|
2177
|
-
config.environmentZenithColor.xyz * upFactor;
|
|
2178
2484
|
let portalScale = environment_portal_radiance_scale(origin, rayDirection);
|
|
2179
2485
|
let portalHit = max_component(portalScale) > 0.0001;
|
|
2180
|
-
return (
|
|
2181
|
-
gradient +
|
|
2182
|
-
config.environmentSunColor.xyz * sunGlow
|
|
2183
|
-
) *
|
|
2184
|
-
max(config.environmentSunDirectionIntensity.w, 0.0001) *
|
|
2486
|
+
return base_environment_radiance(rayDirection) *
|
|
2185
2487
|
select(vec3<f32>(1.0), portalScale, portalHit);
|
|
2186
2488
|
}
|
|
2187
2489
|
|
|
@@ -2197,16 +2499,143 @@ fn gated_environment_radiance(origin: vec3<f32>, direction: vec3<f32>) -> vec3<f
|
|
|
2197
2499
|
return environment_radiance(origin, direction);
|
|
2198
2500
|
}
|
|
2199
2501
|
|
|
2200
|
-
fn
|
|
2502
|
+
fn surface_path_response(hit: HitRecord) -> vec3<f32> {
|
|
2503
|
+
let color = clamp(hit.color.xyz, vec3<f32>(0.0), vec3<f32>(1.0));
|
|
2504
|
+
let opacity = clamp(hit.material.z, 0.0, 1.0);
|
|
2505
|
+
let materialEnergy = select(0.68, 0.92, hit.materialKind == 1u || hit.materialKind == 2u);
|
|
2506
|
+
let transparentEnergy = select(materialEnergy, 0.9, hit.hitType == 3u);
|
|
2507
|
+
return mix(vec3<f32>(1.0), color, max(opacity, 0.18)) * transparentEnergy;
|
|
2508
|
+
}
|
|
2509
|
+
|
|
2510
|
+
fn sunlit_baseline_radiance(normal: vec3<f32>) -> vec3<f32> {
|
|
2511
|
+
let baseline = max(config.pathResolveSettings.y, 0.0);
|
|
2512
|
+
if (baseline <= 0.000001) {
|
|
2513
|
+
return vec3<f32>(0.0);
|
|
2514
|
+
}
|
|
2515
|
+
let sunDirection = safe_normalize(
|
|
2516
|
+
config.environmentSunDirectionIntensity.xyz,
|
|
2517
|
+
vec3<f32>(0.0, 1.0, 0.0)
|
|
2518
|
+
);
|
|
2519
|
+
let sunFacing = saturate(dot(normal, sunDirection));
|
|
2520
|
+
let skyFacing = 0.35 + saturate(normal.y * 0.5 + 0.5) * 0.65;
|
|
2521
|
+
let directionalWeight = 0.38 + sunFacing * 0.62;
|
|
2522
|
+
let sunTint = max(config.environmentSunColor.xyz, vec3<f32>(0.0));
|
|
2523
|
+
return clamp_sample_radiance(sunTint * baseline * skyFacing * directionalWeight * 0.04);
|
|
2524
|
+
}
|
|
2525
|
+
|
|
2526
|
+
fn terminal_surface_environment_source(hit: HitRecord) -> vec3<f32> {
|
|
2201
2527
|
let normal = safe_normalize(hit.shadingNormal.xyz, vec3<f32>(0.0, 1.0, 0.0));
|
|
2202
|
-
let surfaceColor = max(hit.color.xyz, config.ambientColor.xyz);
|
|
2203
2528
|
let normalEnvironment = gated_environment_radiance(
|
|
2204
2529
|
hit.position.xyz + normal * 0.003,
|
|
2205
2530
|
normal
|
|
2206
2531
|
);
|
|
2207
|
-
let
|
|
2532
|
+
let sunlitFloor = sunlit_baseline_radiance(normal);
|
|
2533
|
+
let ambientFloor = select(
|
|
2534
|
+
max(config.ambientColor.xyz, sunlitFloor * 0.82),
|
|
2535
|
+
max(config.ambientColor.xyz * 0.35, sunlitFloor * 0.58),
|
|
2536
|
+
environment_map_enabled()
|
|
2537
|
+
);
|
|
2538
|
+
let environmentInfluence = select(
|
|
2539
|
+
max(0.12, config.pathResolveSettings.y * 0.42),
|
|
2540
|
+
max(config.environmentMapSettings.w, max(0.12, config.pathResolveSettings.y * 0.42)),
|
|
2541
|
+
environment_map_enabled()
|
|
2542
|
+
);
|
|
2543
|
+
let environmentFloor = max(ambientFloor, max(sunlitFloor, normalEnvironment * environmentInfluence));
|
|
2208
2544
|
let materialFloor = select(0.7, 1.0, hit.materialKind == 0u || hit.materialKind == 3u);
|
|
2209
|
-
return clamp_sample_radiance(
|
|
2545
|
+
return clamp_sample_radiance(environmentFloor * materialFloor);
|
|
2546
|
+
}
|
|
2547
|
+
|
|
2548
|
+
fn terminal_surface_environment_contribution(ray: RayRecord, hit: HitRecord) -> vec3<f32> {
|
|
2549
|
+
let surfaceColor = max(hit.color.xyz, config.ambientColor.xyz);
|
|
2550
|
+
return clamp_sample_radiance(
|
|
2551
|
+
ray.throughput.xyz *
|
|
2552
|
+
surfaceColor *
|
|
2553
|
+
terminal_surface_environment_source(hit)
|
|
2554
|
+
);
|
|
2555
|
+
}
|
|
2556
|
+
|
|
2557
|
+
fn direct_environment_portal_irradiance(origin: vec3<f32>, normal: vec3<f32>) -> vec3<f32> {
|
|
2558
|
+
if (config.environmentPortalCount == 0u || config.environmentPortalMode == 0u) {
|
|
2559
|
+
return vec3<f32>(0.0);
|
|
2560
|
+
}
|
|
2561
|
+
|
|
2562
|
+
var irradiance = vec3<f32>(0.0);
|
|
2563
|
+
for (var portalIndex = 0u; portalIndex < config.environmentPortalCount; portalIndex = portalIndex + 1u) {
|
|
2564
|
+
let portal = environmentPortals[portalIndex];
|
|
2565
|
+
if (portal.kind != 1u) {
|
|
2566
|
+
continue;
|
|
2567
|
+
}
|
|
2568
|
+
|
|
2569
|
+
let toPortal = portal.position.xyz - origin;
|
|
2570
|
+
let distanceSquared = max(dot(toPortal, toPortal), 0.01);
|
|
2571
|
+
let direction = safe_normalize(toPortal, normal);
|
|
2572
|
+
let surfaceFacing = saturate(dot(normal, direction));
|
|
2573
|
+
if (surfaceFacing <= 0.0001) {
|
|
2574
|
+
continue;
|
|
2575
|
+
}
|
|
2576
|
+
|
|
2577
|
+
let portalNormal = safe_normalize(portal.normal.xyz, vec3<f32>(0.0, 0.0, 1.0));
|
|
2578
|
+
let twoSided = (portal.flags & 1u) != 0u;
|
|
2579
|
+
let portalFacing = select(
|
|
2580
|
+
saturate(dot(-direction, portalNormal)),
|
|
2581
|
+
max(abs(dot(direction, portalNormal)), 0.15),
|
|
2582
|
+
twoSided
|
|
2583
|
+
);
|
|
2584
|
+
let area = max(portal.position.w, 0.0001);
|
|
2585
|
+
let distanceFalloff = clamp(area / max(distanceSquared, area * 0.25), 0.0, 2.5);
|
|
2586
|
+
irradiance = irradiance +
|
|
2587
|
+
portal.color.rgb *
|
|
2588
|
+
portal.normal.w *
|
|
2589
|
+
portal.color.a *
|
|
2590
|
+
surfaceFacing *
|
|
2591
|
+
portalFacing *
|
|
2592
|
+
distanceFalloff;
|
|
2593
|
+
}
|
|
2594
|
+
return irradiance;
|
|
2595
|
+
}
|
|
2596
|
+
|
|
2597
|
+
fn surface_direct_environment_contribution(ray: RayRecord, hit: HitRecord) -> vec3<f32> {
|
|
2598
|
+
let normal = safe_normalize(hit.shadingNormal.xyz, vec3<f32>(0.0, 1.0, 0.0));
|
|
2599
|
+
let origin = hit.position.xyz + normal * 0.003;
|
|
2600
|
+
let viewDirection = safe_normalize(-ray.direction.xyz, normal);
|
|
2601
|
+
let surfaceColor = clamp(max(hit.color.xyz, config.ambientColor.xyz * 0.35), vec3<f32>(0.0), vec3<f32>(1.0));
|
|
2602
|
+
let roughness = clamp(hit.material.x, 0.0, 1.0);
|
|
2603
|
+
let metallic = clamp(hit.material.y, 0.0, 1.0);
|
|
2604
|
+
|
|
2605
|
+
let normalEnvironment = gated_environment_radiance(origin, normal);
|
|
2606
|
+
let skyVisibility = 0.35 + saturate(normal.y * 0.5 + 0.5) * 0.45;
|
|
2607
|
+
let sunlitFloor = sunlit_baseline_radiance(normal);
|
|
2608
|
+
let ambientIrradiance = max(
|
|
2609
|
+
select(config.ambientColor.xyz * 0.72, config.ambientColor.xyz * 0.28, environment_map_enabled()),
|
|
2610
|
+
sunlitFloor * select(0.72, 0.45, environment_map_enabled())
|
|
2611
|
+
);
|
|
2612
|
+
let environmentIrradianceScale = select(
|
|
2613
|
+
max(0.16, config.pathResolveSettings.y * 0.45),
|
|
2614
|
+
max(config.environmentMapSettings.w, max(0.16, config.pathResolveSettings.y * 0.45)),
|
|
2615
|
+
environment_map_enabled()
|
|
2616
|
+
);
|
|
2617
|
+
let skyIrradiance = max(ambientIrradiance, normalEnvironment * skyVisibility * environmentIrradianceScale);
|
|
2618
|
+
|
|
2619
|
+
let sunDirection = safe_normalize(
|
|
2620
|
+
config.environmentSunDirectionIntensity.xyz,
|
|
2621
|
+
vec3<f32>(0.0, 1.0, 0.0)
|
|
2622
|
+
);
|
|
2623
|
+
let sunFacing = saturate(dot(normal, sunDirection));
|
|
2624
|
+
let sunRadiance = gated_environment_radiance(origin, sunDirection);
|
|
2625
|
+
let sunIrradiance = sunRadiance * sunFacing * 0.2;
|
|
2626
|
+
let portalIrradiance = direct_environment_portal_irradiance(origin, normal);
|
|
2627
|
+
|
|
2628
|
+
let diffuseWeight = select(1.0 - metallic * 0.65, 0.22, hit.materialKind == 1u);
|
|
2629
|
+
let diffuse = surfaceColor * (skyIrradiance + sunIrradiance + portalIrradiance) * diffuseWeight;
|
|
2630
|
+
|
|
2631
|
+
let halfVector = safe_normalize(sunDirection + viewDirection, normal);
|
|
2632
|
+
let specularPower = 8.0 + (1.0 - roughness) * 96.0;
|
|
2633
|
+
let specularFacing = pow(saturate(dot(normal, halfVector)), specularPower) * sunFacing;
|
|
2634
|
+
let specularTint = mix(vec3<f32>(0.04), surfaceColor, metallic);
|
|
2635
|
+
let specular = specularTint * sunRadiance * specularFacing * select(0.16, 0.48, hit.materialKind == 1u || hit.materialKind == 2u);
|
|
2636
|
+
|
|
2637
|
+
let bounceWeight = select(1.0, 0.38, ray.bounce > 0u);
|
|
2638
|
+
return clamp_sample_radiance(ray.throughput.xyz * (diffuse + specular) * bounceWeight);
|
|
2210
2639
|
}
|
|
2211
2640
|
|
|
2212
2641
|
fn default_mesh_range() -> MeshRange {
|
|
@@ -2809,6 +3238,7 @@ fn generatePrimaryRays(@builtin(global_invocation_id) globalId: vec3<u32>) {
|
|
|
2809
3238
|
return;
|
|
2810
3239
|
}
|
|
2811
3240
|
activeQueue[index] = make_ray(index);
|
|
3241
|
+
clear_deferred_path(index);
|
|
2812
3242
|
if (u32(config.projectionAndSampling.w) == 0u) {
|
|
2813
3243
|
accumulation[index] = vec4<f32>(0.0);
|
|
2814
3244
|
}
|
|
@@ -3061,27 +3491,51 @@ fn resolveSurfaceRecords(@builtin(global_invocation_id) globalId: vec3<u32>) {
|
|
|
3061
3491
|
|
|
3062
3492
|
if (hit.hitType == 1u) {
|
|
3063
3493
|
let guidedLightWeight = select(1.0, 0.24, (ray.flags & RAY_FLAG_GUIDED_EMISSIVE) != 0u);
|
|
3064
|
-
|
|
3065
|
-
|
|
3066
|
-
|
|
3067
|
-
|
|
3068
|
-
|
|
3494
|
+
let sourceRadiance = max(hit.emission.xyz, hit.color.xyz) * guidedLightWeight;
|
|
3495
|
+
if (deferred_path_resolve_enabled()) {
|
|
3496
|
+
record_deferred_terminal_source(ray, sourceRadiance);
|
|
3497
|
+
} else {
|
|
3498
|
+
contribution = clamp_sample_radiance(ray.throughput.xyz * sourceRadiance);
|
|
3499
|
+
accumulation[ray.rayId] =
|
|
3500
|
+
accumulation[ray.rayId] + vec4<f32>(contribution * sample_weight(), 1.0);
|
|
3501
|
+
}
|
|
3069
3502
|
atomicAdd(&counters.terminatedCount, 1u);
|
|
3070
3503
|
return;
|
|
3071
3504
|
}
|
|
3072
3505
|
|
|
3073
3506
|
if (hit.hitType == 2u) {
|
|
3074
|
-
|
|
3075
|
-
|
|
3076
|
-
|
|
3507
|
+
if (deferred_path_resolve_enabled()) {
|
|
3508
|
+
record_deferred_terminal_source(ray, hit.color.xyz);
|
|
3509
|
+
} else {
|
|
3510
|
+
contribution = clamp_sample_radiance(ray.throughput.xyz * max(hit.color.xyz, config.ambientColor.xyz));
|
|
3511
|
+
accumulation[ray.rayId] =
|
|
3512
|
+
accumulation[ray.rayId] + vec4<f32>(contribution * sample_weight(), 1.0);
|
|
3513
|
+
}
|
|
3077
3514
|
atomicAdd(&counters.terminatedCount, 1u);
|
|
3078
3515
|
return;
|
|
3079
3516
|
}
|
|
3080
3517
|
|
|
3081
|
-
|
|
3082
|
-
|
|
3518
|
+
let response = surface_path_response(hit);
|
|
3519
|
+
record_deferred_path_response(ray, response);
|
|
3520
|
+
|
|
3521
|
+
let shouldEstimateDirectEnvironment =
|
|
3522
|
+
!deferred_path_resolve_enabled() &&
|
|
3523
|
+
(hit.materialKind == 0u || hit.materialKind == 1u) &&
|
|
3524
|
+
hit.material.z >= 0.95;
|
|
3525
|
+
if (shouldEstimateDirectEnvironment) {
|
|
3526
|
+
let directEnvironment = surface_direct_environment_contribution(ray, hit);
|
|
3083
3527
|
accumulation[ray.rayId] =
|
|
3084
|
-
accumulation[ray.rayId] + vec4<f32>(
|
|
3528
|
+
accumulation[ray.rayId] + vec4<f32>(directEnvironment * sample_weight(), 0.0);
|
|
3529
|
+
}
|
|
3530
|
+
|
|
3531
|
+
if (ray.bounce + 1u >= config.maxDepth) {
|
|
3532
|
+
if (deferred_path_resolve_enabled()) {
|
|
3533
|
+
record_deferred_terminal_source(ray, terminal_surface_environment_source(hit));
|
|
3534
|
+
} else {
|
|
3535
|
+
let terminalEnvironment = terminal_surface_environment_contribution(ray, hit);
|
|
3536
|
+
accumulation[ray.rayId] =
|
|
3537
|
+
accumulation[ray.rayId] + vec4<f32>(terminalEnvironment * sample_weight(), 1.0);
|
|
3538
|
+
}
|
|
3085
3539
|
atomicAdd(&counters.terminatedCount, 1u);
|
|
3086
3540
|
return;
|
|
3087
3541
|
}
|
|
@@ -3090,17 +3544,17 @@ fn resolveSurfaceRecords(@builtin(global_invocation_id) globalId: vec3<u32>) {
|
|
|
3090
3544
|
let scatter = scatter_direction(ray, hit, seed);
|
|
3091
3545
|
let nextIndex = atomicAdd(&counters.nextCount, 1u);
|
|
3092
3546
|
if (nextIndex >= config.tilePixelCount) {
|
|
3093
|
-
|
|
3094
|
-
|
|
3095
|
-
|
|
3547
|
+
if (deferred_path_resolve_enabled()) {
|
|
3548
|
+
record_deferred_terminal_source(ray, terminal_surface_environment_source(hit));
|
|
3549
|
+
} else {
|
|
3550
|
+
let overflowEnvironment = terminal_surface_environment_contribution(ray, hit);
|
|
3551
|
+
accumulation[ray.rayId] =
|
|
3552
|
+
accumulation[ray.rayId] + vec4<f32>(overflowEnvironment * sample_weight(), 1.0);
|
|
3553
|
+
}
|
|
3096
3554
|
atomicAdd(&counters.terminatedCount, 1u);
|
|
3097
3555
|
return;
|
|
3098
3556
|
}
|
|
3099
|
-
let
|
|
3100
|
-
let opacity = clamp(hit.material.z, 0.0, 1.0);
|
|
3101
|
-
let materialEnergy = select(0.68, 0.92, hit.materialKind == 1u || hit.materialKind == 2u);
|
|
3102
|
-
let transparentEnergy = select(materialEnergy, 0.9, hit.hitType == 3u);
|
|
3103
|
-
let throughput = ray.throughput.xyz * mix(vec3<f32>(1.0), color, max(opacity, 0.18)) * transparentEnergy;
|
|
3557
|
+
let throughput = ray.throughput.xyz * response;
|
|
3104
3558
|
nextQueue[nextIndex] = RayRecord(
|
|
3105
3559
|
ray.rayId,
|
|
3106
3560
|
ray.rayId,
|
|
@@ -3128,6 +3582,27 @@ fn compactAndSwapQueues(@builtin(global_invocation_id) globalId: vec3<u32>) {
|
|
|
3128
3582
|
write_active_dispatch_args(activeCount);
|
|
3129
3583
|
}
|
|
3130
3584
|
|
|
3585
|
+
fn resolve_deferred_path_radiance(rayId: u32) -> vec3<f32> {
|
|
3586
|
+
let terminal = pathVertices[path_vertex_index(rayId, config.maxDepth)];
|
|
3587
|
+
if (terminal.w <= 0.0) {
|
|
3588
|
+
return vec3<f32>(0.0);
|
|
3589
|
+
}
|
|
3590
|
+
|
|
3591
|
+
var radiance = terminal.xyz;
|
|
3592
|
+
var depth = config.maxDepth;
|
|
3593
|
+
loop {
|
|
3594
|
+
if (depth == 0u) {
|
|
3595
|
+
break;
|
|
3596
|
+
}
|
|
3597
|
+
depth = depth - 1u;
|
|
3598
|
+
let response = pathVertices[path_vertex_index(rayId, depth)];
|
|
3599
|
+
if (response.w > 0.0) {
|
|
3600
|
+
radiance = radiance * response.xyz;
|
|
3601
|
+
}
|
|
3602
|
+
}
|
|
3603
|
+
return clamp_sample_radiance(radiance);
|
|
3604
|
+
}
|
|
3605
|
+
|
|
3131
3606
|
@compute @workgroup_size(64)
|
|
3132
3607
|
fn accumulateTerminalRadiance(@builtin(global_invocation_id) globalId: vec3<u32>) {
|
|
3133
3608
|
let index = globalId.x;
|
|
@@ -3137,7 +3612,12 @@ fn accumulateTerminalRadiance(@builtin(global_invocation_id) globalId: vec3<u32>
|
|
|
3137
3612
|
let localX = index % config.tileWidth;
|
|
3138
3613
|
let localY = index / config.tileWidth;
|
|
3139
3614
|
let pixel = vec2<i32>(i32(config.tileX + localX), i32(config.tileY + localY));
|
|
3140
|
-
|
|
3615
|
+
var radiance = max(accumulation[index].xyz, vec3<f32>(0.0));
|
|
3616
|
+
if (deferred_path_resolve_enabled()) {
|
|
3617
|
+
let resolved = resolve_deferred_path_radiance(index) * sample_weight();
|
|
3618
|
+
radiance = clamp_sample_radiance(radiance + resolved);
|
|
3619
|
+
accumulation[index] = vec4<f32>(radiance, 1.0);
|
|
3620
|
+
}
|
|
3141
3621
|
|
|
3142
3622
|
textureStore(radianceImage, pixel, vec4<f32>(radiance, 1.0));
|
|
3143
3623
|
if (config.denoise == 0u) {
|
|
@@ -3280,6 +3760,137 @@ function createWavefrontDeviceDescriptor(adapter, options = {}) {
|
|
|
3280
3760
|
return Object.keys(descriptor).length > 0 ? descriptor : undefined;
|
|
3281
3761
|
}
|
|
3282
3762
|
|
|
3763
|
+
function readGpuLimit(adapter, device, name) {
|
|
3764
|
+
const adapterValue = Number(adapter?.limits?.[name]);
|
|
3765
|
+
if (Number.isFinite(adapterValue)) {
|
|
3766
|
+
return adapterValue;
|
|
3767
|
+
}
|
|
3768
|
+
const deviceValue = Number(device?.limits?.[name]);
|
|
3769
|
+
return Number.isFinite(deviceValue) ? deviceValue : null;
|
|
3770
|
+
}
|
|
3771
|
+
|
|
3772
|
+
function createAdapterInfoSnapshot(adapter) {
|
|
3773
|
+
const info = adapter?.info;
|
|
3774
|
+
if (!info || typeof info !== "object") {
|
|
3775
|
+
return null;
|
|
3776
|
+
}
|
|
3777
|
+
return Object.freeze({
|
|
3778
|
+
vendor: typeof info.vendor === "string" ? info.vendor : "",
|
|
3779
|
+
architecture: typeof info.architecture === "string" ? info.architecture : "",
|
|
3780
|
+
device: typeof info.device === "string" ? info.device : "",
|
|
3781
|
+
description: typeof info.description === "string" ? info.description : "",
|
|
3782
|
+
});
|
|
3783
|
+
}
|
|
3784
|
+
|
|
3785
|
+
function createGpuAdapterParallelismDiagnostics(adapter, device) {
|
|
3786
|
+
return Object.freeze({
|
|
3787
|
+
physicalCoreCount: null,
|
|
3788
|
+
physicalCoreCountAvailable: false,
|
|
3789
|
+
physicalCoreCountUnavailableReason: "WebGPU does not expose physical GPU core counts.",
|
|
3790
|
+
adapterInfo: createAdapterInfoSnapshot(adapter),
|
|
3791
|
+
adapterLimits: Object.freeze({
|
|
3792
|
+
maxComputeInvocationsPerWorkgroup: readGpuLimit(adapter, device, "maxComputeInvocationsPerWorkgroup"),
|
|
3793
|
+
maxComputeWorkgroupSizeX: readGpuLimit(adapter, device, "maxComputeWorkgroupSizeX"),
|
|
3794
|
+
maxComputeWorkgroupSizeY: readGpuLimit(adapter, device, "maxComputeWorkgroupSizeY"),
|
|
3795
|
+
maxComputeWorkgroupSizeZ: readGpuLimit(adapter, device, "maxComputeWorkgroupSizeZ"),
|
|
3796
|
+
maxComputeWorkgroupsPerDimension: readGpuLimit(adapter, device, "maxComputeWorkgroupsPerDimension"),
|
|
3797
|
+
maxStorageBuffersPerShaderStage: readGpuLimit(adapter, device, "maxStorageBuffersPerShaderStage"),
|
|
3798
|
+
maxStorageBufferBindingSize: readGpuLimit(adapter, device, "maxStorageBufferBindingSize"),
|
|
3799
|
+
}),
|
|
3800
|
+
configuredWorkgroupSize: WORKGROUP_SIZE,
|
|
3801
|
+
});
|
|
3802
|
+
}
|
|
3803
|
+
|
|
3804
|
+
function createGpuParallelismCounters() {
|
|
3805
|
+
return {
|
|
3806
|
+
directDispatches: 0,
|
|
3807
|
+
directWorkgroups: 0,
|
|
3808
|
+
directShaderInvocations: 0,
|
|
3809
|
+
multiWorkgroupDispatches: 0,
|
|
3810
|
+
largestDirectWorkgroupsPerDispatch: 0,
|
|
3811
|
+
indirectDispatches: 0,
|
|
3812
|
+
estimatedIndirectWorkgroupsUpperBound: 0,
|
|
3813
|
+
estimatedIndirectShaderInvocationsUpperBound: 0,
|
|
3814
|
+
indirectDispatchesWithMultiWorkgroupCapacity: 0,
|
|
3815
|
+
largestEstimatedIndirectWorkgroupsPerDispatch: 0,
|
|
3816
|
+
};
|
|
3817
|
+
}
|
|
3818
|
+
|
|
3819
|
+
function countDispatchWorkgroups(groups) {
|
|
3820
|
+
return groups.reduce((product, value) => {
|
|
3821
|
+
const numeric = Number(value ?? 1);
|
|
3822
|
+
const count = Number.isFinite(numeric) ? Math.max(1, Math.trunc(numeric)) : 1;
|
|
3823
|
+
return product * count;
|
|
3824
|
+
}, 1);
|
|
3825
|
+
}
|
|
3826
|
+
|
|
3827
|
+
function recordDirectDispatch(parallelism, groups, invocationsPerWorkgroup = WORKGROUP_SIZE) {
|
|
3828
|
+
const workgroups = countDispatchWorkgroups(groups);
|
|
3829
|
+
parallelism.directDispatches += 1;
|
|
3830
|
+
parallelism.directWorkgroups += workgroups;
|
|
3831
|
+
parallelism.directShaderInvocations += workgroups * invocationsPerWorkgroup;
|
|
3832
|
+
parallelism.largestDirectWorkgroupsPerDispatch = Math.max(
|
|
3833
|
+
parallelism.largestDirectWorkgroupsPerDispatch,
|
|
3834
|
+
workgroups
|
|
3835
|
+
);
|
|
3836
|
+
if (workgroups > 1) {
|
|
3837
|
+
parallelism.multiWorkgroupDispatches += 1;
|
|
3838
|
+
}
|
|
3839
|
+
}
|
|
3840
|
+
|
|
3841
|
+
function recordIndirectDispatch(parallelism, estimatedWorkgroupsUpperBound, invocationsPerWorkgroup = WORKGROUP_SIZE) {
|
|
3842
|
+
const workgroups = Math.max(1, Math.trunc(Number(estimatedWorkgroupsUpperBound) || 1));
|
|
3843
|
+
parallelism.indirectDispatches += 1;
|
|
3844
|
+
parallelism.estimatedIndirectWorkgroupsUpperBound += workgroups;
|
|
3845
|
+
parallelism.estimatedIndirectShaderInvocationsUpperBound += workgroups * invocationsPerWorkgroup;
|
|
3846
|
+
parallelism.largestEstimatedIndirectWorkgroupsPerDispatch = Math.max(
|
|
3847
|
+
parallelism.largestEstimatedIndirectWorkgroupsPerDispatch,
|
|
3848
|
+
workgroups
|
|
3849
|
+
);
|
|
3850
|
+
if (workgroups > 1) {
|
|
3851
|
+
parallelism.indirectDispatchesWithMultiWorkgroupCapacity += 1;
|
|
3852
|
+
}
|
|
3853
|
+
}
|
|
3854
|
+
|
|
3855
|
+
function createGpuParallelismDiagnostics(adapterDiagnostics, counters) {
|
|
3856
|
+
const totalEstimatedWorkgroupsUpperBound =
|
|
3857
|
+
counters.directWorkgroups + counters.estimatedIndirectWorkgroupsUpperBound;
|
|
3858
|
+
const totalEstimatedShaderInvocationsUpperBound =
|
|
3859
|
+
counters.directShaderInvocations + counters.estimatedIndirectShaderInvocationsUpperBound;
|
|
3860
|
+
const exposesMultiWorkgroupParallelism =
|
|
3861
|
+
counters.multiWorkgroupDispatches > 0 || counters.indirectDispatchesWithMultiWorkgroupCapacity > 0;
|
|
3862
|
+
return Object.freeze({
|
|
3863
|
+
...adapterDiagnostics,
|
|
3864
|
+
directDispatches: counters.directDispatches,
|
|
3865
|
+
directWorkgroups: counters.directWorkgroups,
|
|
3866
|
+
directShaderInvocations: counters.directShaderInvocations,
|
|
3867
|
+
multiWorkgroupDispatches: counters.multiWorkgroupDispatches,
|
|
3868
|
+
largestDirectWorkgroupsPerDispatch: counters.largestDirectWorkgroupsPerDispatch,
|
|
3869
|
+
indirectDispatches: counters.indirectDispatches,
|
|
3870
|
+
estimatedIndirectWorkgroupsUpperBound: counters.estimatedIndirectWorkgroupsUpperBound,
|
|
3871
|
+
estimatedIndirectShaderInvocationsUpperBound: counters.estimatedIndirectShaderInvocationsUpperBound,
|
|
3872
|
+
indirectDispatchesWithMultiWorkgroupCapacity: counters.indirectDispatchesWithMultiWorkgroupCapacity,
|
|
3873
|
+
largestEstimatedIndirectWorkgroupsPerDispatch: counters.largestEstimatedIndirectWorkgroupsPerDispatch,
|
|
3874
|
+
totalEstimatedWorkgroupsUpperBound,
|
|
3875
|
+
totalEstimatedShaderInvocationsUpperBound,
|
|
3876
|
+
exposesMultiWorkgroupParallelism,
|
|
3877
|
+
likelyUsesMoreThanOnePhysicalGpuCore: null,
|
|
3878
|
+
coreUtilizationStatus: "not-exposed-by-webgpu",
|
|
3879
|
+
});
|
|
3880
|
+
}
|
|
3881
|
+
|
|
3882
|
+
function createEnvironmentMapSnapshot(environmentMap) {
|
|
3883
|
+
return Object.freeze({
|
|
3884
|
+
enabled: environmentMap.enabled,
|
|
3885
|
+
width: environmentMap.width,
|
|
3886
|
+
height: environmentMap.height,
|
|
3887
|
+
projection: environmentMap.projection,
|
|
3888
|
+
intensity: environmentMap.intensity,
|
|
3889
|
+
rotationRadians: environmentMap.rotationRadians,
|
|
3890
|
+
ambientStrength: environmentMap.ambientStrength,
|
|
3891
|
+
});
|
|
3892
|
+
}
|
|
3893
|
+
|
|
3283
3894
|
export async function createWavefrontPathTracingComputeRenderer(options = {}) {
|
|
3284
3895
|
assertAnalyticDisplayQualityPolicy(options);
|
|
3285
3896
|
const constants = getGpuUsageConstants();
|
|
@@ -3301,6 +3912,7 @@ export async function createWavefrontPathTracingComputeRenderer(options = {}) {
|
|
|
3301
3912
|
}
|
|
3302
3913
|
|
|
3303
3914
|
const device = await adapter.requestDevice(createWavefrontDeviceDescriptor(adapter, options));
|
|
3915
|
+
const gpuAdapterParallelism = createGpuAdapterParallelismDiagnostics(adapter, device);
|
|
3304
3916
|
const context = canvas.getContext("webgpu");
|
|
3305
3917
|
if (!context || typeof context.configure !== "function") {
|
|
3306
3918
|
throw new Error("Canvas WebGPU context does not support configure().");
|
|
@@ -3334,6 +3946,7 @@ export async function createWavefrontPathTracingComputeRenderer(options = {}) {
|
|
|
3334
3946
|
const rayQueueBytes = config.tilePixelCapacity * RAY_RECORD_BYTES;
|
|
3335
3947
|
const hitBytes = config.tilePixelCapacity * HIT_RECORD_BYTES;
|
|
3336
3948
|
const accumulationBytes = config.tilePixelCapacity * ACCUMULATION_RECORD_BYTES;
|
|
3949
|
+
const pathVertexBytes = config.tilePixelCapacity * (config.maxDepth + 1) * PATH_VERTEX_RECORD_BYTES;
|
|
3337
3950
|
const activeQueue = createBuffer(device, bufferUsage, rayQueueBytes, "plasius.wavefront.activeQueue");
|
|
3338
3951
|
const nextQueue = createBuffer(device, bufferUsage, rayQueueBytes, "plasius.wavefront.nextQueue");
|
|
3339
3952
|
const hitBuffer = createBuffer(device, bufferUsage, hitBytes, "plasius.wavefront.hitBuffer");
|
|
@@ -3343,6 +3956,12 @@ export async function createWavefrontPathTracingComputeRenderer(options = {}) {
|
|
|
3343
3956
|
accumulationBytes,
|
|
3344
3957
|
"plasius.wavefront.accumulation"
|
|
3345
3958
|
);
|
|
3959
|
+
const pathVertexBuffer = createBuffer(
|
|
3960
|
+
device,
|
|
3961
|
+
bufferUsage,
|
|
3962
|
+
pathVertexBytes,
|
|
3963
|
+
"plasius.wavefront.pathVertices"
|
|
3964
|
+
);
|
|
3346
3965
|
const sceneObjectBuffer = createBuffer(
|
|
3347
3966
|
device,
|
|
3348
3967
|
constants.buffer.STORAGE | constants.buffer.COPY_DST,
|
|
@@ -3400,9 +4019,10 @@ export async function createWavefrontPathTracingComputeRenderer(options = {}) {
|
|
|
3400
4019
|
? uniformOffsetAlignment
|
|
3401
4020
|
: CONFIG_BUFFER_BYTES
|
|
3402
4021
|
);
|
|
4022
|
+
const outputConfigSlotCount = config.deferredPathResolve ? 0 : tiles.length;
|
|
3403
4023
|
const frameConfigSlotCount = Math.max(
|
|
3404
4024
|
1,
|
|
3405
|
-
tiles.length * config.samplesPerPixel +
|
|
4025
|
+
tiles.length * config.samplesPerPixel + outputConfigSlotCount + (config.denoise ? 1 : 0)
|
|
3406
4026
|
);
|
|
3407
4027
|
const configBuffer = createBuffer(
|
|
3408
4028
|
device,
|
|
@@ -3490,6 +4110,12 @@ export async function createWavefrontPathTracingComputeRenderer(options = {}) {
|
|
|
3490
4110
|
magFilter: "nearest",
|
|
3491
4111
|
minFilter: "nearest",
|
|
3492
4112
|
});
|
|
4113
|
+
const environmentMapResource = createEnvironmentMapResource(
|
|
4114
|
+
device,
|
|
4115
|
+
constants,
|
|
4116
|
+
config.environmentMap,
|
|
4117
|
+
config.environmentColor
|
|
4118
|
+
);
|
|
3493
4119
|
|
|
3494
4120
|
const traceBindGroupLayout = device.createBindGroupLayout({
|
|
3495
4121
|
label: "plasius.wavefront.traceBindGroupLayout",
|
|
@@ -3518,6 +4144,9 @@ export async function createWavefrontPathTracingComputeRenderer(options = {}) {
|
|
|
3518
4144
|
storageTexture: { access: "write-only", format: "rgba16float" },
|
|
3519
4145
|
},
|
|
3520
4146
|
{ binding: 19, visibility: constants.shader.COMPUTE, buffer: { type: "read-only-storage" } },
|
|
4147
|
+
{ binding: 20, visibility: constants.shader.COMPUTE, texture: { sampleType: "float" } },
|
|
4148
|
+
{ binding: 21, visibility: constants.shader.COMPUTE, sampler: { type: "filtering" } },
|
|
4149
|
+
{ binding: 22, visibility: constants.shader.COMPUTE, buffer: { type: "storage" } },
|
|
3521
4150
|
],
|
|
3522
4151
|
});
|
|
3523
4152
|
const accelerationBindGroupLayout = device.createBindGroupLayout({
|
|
@@ -3694,6 +4323,9 @@ export async function createWavefrontPathTracingComputeRenderer(options = {}) {
|
|
|
3694
4323
|
{ binding: 9, resource: { buffer: bvhNodeBuffer } },
|
|
3695
4324
|
{ binding: 16, resource: radianceView },
|
|
3696
4325
|
{ binding: 19, resource: { buffer: environmentPortalBuffer } },
|
|
4326
|
+
{ binding: 20, resource: environmentMapResource.view },
|
|
4327
|
+
{ binding: 21, resource: environmentMapResource.sampler },
|
|
4328
|
+
{ binding: 22, resource: { buffer: pathVertexBuffer } },
|
|
3697
4329
|
],
|
|
3698
4330
|
});
|
|
3699
4331
|
}
|
|
@@ -3787,6 +4419,11 @@ export async function createWavefrontPathTracingComputeRenderer(options = {}) {
|
|
|
3787
4419
|
let frame = 0;
|
|
3788
4420
|
let accelerationBuilt = !config.gpuAccelerationBuildRequired;
|
|
3789
4421
|
let accelerationBuildCount = 0;
|
|
4422
|
+
let activeCameraOptions = options.camera ?? DEFAULT_CAMERA;
|
|
4423
|
+
let lastGpuParallelism = createGpuParallelismDiagnostics(
|
|
4424
|
+
gpuAdapterParallelism,
|
|
4425
|
+
createGpuParallelismCounters()
|
|
4426
|
+
);
|
|
3790
4427
|
|
|
3791
4428
|
function createFrameConfigWriter(frameIndex) {
|
|
3792
4429
|
let slot = 0;
|
|
@@ -3805,7 +4442,7 @@ export async function createWavefrontPathTracingComputeRenderer(options = {}) {
|
|
|
3805
4442
|
};
|
|
3806
4443
|
}
|
|
3807
4444
|
|
|
3808
|
-
function dispatchGpuAccelerationBuild(frameIndex) {
|
|
4445
|
+
function dispatchGpuAccelerationBuild(frameIndex, parallelism) {
|
|
3809
4446
|
if (!config.gpuAccelerationBuildRequired || accelerationBuilt) {
|
|
3810
4447
|
return false;
|
|
3811
4448
|
}
|
|
@@ -3844,24 +4481,32 @@ export async function createWavefrontPathTracingComputeRenderer(options = {}) {
|
|
|
3844
4481
|
});
|
|
3845
4482
|
passEncoder.setBindGroup(0, bvhBuildBindGroup, [0]);
|
|
3846
4483
|
passEncoder.setPipeline(pipelines.prepareMeshTrianglesAndLeaves);
|
|
3847
|
-
|
|
4484
|
+
const prepareWorkgroups = Math.ceil(config.bvhLeafSortCapacity / WORKGROUP_SIZE);
|
|
4485
|
+
passEncoder.dispatchWorkgroups(prepareWorkgroups);
|
|
4486
|
+
recordDirectDispatch(parallelism, [prepareWorkgroups]);
|
|
3848
4487
|
passEncoder.setPipeline(pipelines.sortBvhLeafRefs);
|
|
3849
4488
|
for (let stageIndex = 0; stageIndex < config.bvhSortStages.length; stageIndex += 1) {
|
|
3850
4489
|
passEncoder.setBindGroup(0, bvhBuildBindGroup, [
|
|
3851
4490
|
(stageIndex + 1) * configBufferStride,
|
|
3852
4491
|
]);
|
|
3853
|
-
|
|
4492
|
+
const sortWorkgroups = Math.ceil(config.bvhLeafSortCapacity / WORKGROUP_SIZE);
|
|
4493
|
+
passEncoder.dispatchWorkgroups(sortWorkgroups);
|
|
4494
|
+
recordDirectDispatch(parallelism, [sortWorkgroups]);
|
|
3854
4495
|
}
|
|
3855
4496
|
passEncoder.setBindGroup(0, bvhBuildBindGroup, [0]);
|
|
3856
4497
|
passEncoder.setPipeline(pipelines.writeSortedBvhLeaves);
|
|
3857
|
-
|
|
4498
|
+
const leafWriteWorkgroups = Math.ceil(config.triangleCount / WORKGROUP_SIZE);
|
|
4499
|
+
passEncoder.dispatchWorkgroups(leafWriteWorkgroups);
|
|
4500
|
+
recordDirectDispatch(parallelism, [leafWriteWorkgroups]);
|
|
3858
4501
|
passEncoder.setPipeline(pipelines.buildBvhInternalLevel);
|
|
3859
4502
|
for (let levelIndex = 0; levelIndex < config.bvhBuildLevels.length; levelIndex += 1) {
|
|
3860
4503
|
const buildLevel = config.bvhBuildLevels[levelIndex];
|
|
3861
4504
|
passEncoder.setBindGroup(0, bvhBuildBindGroup, [
|
|
3862
4505
|
(buildLevelConfigStart + levelIndex) * configBufferStride,
|
|
3863
4506
|
]);
|
|
3864
|
-
|
|
4507
|
+
const levelWorkgroups = Math.ceil(buildLevel.count / WORKGROUP_SIZE);
|
|
4508
|
+
passEncoder.dispatchWorkgroups(levelWorkgroups);
|
|
4509
|
+
recordDirectDispatch(parallelism, [levelWorkgroups]);
|
|
3865
4510
|
}
|
|
3866
4511
|
passEncoder.end();
|
|
3867
4512
|
device.queue.submit([encoder.finish()]);
|
|
@@ -3870,7 +4515,7 @@ export async function createWavefrontPathTracingComputeRenderer(options = {}) {
|
|
|
3870
4515
|
return true;
|
|
3871
4516
|
}
|
|
3872
4517
|
|
|
3873
|
-
function encodeTileSample(encoder, tile, configOffset) {
|
|
4518
|
+
function encodeTileSample(encoder, tile, configOffset, parallelism) {
|
|
3874
4519
|
const generatePass = encoder.beginComputePass({
|
|
3875
4520
|
label: "plasius.wavefront.generatePrimaryRaysPass",
|
|
3876
4521
|
});
|
|
@@ -3879,6 +4524,7 @@ export async function createWavefrontPathTracingComputeRenderer(options = {}) {
|
|
|
3879
4524
|
generatePass.setBindGroup(0, bindGroups[0], [configOffset]);
|
|
3880
4525
|
generatePass.setPipeline(pipelines.generatePrimaryRays);
|
|
3881
4526
|
generatePass.dispatchWorkgroups(tileWorkgroups);
|
|
4527
|
+
recordDirectDispatch(parallelism, [tileWorkgroups]);
|
|
3882
4528
|
generatePass.end();
|
|
3883
4529
|
|
|
3884
4530
|
for (let bounceIndex = 0; bounceIndex < config.maxDepth; bounceIndex += 1) {
|
|
@@ -3895,15 +4541,18 @@ export async function createWavefrontPathTracingComputeRenderer(options = {}) {
|
|
|
3895
4541
|
passEncoder.setBindGroup(0, bindGroups[bounceIndex % 2], [configOffset]);
|
|
3896
4542
|
passEncoder.setPipeline(pipelines.intersectActiveQueue);
|
|
3897
4543
|
passEncoder.dispatchWorkgroupsIndirect(activeDispatchBuffer, 0);
|
|
4544
|
+
recordIndirectDispatch(parallelism, tileWorkgroups);
|
|
3898
4545
|
passEncoder.setPipeline(pipelines.resolveSurfaceRecords);
|
|
3899
4546
|
passEncoder.dispatchWorkgroupsIndirect(activeDispatchBuffer, 0);
|
|
4547
|
+
recordIndirectDispatch(parallelism, tileWorkgroups);
|
|
3900
4548
|
passEncoder.setPipeline(pipelines.compactAndSwapQueues);
|
|
3901
4549
|
passEncoder.dispatchWorkgroups(1);
|
|
4550
|
+
recordDirectDispatch(parallelism, [1], 1);
|
|
3902
4551
|
passEncoder.end();
|
|
3903
4552
|
}
|
|
3904
4553
|
}
|
|
3905
4554
|
|
|
3906
|
-
function encodeTileOutput(encoder, tile, configOffset) {
|
|
4555
|
+
function encodeTileOutput(encoder, tile, configOffset, parallelism) {
|
|
3907
4556
|
const passEncoder = encoder.beginComputePass({
|
|
3908
4557
|
label: "plasius.wavefront.outputPass",
|
|
3909
4558
|
});
|
|
@@ -3912,19 +4561,23 @@ export async function createWavefrontPathTracingComputeRenderer(options = {}) {
|
|
|
3912
4561
|
passEncoder.setBindGroup(0, bindGroups[0], [configOffset]);
|
|
3913
4562
|
passEncoder.setPipeline(pipelines.accumulateTerminalRadiance);
|
|
3914
4563
|
passEncoder.dispatchWorkgroups(tileWorkgroups);
|
|
4564
|
+
recordDirectDispatch(parallelism, [tileWorkgroups]);
|
|
3915
4565
|
passEncoder.end();
|
|
3916
4566
|
}
|
|
3917
4567
|
|
|
3918
|
-
function encodeDenoise(encoder, configOffset) {
|
|
4568
|
+
function encodeDenoise(encoder, configOffset, parallelism) {
|
|
3919
4569
|
if (!config.denoise) {
|
|
3920
4570
|
return;
|
|
3921
4571
|
}
|
|
4572
|
+
const denoiseWorkgroupsX = Math.ceil(config.width / 8);
|
|
4573
|
+
const denoiseWorkgroupsY = Math.ceil(config.height / 8);
|
|
3922
4574
|
const radiancePass = encoder.beginComputePass({
|
|
3923
4575
|
label: "plasius.wavefront.denoiseRadiancePass",
|
|
3924
4576
|
});
|
|
3925
4577
|
radiancePass.setBindGroup(0, denoiseRadianceBindGroup, [configOffset]);
|
|
3926
4578
|
radiancePass.setPipeline(pipelines.denoiseLinearRadiance);
|
|
3927
|
-
radiancePass.dispatchWorkgroups(
|
|
4579
|
+
radiancePass.dispatchWorkgroups(denoiseWorkgroupsX, denoiseWorkgroupsY);
|
|
4580
|
+
recordDirectDispatch(parallelism, [denoiseWorkgroupsX, denoiseWorkgroupsY]);
|
|
3928
4581
|
radiancePass.end();
|
|
3929
4582
|
|
|
3930
4583
|
const resolvePass = encoder.beginComputePass({
|
|
@@ -3932,7 +4585,8 @@ export async function createWavefrontPathTracingComputeRenderer(options = {}) {
|
|
|
3932
4585
|
});
|
|
3933
4586
|
resolvePass.setBindGroup(0, denoiseResolveBindGroup, [configOffset]);
|
|
3934
4587
|
resolvePass.setPipeline(pipelines.resolveDenoisedOutputImage);
|
|
3935
|
-
resolvePass.dispatchWorkgroups(
|
|
4588
|
+
resolvePass.dispatchWorkgroups(denoiseWorkgroupsX, denoiseWorkgroupsY);
|
|
4589
|
+
recordDirectDispatch(parallelism, [denoiseWorkgroupsX, denoiseWorkgroupsY]);
|
|
3936
4590
|
resolvePass.end();
|
|
3937
4591
|
}
|
|
3938
4592
|
|
|
@@ -3955,42 +4609,75 @@ export async function createWavefrontPathTracingComputeRenderer(options = {}) {
|
|
|
3955
4609
|
passEncoder.end();
|
|
3956
4610
|
}
|
|
3957
4611
|
|
|
3958
|
-
function dispatchFrame(frameIndex) {
|
|
4612
|
+
function dispatchFrame(frameIndex, parallelism) {
|
|
3959
4613
|
const writeFrameConfig = createFrameConfigWriter(frameIndex);
|
|
3960
|
-
|
|
3961
|
-
|
|
4614
|
+
let submissionCount = 0;
|
|
4615
|
+
let encodedFramePasses = 0;
|
|
4616
|
+
let encoder = device.createCommandEncoder({
|
|
4617
|
+
label: `plasius.wavefront.frame.${frameIndex}.batched.${submissionCount + 1}`,
|
|
3962
4618
|
});
|
|
4619
|
+
|
|
4620
|
+
function submitCurrentEncoder() {
|
|
4621
|
+
if (encodedFramePasses <= 0) {
|
|
4622
|
+
return;
|
|
4623
|
+
}
|
|
4624
|
+
device.queue.submit([encoder.finish()]);
|
|
4625
|
+
submissionCount += 1;
|
|
4626
|
+
encodedFramePasses = 0;
|
|
4627
|
+
encoder = device.createCommandEncoder({
|
|
4628
|
+
label: `plasius.wavefront.frame.${frameIndex}.batched.${submissionCount + 1}`,
|
|
4629
|
+
});
|
|
4630
|
+
}
|
|
4631
|
+
|
|
4632
|
+
function reserveEncoder(passCount = 1) {
|
|
4633
|
+
if (
|
|
4634
|
+
encodedFramePasses > 0 &&
|
|
4635
|
+
encodedFramePasses + passCount > config.maxFramePassesPerSubmission
|
|
4636
|
+
) {
|
|
4637
|
+
submitCurrentEncoder();
|
|
4638
|
+
}
|
|
4639
|
+
encodedFramePasses += passCount;
|
|
4640
|
+
return encoder;
|
|
4641
|
+
}
|
|
4642
|
+
|
|
3963
4643
|
for (const tile of tiles) {
|
|
3964
4644
|
for (let sampleIndex = 0; sampleIndex < config.samplesPerPixel; sampleIndex += 1) {
|
|
3965
4645
|
const configOffset = writeFrameConfig(tile, {
|
|
3966
4646
|
sampleIndex,
|
|
3967
4647
|
sampleWeight: 1 / config.samplesPerPixel,
|
|
3968
4648
|
});
|
|
3969
|
-
encodeTileSample(
|
|
4649
|
+
encodeTileSample(reserveEncoder(), tile, configOffset, parallelism);
|
|
4650
|
+
if (config.deferredPathResolve) {
|
|
4651
|
+
encodeTileOutput(reserveEncoder(), tile, configOffset, parallelism);
|
|
4652
|
+
}
|
|
4653
|
+
}
|
|
4654
|
+
if (!config.deferredPathResolve) {
|
|
4655
|
+
const outputConfigOffset = writeFrameConfig(tile, {
|
|
4656
|
+
sampleIndex: 0,
|
|
4657
|
+
sampleWeight: 1 / config.samplesPerPixel,
|
|
4658
|
+
});
|
|
4659
|
+
encodeTileOutput(reserveEncoder(), tile, outputConfigOffset, parallelism);
|
|
3970
4660
|
}
|
|
3971
|
-
const outputConfigOffset = writeFrameConfig(tile, {
|
|
3972
|
-
sampleIndex: 0,
|
|
3973
|
-
sampleWeight: 1 / config.samplesPerPixel,
|
|
3974
|
-
});
|
|
3975
|
-
encodeTileOutput(encoder, tile, outputConfigOffset);
|
|
3976
4661
|
}
|
|
3977
4662
|
if (config.denoise) {
|
|
3978
4663
|
const denoiseConfigOffset = writeFrameConfig(
|
|
3979
4664
|
{ x: 0, y: 0, width: config.width, height: config.height },
|
|
3980
4665
|
{ sampleIndex: 0, sampleWeight: 1 / config.samplesPerPixel }
|
|
3981
4666
|
);
|
|
3982
|
-
encodeDenoise(
|
|
4667
|
+
encodeDenoise(reserveEncoder(), denoiseConfigOffset, parallelism);
|
|
3983
4668
|
}
|
|
3984
|
-
encodePresent(
|
|
3985
|
-
|
|
3986
|
-
return
|
|
4669
|
+
encodePresent(reserveEncoder());
|
|
4670
|
+
submitCurrentEncoder();
|
|
4671
|
+
return submissionCount;
|
|
3987
4672
|
}
|
|
3988
4673
|
|
|
3989
4674
|
function renderOnce() {
|
|
3990
4675
|
frame += 1;
|
|
3991
4676
|
const frameIndex = frame + config.frameIndex;
|
|
3992
|
-
const
|
|
3993
|
-
const
|
|
4677
|
+
const parallelismCounters = createGpuParallelismCounters();
|
|
4678
|
+
const accelerationBuildSubmitted = dispatchGpuAccelerationBuild(frameIndex, parallelismCounters);
|
|
4679
|
+
const frameSubmissionCount = dispatchFrame(frameIndex, parallelismCounters);
|
|
4680
|
+
lastGpuParallelism = createGpuParallelismDiagnostics(gpuAdapterParallelism, parallelismCounters);
|
|
3994
4681
|
return Object.freeze({
|
|
3995
4682
|
frame,
|
|
3996
4683
|
width: config.width,
|
|
@@ -3999,6 +4686,7 @@ export async function createWavefrontPathTracingComputeRenderer(options = {}) {
|
|
|
3999
4686
|
tiles: tiles.length,
|
|
4000
4687
|
tileSize: config.tileSize,
|
|
4001
4688
|
samplesPerPixel: config.samplesPerPixel,
|
|
4689
|
+
maxFramePassesPerSubmission: config.maxFramePassesPerSubmission,
|
|
4002
4690
|
screenRays: config.width * config.height,
|
|
4003
4691
|
primaryRays: config.width * config.height * config.samplesPerPixel,
|
|
4004
4692
|
sceneObjectCount: config.sceneObjectCount,
|
|
@@ -4006,6 +4694,8 @@ export async function createWavefrontPathTracingComputeRenderer(options = {}) {
|
|
|
4006
4694
|
emissiveTriangleCount: config.emissiveTriangleCount,
|
|
4007
4695
|
environmentPortalCount: config.environmentPortalCount,
|
|
4008
4696
|
environmentPortalMode: config.environmentPortalMode,
|
|
4697
|
+
environmentMap: createEnvironmentMapSnapshot(config.environmentMap),
|
|
4698
|
+
deferredPathResolve: config.deferredPathResolve,
|
|
4009
4699
|
bvhNodeCount: config.bvhNodeCount,
|
|
4010
4700
|
displayQuality: config.displayQuality,
|
|
4011
4701
|
accelerationBuildMode: config.accelerationBuildMode,
|
|
@@ -4015,6 +4705,7 @@ export async function createWavefrontPathTracingComputeRenderer(options = {}) {
|
|
|
4015
4705
|
accelerationBuildCount,
|
|
4016
4706
|
commandSubmissions: frameSubmissionCount + (accelerationBuildSubmitted ? 1 : 0),
|
|
4017
4707
|
frameConfigSlots: frameConfigSlotCount,
|
|
4708
|
+
gpuParallelism: lastGpuParallelism,
|
|
4018
4709
|
memory: config.memory,
|
|
4019
4710
|
});
|
|
4020
4711
|
}
|
|
@@ -4091,12 +4782,31 @@ export async function createWavefrontPathTracingComputeRenderer(options = {}) {
|
|
|
4091
4782
|
samplesPerPixel: config.samplesPerPixel,
|
|
4092
4783
|
sceneObjectCapacity: config.sceneObjectCapacity,
|
|
4093
4784
|
sceneObjects: packedScene.objects,
|
|
4785
|
+
camera: activeCameraOptions,
|
|
4094
4786
|
frameIndex: config.frameIndex,
|
|
4095
4787
|
});
|
|
4096
4788
|
device.queue.writeBuffer(sceneObjectBuffer, 0, packedScene.buffer);
|
|
4097
4789
|
return config;
|
|
4098
4790
|
}
|
|
4099
4791
|
|
|
4792
|
+
function updateCamera(cameraOptions = {}) {
|
|
4793
|
+
activeCameraOptions = cameraOptions;
|
|
4794
|
+
config = createWavefrontPathTracingComputeConfig({
|
|
4795
|
+
...options,
|
|
4796
|
+
canvas,
|
|
4797
|
+
width: config.width,
|
|
4798
|
+
height: config.height,
|
|
4799
|
+
maxDepth: config.maxDepth,
|
|
4800
|
+
tileSize: config.tileSize,
|
|
4801
|
+
samplesPerPixel: config.samplesPerPixel,
|
|
4802
|
+
sceneObjectCapacity: config.sceneObjectCapacity,
|
|
4803
|
+
sceneObjects: packedScene.objects,
|
|
4804
|
+
camera: activeCameraOptions,
|
|
4805
|
+
frameIndex: config.frameIndex,
|
|
4806
|
+
});
|
|
4807
|
+
return config;
|
|
4808
|
+
}
|
|
4809
|
+
|
|
4100
4810
|
function getSnapshot() {
|
|
4101
4811
|
return Object.freeze({
|
|
4102
4812
|
frame,
|
|
@@ -4106,11 +4816,14 @@ export async function createWavefrontPathTracingComputeRenderer(options = {}) {
|
|
|
4106
4816
|
tiles: tiles.length,
|
|
4107
4817
|
tileSize: config.tileSize,
|
|
4108
4818
|
samplesPerPixel: config.samplesPerPixel,
|
|
4819
|
+
maxFramePassesPerSubmission: config.maxFramePassesPerSubmission,
|
|
4109
4820
|
sceneObjectCount: config.sceneObjectCount,
|
|
4110
4821
|
triangleCount: config.triangleCount,
|
|
4111
4822
|
emissiveTriangleCount: config.emissiveTriangleCount,
|
|
4112
4823
|
environmentPortalCount: config.environmentPortalCount,
|
|
4113
4824
|
environmentPortalMode: config.environmentPortalMode,
|
|
4825
|
+
environmentMap: createEnvironmentMapSnapshot(config.environmentMap),
|
|
4826
|
+
deferredPathResolve: config.deferredPathResolve,
|
|
4114
4827
|
bvhNodeCount: config.bvhNodeCount,
|
|
4115
4828
|
displayQuality: config.displayQuality,
|
|
4116
4829
|
accelerationBuildMode: config.accelerationBuildMode,
|
|
@@ -4118,6 +4831,7 @@ export async function createWavefrontPathTracingComputeRenderer(options = {}) {
|
|
|
4118
4831
|
accelerationBuilt,
|
|
4119
4832
|
accelerationBuildCount,
|
|
4120
4833
|
frameConfigSlots: frameConfigSlotCount,
|
|
4834
|
+
gpuParallelism: lastGpuParallelism,
|
|
4121
4835
|
memory: config.memory,
|
|
4122
4836
|
});
|
|
4123
4837
|
}
|
|
@@ -4127,6 +4841,7 @@ export async function createWavefrontPathTracingComputeRenderer(options = {}) {
|
|
|
4127
4841
|
nextQueue.destroy?.();
|
|
4128
4842
|
hitBuffer.destroy?.();
|
|
4129
4843
|
accumulationBuffer.destroy?.();
|
|
4844
|
+
pathVertexBuffer.destroy?.();
|
|
4130
4845
|
sceneObjectBuffer.destroy?.();
|
|
4131
4846
|
triangleBuffer.destroy?.();
|
|
4132
4847
|
bvhNodeBuffer.destroy?.();
|
|
@@ -4142,6 +4857,9 @@ export async function createWavefrontPathTracingComputeRenderer(options = {}) {
|
|
|
4142
4857
|
radianceTexture.destroy?.();
|
|
4143
4858
|
denoiseScratchTexture.destroy?.();
|
|
4144
4859
|
outputTexture.destroy?.();
|
|
4860
|
+
if (environmentMapResource.ownsTexture) {
|
|
4861
|
+
environmentMapResource.texture?.destroy?.();
|
|
4862
|
+
}
|
|
4145
4863
|
context.unconfigure?.();
|
|
4146
4864
|
}
|
|
4147
4865
|
|
|
@@ -4155,6 +4873,7 @@ export async function createWavefrontPathTracingComputeRenderer(options = {}) {
|
|
|
4155
4873
|
renderFrame,
|
|
4156
4874
|
readOutputProbe,
|
|
4157
4875
|
updateSceneObjects,
|
|
4876
|
+
updateCamera,
|
|
4158
4877
|
getSnapshot,
|
|
4159
4878
|
destroy,
|
|
4160
4879
|
});
|