@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/src/wavefront-compute.js
CHANGED
|
@@ -21,11 +21,12 @@ const BVH_LEAF_REF_RECORD_BYTES = 16;
|
|
|
21
21
|
const EMISSIVE_TRIANGLE_INDEX_BYTES = 4;
|
|
22
22
|
const ENVIRONMENT_PORTAL_RECORD_BYTES = 96;
|
|
23
23
|
const ACCUMULATION_RECORD_BYTES = 16;
|
|
24
|
-
const
|
|
24
|
+
const PATH_VERTEX_RECORD_BYTES = 16;
|
|
25
|
+
const CONFIG_BUFFER_BYTES = 304;
|
|
25
26
|
const COUNTER_DISPATCH_ARGS_OFFSET = 16;
|
|
26
27
|
const INDIRECT_DISPATCH_ARGS_BYTES = 12;
|
|
27
28
|
const COUNTER_BUFFER_BYTES = 32;
|
|
28
|
-
const TRACE_STORAGE_BUFFER_BINDINGS =
|
|
29
|
+
const TRACE_STORAGE_BUFFER_BINDINGS = 10;
|
|
29
30
|
const HIT_TYPE_SURFACE = 0;
|
|
30
31
|
const HIT_TYPE_EMISSIVE = 1;
|
|
31
32
|
const MATERIAL_DIFFUSE = 0;
|
|
@@ -53,6 +54,7 @@ const DEFAULT_ENVIRONMENT_LIGHTING = Object.freeze({
|
|
|
53
54
|
intensity: 1,
|
|
54
55
|
mode: 0,
|
|
55
56
|
exposure: 1,
|
|
57
|
+
sunlitBaseline: 0.16,
|
|
56
58
|
});
|
|
57
59
|
|
|
58
60
|
export const wavefrontPathTracingComputeLimits = Object.freeze({
|
|
@@ -70,6 +72,7 @@ export const wavefrontPathTracingComputeLimits = Object.freeze({
|
|
|
70
72
|
emissiveTriangleMetadataRecordBytes: BVH_NODE_RECORD_BYTES,
|
|
71
73
|
environmentPortalRecordBytes: ENVIRONMENT_PORTAL_RECORD_BYTES,
|
|
72
74
|
accumulationRecordBytes: ACCUMULATION_RECORD_BYTES,
|
|
75
|
+
pathVertexRecordBytes: PATH_VERTEX_RECORD_BYTES,
|
|
73
76
|
counterRecordBytes: COUNTER_BUFFER_BYTES,
|
|
74
77
|
indirectDispatchRecordBytes: INDIRECT_DISPATCH_ARGS_BYTES,
|
|
75
78
|
});
|
|
@@ -161,6 +164,39 @@ function asColor(value, fallback = [1, 1, 1, 1]) {
|
|
|
161
164
|
];
|
|
162
165
|
}
|
|
163
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
|
+
|
|
164
200
|
function emissionPower(emission) {
|
|
165
201
|
return Math.max(0, emission?.[0] ?? 0) + Math.max(0, emission?.[1] ?? 0) + Math.max(0, emission?.[2] ?? 0);
|
|
166
202
|
}
|
|
@@ -914,6 +950,14 @@ function resolveEnvironmentLighting(input, environmentColor, ambientColor) {
|
|
|
914
950
|
intensity: Math.max(0.0001, readFiniteNumber("environmentLighting.intensity", source.intensity, DEFAULT_ENVIRONMENT_LIGHTING.intensity)),
|
|
915
951
|
mode: readNonNegativeInteger("environmentLighting.mode", source.mode, DEFAULT_ENVIRONMENT_LIGHTING.mode),
|
|
916
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
|
+
),
|
|
917
961
|
});
|
|
918
962
|
}
|
|
919
963
|
|
|
@@ -1117,6 +1161,11 @@ export function estimateWavefrontPathTracingMemory(options = {}) {
|
|
|
1117
1161
|
options.tilePixelCapacity,
|
|
1118
1162
|
DEFAULT_TILE_SIZE * DEFAULT_TILE_SIZE
|
|
1119
1163
|
);
|
|
1164
|
+
const maxDepth = clamp(
|
|
1165
|
+
readPositiveInteger("maxDepth", options.maxDepth, DEFAULT_MAX_DEPTH),
|
|
1166
|
+
1,
|
|
1167
|
+
16
|
|
1168
|
+
);
|
|
1120
1169
|
const sceneObjectCapacity = readPositiveInteger(
|
|
1121
1170
|
"sceneObjectCapacity",
|
|
1122
1171
|
options.sceneObjectCapacity,
|
|
@@ -1142,6 +1191,7 @@ export function estimateWavefrontPathTracingMemory(options = {}) {
|
|
|
1142
1191
|
const queueBytes = tilePixelCapacity * RAY_RECORD_BYTES;
|
|
1143
1192
|
const hitBytes = tilePixelCapacity * HIT_RECORD_BYTES;
|
|
1144
1193
|
const accumulationBytes = tilePixelCapacity * ACCUMULATION_RECORD_BYTES;
|
|
1194
|
+
const pathVertexBytes = tilePixelCapacity * (maxDepth + 1) * PATH_VERTEX_RECORD_BYTES;
|
|
1145
1195
|
const sceneObjectBytes = sceneObjectCapacity * SCENE_OBJECT_RECORD_BYTES;
|
|
1146
1196
|
const triangleBytes = triangleCapacity * TRIANGLE_RECORD_BYTES;
|
|
1147
1197
|
const bvhNodeBytes = bvhNodeCapacity * BVH_NODE_RECORD_BYTES;
|
|
@@ -1156,6 +1206,7 @@ export function estimateWavefrontPathTracingMemory(options = {}) {
|
|
|
1156
1206
|
queuePairBytes: queueBytes * 2,
|
|
1157
1207
|
hitBytes,
|
|
1158
1208
|
accumulationBytes,
|
|
1209
|
+
pathVertexBytes,
|
|
1159
1210
|
sceneObjectBytes,
|
|
1160
1211
|
triangleBytes,
|
|
1161
1212
|
bvhNodeBytes,
|
|
@@ -1169,6 +1220,7 @@ export function estimateWavefrontPathTracingMemory(options = {}) {
|
|
|
1169
1220
|
queueBytes * 2 +
|
|
1170
1221
|
hitBytes +
|
|
1171
1222
|
accumulationBytes +
|
|
1223
|
+
pathVertexBytes +
|
|
1172
1224
|
sceneObjectBytes +
|
|
1173
1225
|
triangleBytes +
|
|
1174
1226
|
bvhNodeBytes +
|
|
@@ -1286,6 +1338,12 @@ export function createWavefrontPathTracingComputeConfig(options = {}) {
|
|
|
1286
1338
|
options.environmentLighting?.environmentPortalMode,
|
|
1287
1339
|
environmentPortals.length > 0
|
|
1288
1340
|
);
|
|
1341
|
+
const environmentMap = resolveEnvironmentMap(
|
|
1342
|
+
options.environmentMap ??
|
|
1343
|
+
options.environmentTexture ??
|
|
1344
|
+
options.environmentLighting?.environmentMap
|
|
1345
|
+
);
|
|
1346
|
+
const deferredPathResolve = resolveDeferredPathResolve(options);
|
|
1289
1347
|
|
|
1290
1348
|
return Object.freeze({
|
|
1291
1349
|
mode: rendererWavefrontComputeMode,
|
|
@@ -1321,12 +1379,15 @@ export function createWavefrontPathTracingComputeConfig(options = {}) {
|
|
|
1321
1379
|
environmentPortalCount: environmentPortals.length,
|
|
1322
1380
|
environmentPortalCapacity,
|
|
1323
1381
|
environmentPortalMode,
|
|
1382
|
+
environmentMap,
|
|
1383
|
+
deferredPathResolve,
|
|
1324
1384
|
displayQuality: options.displayQuality === true,
|
|
1325
1385
|
requiresMeshBvhForDisplayQuality: true,
|
|
1326
1386
|
denoise: options.denoise !== false,
|
|
1327
1387
|
frameIndex: readNonNegativeInteger("frameIndex", options.frameIndex, 0),
|
|
1328
1388
|
memory: estimateWavefrontPathTracingMemory({
|
|
1329
1389
|
tilePixelCapacity,
|
|
1390
|
+
maxDepth,
|
|
1330
1391
|
sceneObjectCapacity,
|
|
1331
1392
|
triangleCapacity,
|
|
1332
1393
|
bvhNodeCapacity,
|
|
@@ -1550,6 +1611,18 @@ function createConfigPayload(config, tile, frameIndex, buildRange = {}) {
|
|
|
1550
1611
|
data.setUint32(260, config.environmentPortalMode ?? 0, true);
|
|
1551
1612
|
data.setUint32(264, 0, true);
|
|
1552
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
|
+
]);
|
|
1553
1626
|
return bytes;
|
|
1554
1627
|
}
|
|
1555
1628
|
|
|
@@ -1822,6 +1895,149 @@ function alignTo(value, alignment) {
|
|
|
1822
1895
|
return Math.ceil(value / resolvedAlignment) * resolvedAlignment;
|
|
1823
1896
|
}
|
|
1824
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
|
+
|
|
1825
2041
|
async function getPipelineDiagnostics(shaderModule) {
|
|
1826
2042
|
if (typeof shaderModule?.compilationInfo !== "function") {
|
|
1827
2043
|
return "";
|
|
@@ -2035,6 +2251,8 @@ struct FrameConfig {
|
|
|
2035
2251
|
environmentPortalMode: u32,
|
|
2036
2252
|
_portalPad0: u32,
|
|
2037
2253
|
_portalPad1: u32,
|
|
2254
|
+
environmentMapSettings: vec4<f32>,
|
|
2255
|
+
pathResolveSettings: vec4<f32>,
|
|
2038
2256
|
};
|
|
2039
2257
|
|
|
2040
2258
|
struct Counters {
|
|
@@ -2094,6 +2312,9 @@ struct EnvironmentPortal {
|
|
|
2094
2312
|
@group(0) @binding(17) var finalDenoiseInputRadiance: texture_2d<f32>;
|
|
2095
2313
|
@group(0) @binding(18) var denoisedOutputImage: texture_storage_2d<rgba8unorm, write>;
|
|
2096
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>>;
|
|
2097
2318
|
|
|
2098
2319
|
fn hash_u32(value: u32) -> u32 {
|
|
2099
2320
|
var x = value;
|
|
@@ -2138,6 +2359,89 @@ fn max_component(value: vec3<f32>) -> f32 {
|
|
|
2138
2359
|
return max(max(value.x, value.y), value.z);
|
|
2139
2360
|
}
|
|
2140
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
|
+
|
|
2141
2445
|
fn environment_portal_radiance_scale(origin: vec3<f32>, direction: vec3<f32>) -> vec3<f32> {
|
|
2142
2446
|
if (config.environmentPortalCount == 0u || config.environmentPortalMode == 0u) {
|
|
2143
2447
|
return vec3<f32>(1.0);
|
|
@@ -2177,22 +2481,9 @@ fn environment_portal_radiance_scale(origin: vec3<f32>, direction: vec3<f32>) ->
|
|
|
2177
2481
|
|
|
2178
2482
|
fn environment_radiance(origin: vec3<f32>, direction: vec3<f32>) -> vec3<f32> {
|
|
2179
2483
|
let rayDirection = safe_normalize(direction, vec3<f32>(0.0, 1.0, 0.0));
|
|
2180
|
-
let upFactor = saturate(rayDirection.y * 0.5 + 0.5);
|
|
2181
|
-
let sunDirection = safe_normalize(
|
|
2182
|
-
config.environmentSunDirectionIntensity.xyz,
|
|
2183
|
-
vec3<f32>(0.0, 1.0, 0.0)
|
|
2184
|
-
);
|
|
2185
|
-
let sunGlow = pow(saturate(dot(rayDirection, sunDirection)), 192.0);
|
|
2186
|
-
let gradient =
|
|
2187
|
-
config.environmentHorizonColor.xyz * (1.0 - upFactor) +
|
|
2188
|
-
config.environmentZenithColor.xyz * upFactor;
|
|
2189
2484
|
let portalScale = environment_portal_radiance_scale(origin, rayDirection);
|
|
2190
2485
|
let portalHit = max_component(portalScale) > 0.0001;
|
|
2191
|
-
return (
|
|
2192
|
-
gradient +
|
|
2193
|
-
config.environmentSunColor.xyz * sunGlow
|
|
2194
|
-
) *
|
|
2195
|
-
max(config.environmentSunDirectionIntensity.w, 0.0001) *
|
|
2486
|
+
return base_environment_radiance(rayDirection) *
|
|
2196
2487
|
select(vec3<f32>(1.0), portalScale, portalHit);
|
|
2197
2488
|
}
|
|
2198
2489
|
|
|
@@ -2208,16 +2499,59 @@ fn gated_environment_radiance(origin: vec3<f32>, direction: vec3<f32>) -> vec3<f
|
|
|
2208
2499
|
return environment_radiance(origin, direction);
|
|
2209
2500
|
}
|
|
2210
2501
|
|
|
2211
|
-
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> {
|
|
2212
2527
|
let normal = safe_normalize(hit.shadingNormal.xyz, vec3<f32>(0.0, 1.0, 0.0));
|
|
2213
|
-
let surfaceColor = max(hit.color.xyz, config.ambientColor.xyz);
|
|
2214
2528
|
let normalEnvironment = gated_environment_radiance(
|
|
2215
2529
|
hit.position.xyz + normal * 0.003,
|
|
2216
2530
|
normal
|
|
2217
2531
|
);
|
|
2218
|
-
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));
|
|
2219
2544
|
let materialFloor = select(0.7, 1.0, hit.materialKind == 0u || hit.materialKind == 3u);
|
|
2220
|
-
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
|
+
);
|
|
2221
2555
|
}
|
|
2222
2556
|
|
|
2223
2557
|
fn direct_environment_portal_irradiance(origin: vec3<f32>, normal: vec3<f32>) -> vec3<f32> {
|
|
@@ -2270,7 +2604,17 @@ fn surface_direct_environment_contribution(ray: RayRecord, hit: HitRecord) -> ve
|
|
|
2270
2604
|
|
|
2271
2605
|
let normalEnvironment = gated_environment_radiance(origin, normal);
|
|
2272
2606
|
let skyVisibility = 0.35 + saturate(normal.y * 0.5 + 0.5) * 0.45;
|
|
2273
|
-
let
|
|
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);
|
|
2274
2618
|
|
|
2275
2619
|
let sunDirection = safe_normalize(
|
|
2276
2620
|
config.environmentSunDirectionIntensity.xyz,
|
|
@@ -2894,6 +3238,7 @@ fn generatePrimaryRays(@builtin(global_invocation_id) globalId: vec3<u32>) {
|
|
|
2894
3238
|
return;
|
|
2895
3239
|
}
|
|
2896
3240
|
activeQueue[index] = make_ray(index);
|
|
3241
|
+
clear_deferred_path(index);
|
|
2897
3242
|
if (u32(config.projectionAndSampling.w) == 0u) {
|
|
2898
3243
|
accumulation[index] = vec4<f32>(0.0);
|
|
2899
3244
|
}
|
|
@@ -3146,25 +3491,37 @@ fn resolveSurfaceRecords(@builtin(global_invocation_id) globalId: vec3<u32>) {
|
|
|
3146
3491
|
|
|
3147
3492
|
if (hit.hitType == 1u) {
|
|
3148
3493
|
let guidedLightWeight = select(1.0, 0.24, (ray.flags & RAY_FLAG_GUIDED_EMISSIVE) != 0u);
|
|
3149
|
-
|
|
3150
|
-
|
|
3151
|
-
|
|
3152
|
-
|
|
3153
|
-
|
|
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
|
+
}
|
|
3154
3502
|
atomicAdd(&counters.terminatedCount, 1u);
|
|
3155
3503
|
return;
|
|
3156
3504
|
}
|
|
3157
3505
|
|
|
3158
3506
|
if (hit.hitType == 2u) {
|
|
3159
|
-
|
|
3160
|
-
|
|
3161
|
-
|
|
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
|
+
}
|
|
3162
3514
|
atomicAdd(&counters.terminatedCount, 1u);
|
|
3163
3515
|
return;
|
|
3164
3516
|
}
|
|
3165
3517
|
|
|
3518
|
+
let response = surface_path_response(hit);
|
|
3519
|
+
record_deferred_path_response(ray, response);
|
|
3520
|
+
|
|
3166
3521
|
let shouldEstimateDirectEnvironment =
|
|
3167
|
-
(
|
|
3522
|
+
!deferred_path_resolve_enabled() &&
|
|
3523
|
+
(hit.materialKind == 0u || hit.materialKind == 1u) &&
|
|
3524
|
+
hit.material.z >= 0.95;
|
|
3168
3525
|
if (shouldEstimateDirectEnvironment) {
|
|
3169
3526
|
let directEnvironment = surface_direct_environment_contribution(ray, hit);
|
|
3170
3527
|
accumulation[ray.rayId] =
|
|
@@ -3172,9 +3529,13 @@ fn resolveSurfaceRecords(@builtin(global_invocation_id) globalId: vec3<u32>) {
|
|
|
3172
3529
|
}
|
|
3173
3530
|
|
|
3174
3531
|
if (ray.bounce + 1u >= config.maxDepth) {
|
|
3175
|
-
|
|
3176
|
-
|
|
3177
|
-
|
|
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
|
+
}
|
|
3178
3539
|
atomicAdd(&counters.terminatedCount, 1u);
|
|
3179
3540
|
return;
|
|
3180
3541
|
}
|
|
@@ -3183,17 +3544,17 @@ fn resolveSurfaceRecords(@builtin(global_invocation_id) globalId: vec3<u32>) {
|
|
|
3183
3544
|
let scatter = scatter_direction(ray, hit, seed);
|
|
3184
3545
|
let nextIndex = atomicAdd(&counters.nextCount, 1u);
|
|
3185
3546
|
if (nextIndex >= config.tilePixelCount) {
|
|
3186
|
-
|
|
3187
|
-
|
|
3188
|
-
|
|
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
|
+
}
|
|
3189
3554
|
atomicAdd(&counters.terminatedCount, 1u);
|
|
3190
3555
|
return;
|
|
3191
3556
|
}
|
|
3192
|
-
let
|
|
3193
|
-
let opacity = clamp(hit.material.z, 0.0, 1.0);
|
|
3194
|
-
let materialEnergy = select(0.68, 0.92, hit.materialKind == 1u || hit.materialKind == 2u);
|
|
3195
|
-
let transparentEnergy = select(materialEnergy, 0.9, hit.hitType == 3u);
|
|
3196
|
-
let throughput = ray.throughput.xyz * mix(vec3<f32>(1.0), color, max(opacity, 0.18)) * transparentEnergy;
|
|
3557
|
+
let throughput = ray.throughput.xyz * response;
|
|
3197
3558
|
nextQueue[nextIndex] = RayRecord(
|
|
3198
3559
|
ray.rayId,
|
|
3199
3560
|
ray.rayId,
|
|
@@ -3221,6 +3582,27 @@ fn compactAndSwapQueues(@builtin(global_invocation_id) globalId: vec3<u32>) {
|
|
|
3221
3582
|
write_active_dispatch_args(activeCount);
|
|
3222
3583
|
}
|
|
3223
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
|
+
|
|
3224
3606
|
@compute @workgroup_size(64)
|
|
3225
3607
|
fn accumulateTerminalRadiance(@builtin(global_invocation_id) globalId: vec3<u32>) {
|
|
3226
3608
|
let index = globalId.x;
|
|
@@ -3230,7 +3612,12 @@ fn accumulateTerminalRadiance(@builtin(global_invocation_id) globalId: vec3<u32>
|
|
|
3230
3612
|
let localX = index % config.tileWidth;
|
|
3231
3613
|
let localY = index / config.tileWidth;
|
|
3232
3614
|
let pixel = vec2<i32>(i32(config.tileX + localX), i32(config.tileY + localY));
|
|
3233
|
-
|
|
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
|
+
}
|
|
3234
3621
|
|
|
3235
3622
|
textureStore(radianceImage, pixel, vec4<f32>(radiance, 1.0));
|
|
3236
3623
|
if (config.denoise == 0u) {
|
|
@@ -3373,6 +3760,137 @@ function createWavefrontDeviceDescriptor(adapter, options = {}) {
|
|
|
3373
3760
|
return Object.keys(descriptor).length > 0 ? descriptor : undefined;
|
|
3374
3761
|
}
|
|
3375
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
|
+
|
|
3376
3894
|
export async function createWavefrontPathTracingComputeRenderer(options = {}) {
|
|
3377
3895
|
assertAnalyticDisplayQualityPolicy(options);
|
|
3378
3896
|
const constants = getGpuUsageConstants();
|
|
@@ -3394,6 +3912,7 @@ export async function createWavefrontPathTracingComputeRenderer(options = {}) {
|
|
|
3394
3912
|
}
|
|
3395
3913
|
|
|
3396
3914
|
const device = await adapter.requestDevice(createWavefrontDeviceDescriptor(adapter, options));
|
|
3915
|
+
const gpuAdapterParallelism = createGpuAdapterParallelismDiagnostics(adapter, device);
|
|
3397
3916
|
const context = canvas.getContext("webgpu");
|
|
3398
3917
|
if (!context || typeof context.configure !== "function") {
|
|
3399
3918
|
throw new Error("Canvas WebGPU context does not support configure().");
|
|
@@ -3427,6 +3946,7 @@ export async function createWavefrontPathTracingComputeRenderer(options = {}) {
|
|
|
3427
3946
|
const rayQueueBytes = config.tilePixelCapacity * RAY_RECORD_BYTES;
|
|
3428
3947
|
const hitBytes = config.tilePixelCapacity * HIT_RECORD_BYTES;
|
|
3429
3948
|
const accumulationBytes = config.tilePixelCapacity * ACCUMULATION_RECORD_BYTES;
|
|
3949
|
+
const pathVertexBytes = config.tilePixelCapacity * (config.maxDepth + 1) * PATH_VERTEX_RECORD_BYTES;
|
|
3430
3950
|
const activeQueue = createBuffer(device, bufferUsage, rayQueueBytes, "plasius.wavefront.activeQueue");
|
|
3431
3951
|
const nextQueue = createBuffer(device, bufferUsage, rayQueueBytes, "plasius.wavefront.nextQueue");
|
|
3432
3952
|
const hitBuffer = createBuffer(device, bufferUsage, hitBytes, "plasius.wavefront.hitBuffer");
|
|
@@ -3436,6 +3956,12 @@ export async function createWavefrontPathTracingComputeRenderer(options = {}) {
|
|
|
3436
3956
|
accumulationBytes,
|
|
3437
3957
|
"plasius.wavefront.accumulation"
|
|
3438
3958
|
);
|
|
3959
|
+
const pathVertexBuffer = createBuffer(
|
|
3960
|
+
device,
|
|
3961
|
+
bufferUsage,
|
|
3962
|
+
pathVertexBytes,
|
|
3963
|
+
"plasius.wavefront.pathVertices"
|
|
3964
|
+
);
|
|
3439
3965
|
const sceneObjectBuffer = createBuffer(
|
|
3440
3966
|
device,
|
|
3441
3967
|
constants.buffer.STORAGE | constants.buffer.COPY_DST,
|
|
@@ -3493,9 +4019,10 @@ export async function createWavefrontPathTracingComputeRenderer(options = {}) {
|
|
|
3493
4019
|
? uniformOffsetAlignment
|
|
3494
4020
|
: CONFIG_BUFFER_BYTES
|
|
3495
4021
|
);
|
|
4022
|
+
const outputConfigSlotCount = config.deferredPathResolve ? 0 : tiles.length;
|
|
3496
4023
|
const frameConfigSlotCount = Math.max(
|
|
3497
4024
|
1,
|
|
3498
|
-
tiles.length * config.samplesPerPixel +
|
|
4025
|
+
tiles.length * config.samplesPerPixel + outputConfigSlotCount + (config.denoise ? 1 : 0)
|
|
3499
4026
|
);
|
|
3500
4027
|
const configBuffer = createBuffer(
|
|
3501
4028
|
device,
|
|
@@ -3583,6 +4110,12 @@ export async function createWavefrontPathTracingComputeRenderer(options = {}) {
|
|
|
3583
4110
|
magFilter: "nearest",
|
|
3584
4111
|
minFilter: "nearest",
|
|
3585
4112
|
});
|
|
4113
|
+
const environmentMapResource = createEnvironmentMapResource(
|
|
4114
|
+
device,
|
|
4115
|
+
constants,
|
|
4116
|
+
config.environmentMap,
|
|
4117
|
+
config.environmentColor
|
|
4118
|
+
);
|
|
3586
4119
|
|
|
3587
4120
|
const traceBindGroupLayout = device.createBindGroupLayout({
|
|
3588
4121
|
label: "plasius.wavefront.traceBindGroupLayout",
|
|
@@ -3611,6 +4144,9 @@ export async function createWavefrontPathTracingComputeRenderer(options = {}) {
|
|
|
3611
4144
|
storageTexture: { access: "write-only", format: "rgba16float" },
|
|
3612
4145
|
},
|
|
3613
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" } },
|
|
3614
4150
|
],
|
|
3615
4151
|
});
|
|
3616
4152
|
const accelerationBindGroupLayout = device.createBindGroupLayout({
|
|
@@ -3787,6 +4323,9 @@ export async function createWavefrontPathTracingComputeRenderer(options = {}) {
|
|
|
3787
4323
|
{ binding: 9, resource: { buffer: bvhNodeBuffer } },
|
|
3788
4324
|
{ binding: 16, resource: radianceView },
|
|
3789
4325
|
{ binding: 19, resource: { buffer: environmentPortalBuffer } },
|
|
4326
|
+
{ binding: 20, resource: environmentMapResource.view },
|
|
4327
|
+
{ binding: 21, resource: environmentMapResource.sampler },
|
|
4328
|
+
{ binding: 22, resource: { buffer: pathVertexBuffer } },
|
|
3790
4329
|
],
|
|
3791
4330
|
});
|
|
3792
4331
|
}
|
|
@@ -3881,6 +4420,10 @@ export async function createWavefrontPathTracingComputeRenderer(options = {}) {
|
|
|
3881
4420
|
let accelerationBuilt = !config.gpuAccelerationBuildRequired;
|
|
3882
4421
|
let accelerationBuildCount = 0;
|
|
3883
4422
|
let activeCameraOptions = options.camera ?? DEFAULT_CAMERA;
|
|
4423
|
+
let lastGpuParallelism = createGpuParallelismDiagnostics(
|
|
4424
|
+
gpuAdapterParallelism,
|
|
4425
|
+
createGpuParallelismCounters()
|
|
4426
|
+
);
|
|
3884
4427
|
|
|
3885
4428
|
function createFrameConfigWriter(frameIndex) {
|
|
3886
4429
|
let slot = 0;
|
|
@@ -3899,7 +4442,7 @@ export async function createWavefrontPathTracingComputeRenderer(options = {}) {
|
|
|
3899
4442
|
};
|
|
3900
4443
|
}
|
|
3901
4444
|
|
|
3902
|
-
function dispatchGpuAccelerationBuild(frameIndex) {
|
|
4445
|
+
function dispatchGpuAccelerationBuild(frameIndex, parallelism) {
|
|
3903
4446
|
if (!config.gpuAccelerationBuildRequired || accelerationBuilt) {
|
|
3904
4447
|
return false;
|
|
3905
4448
|
}
|
|
@@ -3938,24 +4481,32 @@ export async function createWavefrontPathTracingComputeRenderer(options = {}) {
|
|
|
3938
4481
|
});
|
|
3939
4482
|
passEncoder.setBindGroup(0, bvhBuildBindGroup, [0]);
|
|
3940
4483
|
passEncoder.setPipeline(pipelines.prepareMeshTrianglesAndLeaves);
|
|
3941
|
-
|
|
4484
|
+
const prepareWorkgroups = Math.ceil(config.bvhLeafSortCapacity / WORKGROUP_SIZE);
|
|
4485
|
+
passEncoder.dispatchWorkgroups(prepareWorkgroups);
|
|
4486
|
+
recordDirectDispatch(parallelism, [prepareWorkgroups]);
|
|
3942
4487
|
passEncoder.setPipeline(pipelines.sortBvhLeafRefs);
|
|
3943
4488
|
for (let stageIndex = 0; stageIndex < config.bvhSortStages.length; stageIndex += 1) {
|
|
3944
4489
|
passEncoder.setBindGroup(0, bvhBuildBindGroup, [
|
|
3945
4490
|
(stageIndex + 1) * configBufferStride,
|
|
3946
4491
|
]);
|
|
3947
|
-
|
|
4492
|
+
const sortWorkgroups = Math.ceil(config.bvhLeafSortCapacity / WORKGROUP_SIZE);
|
|
4493
|
+
passEncoder.dispatchWorkgroups(sortWorkgroups);
|
|
4494
|
+
recordDirectDispatch(parallelism, [sortWorkgroups]);
|
|
3948
4495
|
}
|
|
3949
4496
|
passEncoder.setBindGroup(0, bvhBuildBindGroup, [0]);
|
|
3950
4497
|
passEncoder.setPipeline(pipelines.writeSortedBvhLeaves);
|
|
3951
|
-
|
|
4498
|
+
const leafWriteWorkgroups = Math.ceil(config.triangleCount / WORKGROUP_SIZE);
|
|
4499
|
+
passEncoder.dispatchWorkgroups(leafWriteWorkgroups);
|
|
4500
|
+
recordDirectDispatch(parallelism, [leafWriteWorkgroups]);
|
|
3952
4501
|
passEncoder.setPipeline(pipelines.buildBvhInternalLevel);
|
|
3953
4502
|
for (let levelIndex = 0; levelIndex < config.bvhBuildLevels.length; levelIndex += 1) {
|
|
3954
4503
|
const buildLevel = config.bvhBuildLevels[levelIndex];
|
|
3955
4504
|
passEncoder.setBindGroup(0, bvhBuildBindGroup, [
|
|
3956
4505
|
(buildLevelConfigStart + levelIndex) * configBufferStride,
|
|
3957
4506
|
]);
|
|
3958
|
-
|
|
4507
|
+
const levelWorkgroups = Math.ceil(buildLevel.count / WORKGROUP_SIZE);
|
|
4508
|
+
passEncoder.dispatchWorkgroups(levelWorkgroups);
|
|
4509
|
+
recordDirectDispatch(parallelism, [levelWorkgroups]);
|
|
3959
4510
|
}
|
|
3960
4511
|
passEncoder.end();
|
|
3961
4512
|
device.queue.submit([encoder.finish()]);
|
|
@@ -3964,7 +4515,7 @@ export async function createWavefrontPathTracingComputeRenderer(options = {}) {
|
|
|
3964
4515
|
return true;
|
|
3965
4516
|
}
|
|
3966
4517
|
|
|
3967
|
-
function encodeTileSample(encoder, tile, configOffset) {
|
|
4518
|
+
function encodeTileSample(encoder, tile, configOffset, parallelism) {
|
|
3968
4519
|
const generatePass = encoder.beginComputePass({
|
|
3969
4520
|
label: "plasius.wavefront.generatePrimaryRaysPass",
|
|
3970
4521
|
});
|
|
@@ -3973,6 +4524,7 @@ export async function createWavefrontPathTracingComputeRenderer(options = {}) {
|
|
|
3973
4524
|
generatePass.setBindGroup(0, bindGroups[0], [configOffset]);
|
|
3974
4525
|
generatePass.setPipeline(pipelines.generatePrimaryRays);
|
|
3975
4526
|
generatePass.dispatchWorkgroups(tileWorkgroups);
|
|
4527
|
+
recordDirectDispatch(parallelism, [tileWorkgroups]);
|
|
3976
4528
|
generatePass.end();
|
|
3977
4529
|
|
|
3978
4530
|
for (let bounceIndex = 0; bounceIndex < config.maxDepth; bounceIndex += 1) {
|
|
@@ -3989,15 +4541,18 @@ export async function createWavefrontPathTracingComputeRenderer(options = {}) {
|
|
|
3989
4541
|
passEncoder.setBindGroup(0, bindGroups[bounceIndex % 2], [configOffset]);
|
|
3990
4542
|
passEncoder.setPipeline(pipelines.intersectActiveQueue);
|
|
3991
4543
|
passEncoder.dispatchWorkgroupsIndirect(activeDispatchBuffer, 0);
|
|
4544
|
+
recordIndirectDispatch(parallelism, tileWorkgroups);
|
|
3992
4545
|
passEncoder.setPipeline(pipelines.resolveSurfaceRecords);
|
|
3993
4546
|
passEncoder.dispatchWorkgroupsIndirect(activeDispatchBuffer, 0);
|
|
4547
|
+
recordIndirectDispatch(parallelism, tileWorkgroups);
|
|
3994
4548
|
passEncoder.setPipeline(pipelines.compactAndSwapQueues);
|
|
3995
4549
|
passEncoder.dispatchWorkgroups(1);
|
|
4550
|
+
recordDirectDispatch(parallelism, [1], 1);
|
|
3996
4551
|
passEncoder.end();
|
|
3997
4552
|
}
|
|
3998
4553
|
}
|
|
3999
4554
|
|
|
4000
|
-
function encodeTileOutput(encoder, tile, configOffset) {
|
|
4555
|
+
function encodeTileOutput(encoder, tile, configOffset, parallelism) {
|
|
4001
4556
|
const passEncoder = encoder.beginComputePass({
|
|
4002
4557
|
label: "plasius.wavefront.outputPass",
|
|
4003
4558
|
});
|
|
@@ -4006,19 +4561,23 @@ export async function createWavefrontPathTracingComputeRenderer(options = {}) {
|
|
|
4006
4561
|
passEncoder.setBindGroup(0, bindGroups[0], [configOffset]);
|
|
4007
4562
|
passEncoder.setPipeline(pipelines.accumulateTerminalRadiance);
|
|
4008
4563
|
passEncoder.dispatchWorkgroups(tileWorkgroups);
|
|
4564
|
+
recordDirectDispatch(parallelism, [tileWorkgroups]);
|
|
4009
4565
|
passEncoder.end();
|
|
4010
4566
|
}
|
|
4011
4567
|
|
|
4012
|
-
function encodeDenoise(encoder, configOffset) {
|
|
4568
|
+
function encodeDenoise(encoder, configOffset, parallelism) {
|
|
4013
4569
|
if (!config.denoise) {
|
|
4014
4570
|
return;
|
|
4015
4571
|
}
|
|
4572
|
+
const denoiseWorkgroupsX = Math.ceil(config.width / 8);
|
|
4573
|
+
const denoiseWorkgroupsY = Math.ceil(config.height / 8);
|
|
4016
4574
|
const radiancePass = encoder.beginComputePass({
|
|
4017
4575
|
label: "plasius.wavefront.denoiseRadiancePass",
|
|
4018
4576
|
});
|
|
4019
4577
|
radiancePass.setBindGroup(0, denoiseRadianceBindGroup, [configOffset]);
|
|
4020
4578
|
radiancePass.setPipeline(pipelines.denoiseLinearRadiance);
|
|
4021
|
-
radiancePass.dispatchWorkgroups(
|
|
4579
|
+
radiancePass.dispatchWorkgroups(denoiseWorkgroupsX, denoiseWorkgroupsY);
|
|
4580
|
+
recordDirectDispatch(parallelism, [denoiseWorkgroupsX, denoiseWorkgroupsY]);
|
|
4022
4581
|
radiancePass.end();
|
|
4023
4582
|
|
|
4024
4583
|
const resolvePass = encoder.beginComputePass({
|
|
@@ -4026,7 +4585,8 @@ export async function createWavefrontPathTracingComputeRenderer(options = {}) {
|
|
|
4026
4585
|
});
|
|
4027
4586
|
resolvePass.setBindGroup(0, denoiseResolveBindGroup, [configOffset]);
|
|
4028
4587
|
resolvePass.setPipeline(pipelines.resolveDenoisedOutputImage);
|
|
4029
|
-
resolvePass.dispatchWorkgroups(
|
|
4588
|
+
resolvePass.dispatchWorkgroups(denoiseWorkgroupsX, denoiseWorkgroupsY);
|
|
4589
|
+
recordDirectDispatch(parallelism, [denoiseWorkgroupsX, denoiseWorkgroupsY]);
|
|
4030
4590
|
resolvePass.end();
|
|
4031
4591
|
}
|
|
4032
4592
|
|
|
@@ -4049,7 +4609,7 @@ export async function createWavefrontPathTracingComputeRenderer(options = {}) {
|
|
|
4049
4609
|
passEncoder.end();
|
|
4050
4610
|
}
|
|
4051
4611
|
|
|
4052
|
-
function dispatchFrame(frameIndex) {
|
|
4612
|
+
function dispatchFrame(frameIndex, parallelism) {
|
|
4053
4613
|
const writeFrameConfig = createFrameConfigWriter(frameIndex);
|
|
4054
4614
|
let submissionCount = 0;
|
|
4055
4615
|
let encodedFramePasses = 0;
|
|
@@ -4086,20 +4646,25 @@ export async function createWavefrontPathTracingComputeRenderer(options = {}) {
|
|
|
4086
4646
|
sampleIndex,
|
|
4087
4647
|
sampleWeight: 1 / config.samplesPerPixel,
|
|
4088
4648
|
});
|
|
4089
|
-
encodeTileSample(reserveEncoder(), tile, configOffset);
|
|
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);
|
|
4090
4660
|
}
|
|
4091
|
-
const outputConfigOffset = writeFrameConfig(tile, {
|
|
4092
|
-
sampleIndex: 0,
|
|
4093
|
-
sampleWeight: 1 / config.samplesPerPixel,
|
|
4094
|
-
});
|
|
4095
|
-
encodeTileOutput(reserveEncoder(), tile, outputConfigOffset);
|
|
4096
4661
|
}
|
|
4097
4662
|
if (config.denoise) {
|
|
4098
4663
|
const denoiseConfigOffset = writeFrameConfig(
|
|
4099
4664
|
{ x: 0, y: 0, width: config.width, height: config.height },
|
|
4100
4665
|
{ sampleIndex: 0, sampleWeight: 1 / config.samplesPerPixel }
|
|
4101
4666
|
);
|
|
4102
|
-
encodeDenoise(reserveEncoder(), denoiseConfigOffset);
|
|
4667
|
+
encodeDenoise(reserveEncoder(), denoiseConfigOffset, parallelism);
|
|
4103
4668
|
}
|
|
4104
4669
|
encodePresent(reserveEncoder());
|
|
4105
4670
|
submitCurrentEncoder();
|
|
@@ -4109,8 +4674,10 @@ export async function createWavefrontPathTracingComputeRenderer(options = {}) {
|
|
|
4109
4674
|
function renderOnce() {
|
|
4110
4675
|
frame += 1;
|
|
4111
4676
|
const frameIndex = frame + config.frameIndex;
|
|
4112
|
-
const
|
|
4113
|
-
const
|
|
4677
|
+
const parallelismCounters = createGpuParallelismCounters();
|
|
4678
|
+
const accelerationBuildSubmitted = dispatchGpuAccelerationBuild(frameIndex, parallelismCounters);
|
|
4679
|
+
const frameSubmissionCount = dispatchFrame(frameIndex, parallelismCounters);
|
|
4680
|
+
lastGpuParallelism = createGpuParallelismDiagnostics(gpuAdapterParallelism, parallelismCounters);
|
|
4114
4681
|
return Object.freeze({
|
|
4115
4682
|
frame,
|
|
4116
4683
|
width: config.width,
|
|
@@ -4127,6 +4694,8 @@ export async function createWavefrontPathTracingComputeRenderer(options = {}) {
|
|
|
4127
4694
|
emissiveTriangleCount: config.emissiveTriangleCount,
|
|
4128
4695
|
environmentPortalCount: config.environmentPortalCount,
|
|
4129
4696
|
environmentPortalMode: config.environmentPortalMode,
|
|
4697
|
+
environmentMap: createEnvironmentMapSnapshot(config.environmentMap),
|
|
4698
|
+
deferredPathResolve: config.deferredPathResolve,
|
|
4130
4699
|
bvhNodeCount: config.bvhNodeCount,
|
|
4131
4700
|
displayQuality: config.displayQuality,
|
|
4132
4701
|
accelerationBuildMode: config.accelerationBuildMode,
|
|
@@ -4136,6 +4705,7 @@ export async function createWavefrontPathTracingComputeRenderer(options = {}) {
|
|
|
4136
4705
|
accelerationBuildCount,
|
|
4137
4706
|
commandSubmissions: frameSubmissionCount + (accelerationBuildSubmitted ? 1 : 0),
|
|
4138
4707
|
frameConfigSlots: frameConfigSlotCount,
|
|
4708
|
+
gpuParallelism: lastGpuParallelism,
|
|
4139
4709
|
memory: config.memory,
|
|
4140
4710
|
});
|
|
4141
4711
|
}
|
|
@@ -4252,6 +4822,8 @@ export async function createWavefrontPathTracingComputeRenderer(options = {}) {
|
|
|
4252
4822
|
emissiveTriangleCount: config.emissiveTriangleCount,
|
|
4253
4823
|
environmentPortalCount: config.environmentPortalCount,
|
|
4254
4824
|
environmentPortalMode: config.environmentPortalMode,
|
|
4825
|
+
environmentMap: createEnvironmentMapSnapshot(config.environmentMap),
|
|
4826
|
+
deferredPathResolve: config.deferredPathResolve,
|
|
4255
4827
|
bvhNodeCount: config.bvhNodeCount,
|
|
4256
4828
|
displayQuality: config.displayQuality,
|
|
4257
4829
|
accelerationBuildMode: config.accelerationBuildMode,
|
|
@@ -4259,6 +4831,7 @@ export async function createWavefrontPathTracingComputeRenderer(options = {}) {
|
|
|
4259
4831
|
accelerationBuilt,
|
|
4260
4832
|
accelerationBuildCount,
|
|
4261
4833
|
frameConfigSlots: frameConfigSlotCount,
|
|
4834
|
+
gpuParallelism: lastGpuParallelism,
|
|
4262
4835
|
memory: config.memory,
|
|
4263
4836
|
});
|
|
4264
4837
|
}
|
|
@@ -4268,6 +4841,7 @@ export async function createWavefrontPathTracingComputeRenderer(options = {}) {
|
|
|
4268
4841
|
nextQueue.destroy?.();
|
|
4269
4842
|
hitBuffer.destroy?.();
|
|
4270
4843
|
accumulationBuffer.destroy?.();
|
|
4844
|
+
pathVertexBuffer.destroy?.();
|
|
4271
4845
|
sceneObjectBuffer.destroy?.();
|
|
4272
4846
|
triangleBuffer.destroy?.();
|
|
4273
4847
|
bvhNodeBuffer.destroy?.();
|
|
@@ -4283,6 +4857,9 @@ export async function createWavefrontPathTracingComputeRenderer(options = {}) {
|
|
|
4283
4857
|
radianceTexture.destroy?.();
|
|
4284
4858
|
denoiseScratchTexture.destroy?.();
|
|
4285
4859
|
outputTexture.destroy?.();
|
|
4860
|
+
if (environmentMapResource.ownsTexture) {
|
|
4861
|
+
environmentMapResource.texture?.destroy?.();
|
|
4862
|
+
}
|
|
4286
4863
|
context.unconfigure?.();
|
|
4287
4864
|
}
|
|
4288
4865
|
|