@bornengine/engine 0.4.16
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/LICENSE +21 -0
- package/README.md +231 -0
- package/native/android/Cargo.lock +1848 -0
- package/native/android/Cargo.toml +24 -0
- package/native/android/src/lib.rs +702 -0
- package/native/ios/Cargo.lock +1690 -0
- package/native/ios/Cargo.toml +32 -0
- package/native/ios/src/lib.rs +1267 -0
- package/native/linux/Cargo.lock +3279 -0
- package/native/linux/Cargo.toml +29 -0
- package/native/linux/src/lib.rs +1331 -0
- package/native/macos/Cargo.lock +3310 -0
- package/native/macos/Cargo.toml +46 -0
- package/native/macos/src/lib.rs +1302 -0
- package/native/shared/Cargo.lock +1899 -0
- package/native/shared/Cargo.toml +62 -0
- package/native/shared/assets/default_font.ttf +0 -0
- package/native/shared/build.rs +270 -0
- package/native/shared/shaders/common/clouds.wgsl +122 -0
- package/native/shared/shaders/common/fog.wgsl +16 -0
- package/native/shared/shaders/common/foliage_wind.wgsl +98 -0
- package/native/shared/shaders/common/imposter.wgsl +112 -0
- package/native/shared/shaders/common/pbr.wgsl +186 -0
- package/native/shared/shaders/common/shadows.wgsl +186 -0
- package/native/shared/shaders/common/sky.wgsl +8 -0
- package/native/shared/shaders/common/tonemap.wgsl +25 -0
- package/native/shared/shaders/impulse_field.wgsl +57 -0
- package/native/shared/shaders/material_abi.wgsl +383 -0
- package/native/shared/shaders/materials/test_minimal.wgsl +42 -0
- package/native/shared/src/anim_mixer.rs +61 -0
- package/native/shared/src/attach.rs +263 -0
- package/native/shared/src/audio/decode.rs +123 -0
- package/native/shared/src/audio/mod.rs +863 -0
- package/native/shared/src/audio/render.rs +892 -0
- package/native/shared/src/audio/spsc.rs +156 -0
- package/native/shared/src/audio/stream.rs +226 -0
- package/native/shared/src/custom_shaders.rs +104 -0
- package/native/shared/src/decals.rs +245 -0
- package/native/shared/src/drs.rs +211 -0
- package/native/shared/src/engine.rs +261 -0
- package/native/shared/src/ffi.rs +116 -0
- package/native/shared/src/ffi_core/assets.rs +388 -0
- package/native/shared/src/ffi_core/audio_ffi.rs +184 -0
- package/native/shared/src/ffi_core/draw.rs +334 -0
- package/native/shared/src/ffi_core/game_loop.rs +577 -0
- package/native/shared/src/ffi_core/input.rs +234 -0
- package/native/shared/src/ffi_core/mod.rs +127 -0
- package/native/shared/src/ffi_core/models.rs +1154 -0
- package/native/shared/src/ffi_core/ragdoll_ffi.rs +261 -0
- package/native/shared/src/ffi_core/scene.rs +626 -0
- package/native/shared/src/ffi_core/vfx.rs +212 -0
- package/native/shared/src/ffi_core/visual.rs +691 -0
- package/native/shared/src/frame_callbacks.rs +122 -0
- package/native/shared/src/geometry.rs +236 -0
- package/native/shared/src/handles.rs +182 -0
- package/native/shared/src/input.rs +448 -0
- package/native/shared/src/jolt_sys.rs +822 -0
- package/native/shared/src/lib.rs +55 -0
- package/native/shared/src/models.rs +1093 -0
- package/native/shared/src/models_gltf.rs +1280 -0
- package/native/shared/src/particles.rs +391 -0
- package/native/shared/src/physics_jolt.rs +1908 -0
- package/native/shared/src/picking.rs +298 -0
- package/native/shared/src/postfx.rs +345 -0
- package/native/shared/src/profiler.rs +492 -0
- package/native/shared/src/ragdoll.rs +474 -0
- package/native/shared/src/renderer/atmosphere_lut.rs +573 -0
- package/native/shared/src/renderer/brdf_lut.rs +154 -0
- package/native/shared/src/renderer/draw2d.rs +143 -0
- package/native/shared/src/renderer/formats.rs +822 -0
- package/native/shared/src/renderer/froxel.rs +421 -0
- package/native/shared/src/renderer/gi_bake.rs +653 -0
- package/native/shared/src/renderer/graph.rs +462 -0
- package/native/shared/src/renderer/hiz.rs +269 -0
- package/native/shared/src/renderer/hot_reload.rs +390 -0
- package/native/shared/src/renderer/impulse_field.rs +456 -0
- package/native/shared/src/renderer/lighting.rs +154 -0
- package/native/shared/src/renderer/material_instancing.rs +171 -0
- package/native/shared/src/renderer/material_pipeline.rs +700 -0
- package/native/shared/src/renderer/material_system.rs +1996 -0
- package/native/shared/src/renderer/material_system_tests.rs +601 -0
- package/native/shared/src/renderer/material_system_wasm.rs +41 -0
- package/native/shared/src/renderer/mod.rs +12556 -0
- package/native/shared/src/renderer/model_draw.rs +641 -0
- package/native/shared/src/renderer/occlusion.rs +429 -0
- package/native/shared/src/renderer/planar_pass.rs +593 -0
- package/native/shared/src/renderer/planar_reflection.rs +499 -0
- package/native/shared/src/renderer/post_pass.rs +249 -0
- package/native/shared/src/renderer/postfx_chain.rs +728 -0
- package/native/shared/src/renderer/pt_pass.rs +577 -0
- package/native/shared/src/renderer/scene_pass.rs +607 -0
- package/native/shared/src/renderer/shader_include.rs +205 -0
- package/native/shared/src/renderer/shader_library.rs +135 -0
- package/native/shared/src/renderer/shaders/ao.rs +570 -0
- package/native/shared/src/renderer/shaders/core.rs +1243 -0
- package/native/shared/src/renderer/shaders/env.rs +907 -0
- package/native/shared/src/renderer/shaders/gi.rs +810 -0
- package/native/shared/src/renderer/shaders/mod.rs +19 -0
- package/native/shared/src/renderer/shaders/post.rs +1558 -0
- package/native/shared/src/renderer/shaders/pt.rs +1859 -0
- package/native/shared/src/renderer/shaders/ssgi.rs +1586 -0
- package/native/shared/src/renderer/shadow_pass.rs +731 -0
- package/native/shared/src/renderer/ssgi_pass.rs +392 -0
- package/native/shared/src/renderer/ssr_pass.rs +188 -0
- package/native/shared/src/renderer/texture_store.rs +473 -0
- package/native/shared/src/renderer/transient.rs +591 -0
- package/native/shared/src/renderer/types.rs +941 -0
- package/native/shared/src/renderer/util.rs +152 -0
- package/native/shared/src/scene.rs +1362 -0
- package/native/shared/src/sdf_cache.rs +274 -0
- package/native/shared/src/shadows.rs +1036 -0
- package/native/shared/src/staging.rs +102 -0
- package/native/shared/src/string_header.rs +266 -0
- package/native/shared/src/text_renderer.rs +502 -0
- package/native/shared/src/textures.rs +197 -0
- package/native/tvos/Cargo.lock +1693 -0
- package/native/tvos/Cargo.toml +36 -0
- package/native/tvos/metal-patched/Cargo.toml +178 -0
- package/native/tvos/metal-patched/LICENSE-APACHE +201 -0
- package/native/tvos/metal-patched/LICENSE-MIT +25 -0
- package/native/tvos/metal-patched/src/acceleration_structure.rs +667 -0
- package/native/tvos/metal-patched/src/acceleration_structure_pass.rs +108 -0
- package/native/tvos/metal-patched/src/argument.rs +366 -0
- package/native/tvos/metal-patched/src/blitpass.rs +102 -0
- package/native/tvos/metal-patched/src/buffer.rs +71 -0
- package/native/tvos/metal-patched/src/capturedescriptor.rs +76 -0
- package/native/tvos/metal-patched/src/capturemanager.rs +113 -0
- package/native/tvos/metal-patched/src/commandbuffer.rs +192 -0
- package/native/tvos/metal-patched/src/commandqueue.rs +44 -0
- package/native/tvos/metal-patched/src/computepass.rs +107 -0
- package/native/tvos/metal-patched/src/constants.rs +152 -0
- package/native/tvos/metal-patched/src/counters.rs +119 -0
- package/native/tvos/metal-patched/src/depthstencil.rs +190 -0
- package/native/tvos/metal-patched/src/device.rs +2134 -0
- package/native/tvos/metal-patched/src/drawable.rs +39 -0
- package/native/tvos/metal-patched/src/encoder.rs +2041 -0
- package/native/tvos/metal-patched/src/heap.rs +281 -0
- package/native/tvos/metal-patched/src/indirect_encoder.rs +344 -0
- package/native/tvos/metal-patched/src/lib.rs +657 -0
- package/native/tvos/metal-patched/src/library.rs +902 -0
- package/native/tvos/metal-patched/src/mps.rs +575 -0
- package/native/tvos/metal-patched/src/pipeline/compute.rs +475 -0
- package/native/tvos/metal-patched/src/pipeline/mod.rs +71 -0
- package/native/tvos/metal-patched/src/pipeline/render.rs +762 -0
- package/native/tvos/metal-patched/src/renderpass.rs +443 -0
- package/native/tvos/metal-patched/src/resource.rs +182 -0
- package/native/tvos/metal-patched/src/sampler.rs +165 -0
- package/native/tvos/metal-patched/src/sync.rs +178 -0
- package/native/tvos/metal-patched/src/texture.rs +352 -0
- package/native/tvos/metal-patched/src/types.rs +90 -0
- package/native/tvos/metal-patched/src/vertexdescriptor.rs +250 -0
- package/native/tvos/src/audio_backend.rs +197 -0
- package/native/tvos/src/lib.rs +1891 -0
- package/native/visionos/Cargo.lock +1693 -0
- package/native/visionos/Cargo.toml +40 -0
- package/native/visionos/src/audio_backend.rs +197 -0
- package/native/visionos/src/lib.rs +1887 -0
- package/native/watchos/Cargo.lock +16 -0
- package/native/watchos/Cargo.toml +19 -0
- package/native/watchos/shaders/bloom_postfx.metal +99 -0
- package/native/watchos/src/BloomWatchApp.swift +1267 -0
- package/native/watchos/src/BloomWatchAudio.swift +179 -0
- package/native/watchos/src/audio.rs +55 -0
- package/native/watchos/src/draw_list.rs +229 -0
- package/native/watchos/src/ffi_stubs.rs +915 -0
- package/native/watchos/src/ffi_stubs_manual.rs +35 -0
- package/native/watchos/src/lib.rs +1124 -0
- package/native/watchos/src/models.rs +746 -0
- package/native/watchos/src/postfx.rs +95 -0
- package/native/watchos/src/scene.rs +534 -0
- package/native/watchos/src/textures.rs +184 -0
- package/native/web/Cargo.lock +1657 -0
- package/native/web/Cargo.toml +43 -0
- package/native/web/bloom_glue.js +695 -0
- package/native/web/build.sh +131 -0
- package/native/web/index.html +35 -0
- package/native/web/jolt_bridge.js +1519 -0
- package/native/web/src/input_ffi.rs +286 -0
- package/native/web/src/lib.rs +1796 -0
- package/native/web/src/material_ffi.rs +710 -0
- package/native/web/src/parity_ffi.rs +343 -0
- package/native/web/src/physics_ffi.rs +643 -0
- package/native/web/src/ragdoll_ffi.rs +250 -0
- package/native/web/src/render_settings.rs +98 -0
- package/native/windows/Cargo.lock +1815 -0
- package/native/windows/Cargo.toml +68 -0
- package/native/windows/src/lib.rs +1486 -0
- package/package.json +4279 -0
- package/src/audio/index.ts +315 -0
- package/src/core/colors.ts +63 -0
- package/src/core/index.ts +1206 -0
- package/src/core/keys.ts +63 -0
- package/src/core/types.ts +104 -0
- package/src/index.ts +171 -0
- package/src/math/index.ts +516 -0
- package/src/mobile/index.ts +294 -0
- package/src/models/index.ts +1258 -0
- package/src/physics/index.ts +1134 -0
- package/src/scene/index.ts +698 -0
- package/src/shapes/index.ts +120 -0
- package/src/text/index.ts +48 -0
- package/src/textures/index.ts +187 -0
- package/src/vfx/index.ts +191 -0
- package/src/world/index.ts +24 -0
- package/src/world/loader.ts +423 -0
- package/src/world/prefab.ts +217 -0
- package/src/world/render.ts +172 -0
- package/src/world/saver.ts +108 -0
- package/src/world/serialize.ts +301 -0
- package/src/world/terrain.ts +355 -0
- package/src/world/types.ts +160 -0
- package/src/world/validate.ts +319 -0
- package/src/world/version.ts +114 -0
|
@@ -0,0 +1,392 @@
|
|
|
1
|
+
//! Lumen-style screen-probe SSGI: probe placement, Hi-Z/HW/SDF trace,
|
|
2
|
+
//! temporal EMA, and octahedral resolve into ssgi_rt. Split from
|
|
3
|
+
//! end_frame_with_scene (2000-line file policy + render-graph migration
|
|
4
|
+
//! prep). When disabled, clears ssgi_rt to transparent.
|
|
5
|
+
|
|
6
|
+
use super::*;
|
|
7
|
+
|
|
8
|
+
impl Renderer {
|
|
9
|
+
pub(super) fn record_ssgi_passes(
|
|
10
|
+
&mut self,
|
|
11
|
+
encoder: &mut wgpu::CommandEncoder,
|
|
12
|
+
profiler: &mut crate::profiler::Profiler,
|
|
13
|
+
surf_w: u32,
|
|
14
|
+
surf_h: u32,
|
|
15
|
+
) {
|
|
16
|
+
// ============================================================
|
|
17
|
+
// Ticket 007a: Lumen-style screen-probe SSGI.
|
|
18
|
+
// place → trace (SW Hi-Z) → temporal (EMA ping-pong) → resolve.
|
|
19
|
+
// Resolve writes `ssgi_rt_view` so downstream compositing is
|
|
20
|
+
// unchanged. When disabled we just clear `ssgi_rt_view` to
|
|
21
|
+
// transparent (same fallback shape as the old per-pixel path).
|
|
22
|
+
// ============================================================
|
|
23
|
+
let half_w = (surf_w / 2).max(1);
|
|
24
|
+
let half_h = (surf_h / 2).max(1);
|
|
25
|
+
let gw = self.probe_grid_w;
|
|
26
|
+
let gh = self.probe_grid_h;
|
|
27
|
+
let write_idx = self.probe_history_idx;
|
|
28
|
+
let prev_idx = 1 - write_idx;
|
|
29
|
+
|
|
30
|
+
// PT-1: while the path tracer owns the frame its output already
|
|
31
|
+
// contains full GI — probe SSGI would burn ~2ms to be composited
|
|
32
|
+
// over by nothing (the else-branch clear keeps compose additive-
|
|
33
|
+
// safe either way). pt_owns_frame, not pt_active: progressive mode
|
|
34
|
+
// shows raster frames until accumulation warms up, and those still
|
|
35
|
+
// want SSGI.
|
|
36
|
+
if self.ssgi_enabled && !self.pt_owns_frame() {
|
|
37
|
+
let p00 = self.current_proj_matrix[0][0];
|
|
38
|
+
let p11 = self.current_proj_matrix[1][1];
|
|
39
|
+
let p20 = self.current_proj_matrix[2][0];
|
|
40
|
+
let p21 = self.current_proj_matrix[2][1];
|
|
41
|
+
let inv_view = mat4_invert(self.current_view_matrix);
|
|
42
|
+
|
|
43
|
+
// ---- place ----
|
|
44
|
+
let place_params = ProbePlaceParams {
|
|
45
|
+
inv_view,
|
|
46
|
+
proj_row01: [p00, p11, p20, p21],
|
|
47
|
+
size: [half_w, half_h, gw, gh],
|
|
48
|
+
params: [self.taa_frame_index as f32, PROBE_TILE_SIZE as f32, 0.0, 0.0],
|
|
49
|
+
};
|
|
50
|
+
self.queue.write_buffer(&self.probe_place_uniform, 0, bytemuck::bytes_of(&place_params));
|
|
51
|
+
if self.probe_place_bg_cache.is_none() {
|
|
52
|
+
self.probe_place_bg_cache = Some(self.device.create_bind_group(&wgpu::BindGroupDescriptor {
|
|
53
|
+
label: Some("probe_place_bg"),
|
|
54
|
+
layout: &self.probe_place_layout,
|
|
55
|
+
entries: &[
|
|
56
|
+
wgpu::BindGroupEntry { binding: 0, resource: self.probe_place_uniform.as_entire_binding() },
|
|
57
|
+
wgpu::BindGroupEntry { binding: 1, resource: wgpu::BindingResource::TextureView(&self.hiz_views[0]) },
|
|
58
|
+
wgpu::BindGroupEntry { binding: 2, resource: wgpu::BindingResource::Sampler(&self.hiz_sampler) },
|
|
59
|
+
wgpu::BindGroupEntry { binding: 3, resource: self.probe_header_buffer.as_entire_binding() },
|
|
60
|
+
],
|
|
61
|
+
}));
|
|
62
|
+
}
|
|
63
|
+
{
|
|
64
|
+
let ts = profiler.compute_pass_timestamp_writes("probe_place_pass");
|
|
65
|
+
let mut pass = encoder.begin_compute_pass(&wgpu::ComputePassDescriptor {
|
|
66
|
+
label: Some("probe_place_pass"), timestamp_writes: ts,
|
|
67
|
+
});
|
|
68
|
+
pass.set_pipeline(&self.probe_place_pipeline);
|
|
69
|
+
pass.set_bind_group(0, self.probe_place_bg_cache.as_ref().unwrap(), &[]);
|
|
70
|
+
pass.dispatch_workgroups((gw + 7) / 8, (gh + 7) / 8, 1);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// ---- trace ----
|
|
74
|
+
// Sun direction in world space — inverted because our
|
|
75
|
+
// `light_dir` points from light toward the scene, while the
|
|
76
|
+
// shader's NdotL expects the vector from the shading point
|
|
77
|
+
// toward the light. Normalised because the shader doesn't.
|
|
78
|
+
let ld = self.lighting_uniforms.light_dir;
|
|
79
|
+
let sun_inv_len = 1.0 / (ld[0]*ld[0] + ld[1]*ld[1] + ld[2]*ld[2]).sqrt().max(1e-4);
|
|
80
|
+
let sun_dir_ws = [
|
|
81
|
+
-ld[0] * sun_inv_len,
|
|
82
|
+
-ld[1] * sun_inv_len,
|
|
83
|
+
-ld[2] * sun_inv_len,
|
|
84
|
+
ld[3],
|
|
85
|
+
];
|
|
86
|
+
// Sun colour = light_color × light intensity (ld.w). Sky
|
|
87
|
+
// colour = ambient × ambient intensity (ambient.w) — a
|
|
88
|
+
// crude dome irradiance, good enough for a one-bounce
|
|
89
|
+
// shading estimate. Both fields are ignored by the SW
|
|
90
|
+
// shader which inherits the same uniform struct layout.
|
|
91
|
+
let lc = self.lighting_uniforms.light_color;
|
|
92
|
+
let sun_intensity = ld[3].max(0.0);
|
|
93
|
+
let sun_color = [
|
|
94
|
+
lc[0] * sun_intensity,
|
|
95
|
+
lc[1] * sun_intensity,
|
|
96
|
+
lc[2] * sun_intensity,
|
|
97
|
+
0.0,
|
|
98
|
+
];
|
|
99
|
+
let amb = self.lighting_uniforms.ambient;
|
|
100
|
+
let sky_intensity = amb[3].max(0.0);
|
|
101
|
+
let sky_color = [
|
|
102
|
+
amb[0] * sky_intensity,
|
|
103
|
+
amb[1] * sky_intensity,
|
|
104
|
+
amb[2] * sky_intensity,
|
|
105
|
+
0.0,
|
|
106
|
+
];
|
|
107
|
+
let trace_params = ProbeTraceParams {
|
|
108
|
+
view: self.current_view_matrix,
|
|
109
|
+
proj: self.current_proj_matrix,
|
|
110
|
+
inv_view,
|
|
111
|
+
proj_row01: [p00, p11, p20, p21],
|
|
112
|
+
size: [half_w, half_h, gw, gh],
|
|
113
|
+
params: [
|
|
114
|
+
self.taa_frame_index as f32,
|
|
115
|
+
self.ssgi_intensity,
|
|
116
|
+
self.ssgi_radius,
|
|
117
|
+
10.0, // firefly luma cap
|
|
118
|
+
],
|
|
119
|
+
sun_dir: sun_dir_ws,
|
|
120
|
+
sun_color,
|
|
121
|
+
sky_color,
|
|
122
|
+
// Ticket 014 V3 — clipmap origin xyz + full extent w.
|
|
123
|
+
// The SDF trace variant reads these; HW + Hi-Z ignore.
|
|
124
|
+
clipmap: [
|
|
125
|
+
self.scene_sdf_clipmap_origin[0],
|
|
126
|
+
self.scene_sdf_clipmap_origin[1],
|
|
127
|
+
self.scene_sdf_clipmap_origin[2],
|
|
128
|
+
SCENE_SDF_CLIPMAP_EXTENT,
|
|
129
|
+
],
|
|
130
|
+
// Ticket 014 V6/V13 — WSRC cascade cubes. `extent =
|
|
131
|
+
// 0` marks an unbaked cascade; the shader's
|
|
132
|
+
// `pick_cascade` helper skips those and falls through
|
|
133
|
+
// to the next cascade (or returns black if none are
|
|
134
|
+
// ready). First frame after startup all three are
|
|
135
|
+
// unbaked → miss returns black, matching pre-V6.
|
|
136
|
+
wsrc_cascades: [
|
|
137
|
+
[
|
|
138
|
+
self.wsrc_origin[0][0],
|
|
139
|
+
self.wsrc_origin[0][1],
|
|
140
|
+
self.wsrc_origin[0][2],
|
|
141
|
+
if self.wsrc_built[0] { WSRC_CASCADE_EXTENTS[0] } else { 0.0 },
|
|
142
|
+
],
|
|
143
|
+
[
|
|
144
|
+
self.wsrc_origin[1][0],
|
|
145
|
+
self.wsrc_origin[1][1],
|
|
146
|
+
self.wsrc_origin[1][2],
|
|
147
|
+
if self.wsrc_built[1] { WSRC_CASCADE_EXTENTS[1] } else { 0.0 },
|
|
148
|
+
],
|
|
149
|
+
[
|
|
150
|
+
self.wsrc_origin[2][0],
|
|
151
|
+
self.wsrc_origin[2][1],
|
|
152
|
+
self.wsrc_origin[2][2],
|
|
153
|
+
if self.wsrc_built[2] { WSRC_CASCADE_EXTENTS[2] } else { 0.0 },
|
|
154
|
+
],
|
|
155
|
+
],
|
|
156
|
+
};
|
|
157
|
+
self.queue.write_buffer(&self.probe_trace_uniform, 0, bytemuck::bytes_of(&trace_params));
|
|
158
|
+
// V3 — trace BG now binds the prev-frame history view at
|
|
159
|
+
// binding 11. `prev_idx` ping-pongs every frame so we
|
|
160
|
+
// cache both slots independently.
|
|
161
|
+
if self.probe_trace_bg_cache[prev_idx].is_none() {
|
|
162
|
+
self.probe_trace_bg_cache[prev_idx] = Some(self.device.create_bind_group(&wgpu::BindGroupDescriptor {
|
|
163
|
+
label: Some("probe_trace_bg"),
|
|
164
|
+
layout: &self.probe_trace_layout,
|
|
165
|
+
entries: &[
|
|
166
|
+
wgpu::BindGroupEntry { binding: 0, resource: self.probe_trace_uniform.as_entire_binding() },
|
|
167
|
+
wgpu::BindGroupEntry { binding: 1, resource: self.probe_header_buffer.as_entire_binding() },
|
|
168
|
+
wgpu::BindGroupEntry { binding: 2, resource: wgpu::BindingResource::TextureView(&self.hiz_views[0]) },
|
|
169
|
+
wgpu::BindGroupEntry { binding: 3, resource: wgpu::BindingResource::TextureView(&self.hiz_views[1]) },
|
|
170
|
+
wgpu::BindGroupEntry { binding: 4, resource: wgpu::BindingResource::TextureView(&self.hiz_views[2]) },
|
|
171
|
+
wgpu::BindGroupEntry { binding: 5, resource: wgpu::BindingResource::TextureView(&self.hiz_views[3]) },
|
|
172
|
+
wgpu::BindGroupEntry { binding: 6, resource: wgpu::BindingResource::TextureView(&self.hiz_views[4]) },
|
|
173
|
+
wgpu::BindGroupEntry { binding: 7, resource: wgpu::BindingResource::Sampler(&self.hiz_sampler) },
|
|
174
|
+
wgpu::BindGroupEntry { binding: 8, resource: wgpu::BindingResource::TextureView(&self.hdr_rt_view) },
|
|
175
|
+
wgpu::BindGroupEntry { binding: 9, resource: wgpu::BindingResource::Sampler(&self.composite_sampler) },
|
|
176
|
+
wgpu::BindGroupEntry { binding: 10, resource: wgpu::BindingResource::TextureView(&self.probe_trace_view) },
|
|
177
|
+
wgpu::BindGroupEntry { binding: 11, resource: wgpu::BindingResource::TextureView(&self.probe_history_views[prev_idx]) },
|
|
178
|
+
],
|
|
179
|
+
}));
|
|
180
|
+
}
|
|
181
|
+
// HW trace needs both the TLAS (at least one instance) and
|
|
182
|
+
// the instance-data buffer to exist. Fall back to SDF or
|
|
183
|
+
// Hi-Z when either is missing on an HW-enabled adapter
|
|
184
|
+
// (e.g. first frame before the scene has loaded any
|
|
185
|
+
// geometry).
|
|
186
|
+
let use_hw = self.hw_rt_enabled
|
|
187
|
+
&& self.probe_trace_hw_pipeline.is_some()
|
|
188
|
+
&& self.tlas.is_some()
|
|
189
|
+
&& self.tlas_instance_data_buffer.is_some();
|
|
190
|
+
// Ticket 014 V3/V4 — pick SDF sphere-trace over Hi-Z when
|
|
191
|
+
// the scene clipmap is baked AND the instance-data buffer
|
|
192
|
+
// is ready (needed for broad-phase textured hit sampling
|
|
193
|
+
// added in V4). Otherwise fall through to Hi-Z. HW still
|
|
194
|
+
// wins over both when the feature was granted.
|
|
195
|
+
let use_sdf = !use_hw
|
|
196
|
+
&& self.scene_sdf_clipmap_built
|
|
197
|
+
&& self.tlas_instance_data_buffer.is_some();
|
|
198
|
+
// Log the backend once (and again if it changes, e.g. clipmap
|
|
199
|
+
// finishing its first bake promotes hiz → sdf). Nothing else in
|
|
200
|
+
// the engine reveals which tier actually runs.
|
|
201
|
+
let backend = if use_hw { "hw-ray-query" } else if use_sdf { "sdf-clipmap" } else { "hiz-screen" };
|
|
202
|
+
if self.ssgi_backend_logged != Some(backend) {
|
|
203
|
+
self.ssgi_backend_logged = Some(backend);
|
|
204
|
+
eprintln!("bloom: ssgi trace backend = {}", backend);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
if use_hw {
|
|
208
|
+
// Build the HW bind group lazily. V3 uses a per-
|
|
209
|
+
// prev_idx slot since the prev-frame history view
|
|
210
|
+
// ping-pongs each frame.
|
|
211
|
+
if self.probe_trace_hw_bg_cache[prev_idx].is_none() {
|
|
212
|
+
let tlas = self.tlas.as_ref().unwrap();
|
|
213
|
+
self.probe_trace_hw_bg_cache[prev_idx] = Some(self.device.create_bind_group(&wgpu::BindGroupDescriptor {
|
|
214
|
+
label: Some("probe_trace_hw_bg"),
|
|
215
|
+
layout: self.probe_trace_hw_layout.as_ref().unwrap(),
|
|
216
|
+
entries: &[
|
|
217
|
+
wgpu::BindGroupEntry { binding: 0, resource: self.probe_trace_uniform.as_entire_binding() },
|
|
218
|
+
wgpu::BindGroupEntry { binding: 1, resource: self.probe_header_buffer.as_entire_binding() },
|
|
219
|
+
wgpu::BindGroupEntry { binding: 2, resource: tlas.as_binding() },
|
|
220
|
+
wgpu::BindGroupEntry { binding: 3, resource: self.tlas_instance_data_buffer.as_ref().unwrap().as_entire_binding() },
|
|
221
|
+
wgpu::BindGroupEntry { binding: 4, resource: wgpu::BindingResource::TextureView(&self.probe_trace_view) },
|
|
222
|
+
// Ticket 013 V2: the HW trace samples the
|
|
223
|
+
// *radiance* atlas (pre-lit by card_light_pass)
|
|
224
|
+
// at hit, not the raw albedo atlas.
|
|
225
|
+
wgpu::BindGroupEntry { binding: 5, resource: wgpu::BindingResource::TextureView(&self.mesh_card_radiance_view) },
|
|
226
|
+
wgpu::BindGroupEntry { binding: 6, resource: wgpu::BindingResource::Sampler(&self.mesh_card_atlas_sampler) },
|
|
227
|
+
// V7/V10 — WSRC atlas + linear sampler.
|
|
228
|
+
wgpu::BindGroupEntry { binding: 7, resource: wgpu::BindingResource::TextureView(&self.wsrc_atlas_view) },
|
|
229
|
+
wgpu::BindGroupEntry { binding: 8, resource: wgpu::BindingResource::Sampler(&self.wsrc_atlas_sampler) },
|
|
230
|
+
// V3 — prev-frame probe history.
|
|
231
|
+
wgpu::BindGroupEntry { binding: 9, resource: wgpu::BindingResource::TextureView(&self.probe_history_views[prev_idx]) },
|
|
232
|
+
],
|
|
233
|
+
}));
|
|
234
|
+
}
|
|
235
|
+
let ts = profiler.compute_pass_timestamp_writes("probe_trace_hw_pass");
|
|
236
|
+
let mut pass = encoder.begin_compute_pass(&wgpu::ComputePassDescriptor {
|
|
237
|
+
label: Some("probe_trace_hw_pass"), timestamp_writes: ts,
|
|
238
|
+
});
|
|
239
|
+
pass.set_pipeline(self.probe_trace_hw_pipeline.as_ref().unwrap());
|
|
240
|
+
pass.set_bind_group(0, self.probe_trace_hw_bg_cache[prev_idx].as_ref().unwrap(), &[]);
|
|
241
|
+
pass.dispatch_workgroups(gw, gh, 1);
|
|
242
|
+
} else if use_sdf {
|
|
243
|
+
// Ticket 014 V3 — SW SDF sphere-trace path.
|
|
244
|
+
// V3 (ticket 016) uses a per-prev_idx slot for the
|
|
245
|
+
// prev-frame history binding.
|
|
246
|
+
if self.probe_trace_sdf_bg_cache[prev_idx].is_none() {
|
|
247
|
+
let nf_samp = self.device.create_sampler(&wgpu::SamplerDescriptor {
|
|
248
|
+
label: Some("clipmap_nonfiltering_sampler"),
|
|
249
|
+
address_mode_u: wgpu::AddressMode::ClampToEdge,
|
|
250
|
+
address_mode_v: wgpu::AddressMode::ClampToEdge,
|
|
251
|
+
address_mode_w: wgpu::AddressMode::ClampToEdge,
|
|
252
|
+
mag_filter: wgpu::FilterMode::Nearest,
|
|
253
|
+
min_filter: wgpu::FilterMode::Nearest,
|
|
254
|
+
mipmap_filter: wgpu::MipmapFilterMode::Nearest,
|
|
255
|
+
..Default::default()
|
|
256
|
+
});
|
|
257
|
+
let instance_buf = self.tlas_instance_data_buffer.as_ref()
|
|
258
|
+
.expect("V4: instance_data buffer must exist before SDF dispatch");
|
|
259
|
+
self.probe_trace_sdf_bg_cache[prev_idx] = Some(self.device.create_bind_group(&wgpu::BindGroupDescriptor {
|
|
260
|
+
label: Some("probe_trace_sdf_bg"),
|
|
261
|
+
layout: &self.probe_trace_sdf_layout,
|
|
262
|
+
entries: &[
|
|
263
|
+
wgpu::BindGroupEntry { binding: 0, resource: self.probe_trace_uniform.as_entire_binding() },
|
|
264
|
+
wgpu::BindGroupEntry { binding: 1, resource: self.probe_header_buffer.as_entire_binding() },
|
|
265
|
+
wgpu::BindGroupEntry { binding: 2, resource: wgpu::BindingResource::TextureView(&self.scene_sdf_clipmap_view) },
|
|
266
|
+
wgpu::BindGroupEntry { binding: 3, resource: wgpu::BindingResource::Sampler(&nf_samp) },
|
|
267
|
+
wgpu::BindGroupEntry { binding: 4, resource: wgpu::BindingResource::TextureView(&self.probe_trace_view) },
|
|
268
|
+
wgpu::BindGroupEntry { binding: 5, resource: instance_buf.as_entire_binding() },
|
|
269
|
+
wgpu::BindGroupEntry { binding: 6, resource: wgpu::BindingResource::TextureView(&self.mesh_card_radiance_view) },
|
|
270
|
+
wgpu::BindGroupEntry { binding: 7, resource: wgpu::BindingResource::Sampler(&self.mesh_card_atlas_sampler) },
|
|
271
|
+
// V6/V10 — WSRC atlas + linear sampler.
|
|
272
|
+
wgpu::BindGroupEntry { binding: 8, resource: wgpu::BindingResource::TextureView(&self.wsrc_atlas_view) },
|
|
273
|
+
wgpu::BindGroupEntry { binding: 9, resource: wgpu::BindingResource::Sampler(&self.wsrc_atlas_sampler) },
|
|
274
|
+
// V3 — prev-frame probe history.
|
|
275
|
+
wgpu::BindGroupEntry { binding: 10, resource: wgpu::BindingResource::TextureView(&self.probe_history_views[prev_idx]) },
|
|
276
|
+
],
|
|
277
|
+
}));
|
|
278
|
+
}
|
|
279
|
+
let ts = profiler.compute_pass_timestamp_writes("probe_trace_sdf_pass");
|
|
280
|
+
let mut pass = encoder.begin_compute_pass(&wgpu::ComputePassDescriptor {
|
|
281
|
+
label: Some("probe_trace_sdf_pass"), timestamp_writes: ts,
|
|
282
|
+
});
|
|
283
|
+
pass.set_pipeline(&self.probe_trace_sdf_pipeline);
|
|
284
|
+
pass.set_bind_group(0, self.probe_trace_sdf_bg_cache[prev_idx].as_ref().unwrap(), &[]);
|
|
285
|
+
pass.dispatch_workgroups(gw, gh, 1);
|
|
286
|
+
} else {
|
|
287
|
+
let ts = profiler.compute_pass_timestamp_writes("probe_trace_pass");
|
|
288
|
+
let mut pass = encoder.begin_compute_pass(&wgpu::ComputePassDescriptor {
|
|
289
|
+
label: Some("probe_trace_pass"), timestamp_writes: ts,
|
|
290
|
+
});
|
|
291
|
+
pass.set_pipeline(&self.probe_trace_pipeline);
|
|
292
|
+
pass.set_bind_group(0, self.probe_trace_bg_cache[prev_idx].as_ref().unwrap(), &[]);
|
|
293
|
+
pass.dispatch_workgroups(gw, gh, 1);
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
// ---- temporal (EMA) ----
|
|
297
|
+
// First frame forces alpha=1 so the history is seeded from
|
|
298
|
+
// the current trace rather than blending against a zero clear.
|
|
299
|
+
let force_refresh = if self.taa_frame_index == 0 { 1.0_f32 } else { 0.0_f32 };
|
|
300
|
+
let temporal_params = ProbeTemporalParams {
|
|
301
|
+
params: [0.25, force_refresh, gw as f32, gh as f32],
|
|
302
|
+
};
|
|
303
|
+
self.queue.write_buffer(&self.probe_temporal_uniform, 0, bytemuck::bytes_of(&temporal_params));
|
|
304
|
+
// Bind group indexed by write_idx: each direction of the
|
|
305
|
+
// ping-pong (read prev, write write) gets its own cached BG.
|
|
306
|
+
if self.probe_temporal_bg_cache[write_idx].is_none() {
|
|
307
|
+
self.probe_temporal_bg_cache[write_idx] = Some(self.device.create_bind_group(&wgpu::BindGroupDescriptor {
|
|
308
|
+
label: Some("probe_temporal_bg"),
|
|
309
|
+
layout: &self.probe_temporal_layout,
|
|
310
|
+
entries: &[
|
|
311
|
+
wgpu::BindGroupEntry { binding: 0, resource: self.probe_temporal_uniform.as_entire_binding() },
|
|
312
|
+
wgpu::BindGroupEntry { binding: 1, resource: wgpu::BindingResource::TextureView(&self.probe_trace_view) },
|
|
313
|
+
wgpu::BindGroupEntry { binding: 2, resource: wgpu::BindingResource::TextureView(&self.probe_history_views[prev_idx]) },
|
|
314
|
+
wgpu::BindGroupEntry { binding: 3, resource: wgpu::BindingResource::TextureView(&self.probe_history_views[write_idx]) },
|
|
315
|
+
],
|
|
316
|
+
}));
|
|
317
|
+
}
|
|
318
|
+
{
|
|
319
|
+
let ts = profiler.compute_pass_timestamp_writes("probe_temporal_pass");
|
|
320
|
+
let mut pass = encoder.begin_compute_pass(&wgpu::ComputePassDescriptor {
|
|
321
|
+
label: Some("probe_temporal_pass"), timestamp_writes: ts,
|
|
322
|
+
});
|
|
323
|
+
pass.set_pipeline(&self.probe_temporal_pipeline);
|
|
324
|
+
pass.set_bind_group(0, self.probe_temporal_bg_cache[write_idx].as_ref().unwrap(), &[]);
|
|
325
|
+
pass.dispatch_workgroups(gw, gh, 1);
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
// ---- resolve ----
|
|
329
|
+
let resolve_params = ProbeResolveParams {
|
|
330
|
+
inv_view,
|
|
331
|
+
proj_row01: [p00, p11, p20, p21],
|
|
332
|
+
size: [half_w, half_h, gw, gh],
|
|
333
|
+
params: [PROBE_TILE_SIZE as f32, 1.0, 0.0, 0.0],
|
|
334
|
+
};
|
|
335
|
+
self.queue.write_buffer(&self.probe_resolve_uniform, 0, bytemuck::bytes_of(&resolve_params));
|
|
336
|
+
if self.probe_resolve_bg_cache[write_idx].is_none() {
|
|
337
|
+
self.probe_resolve_bg_cache[write_idx] = Some(self.device.create_bind_group(&wgpu::BindGroupDescriptor {
|
|
338
|
+
label: Some("probe_resolve_bg"),
|
|
339
|
+
layout: &self.probe_resolve_layout,
|
|
340
|
+
entries: &[
|
|
341
|
+
wgpu::BindGroupEntry { binding: 0, resource: self.probe_resolve_uniform.as_entire_binding() },
|
|
342
|
+
wgpu::BindGroupEntry { binding: 1, resource: self.probe_header_buffer.as_entire_binding() },
|
|
343
|
+
wgpu::BindGroupEntry { binding: 2, resource: wgpu::BindingResource::TextureView(&self.probe_history_views[write_idx]) },
|
|
344
|
+
wgpu::BindGroupEntry { binding: 3, resource: wgpu::BindingResource::Sampler(&self.composite_sampler) },
|
|
345
|
+
wgpu::BindGroupEntry { binding: 4, resource: wgpu::BindingResource::TextureView(&self.hiz_views[0]) },
|
|
346
|
+
wgpu::BindGroupEntry { binding: 5, resource: wgpu::BindingResource::Sampler(&self.hiz_sampler) },
|
|
347
|
+
],
|
|
348
|
+
}));
|
|
349
|
+
}
|
|
350
|
+
let ts = profiler.pass_timestamp_writes("probe_resolve_pass");
|
|
351
|
+
let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
|
|
352
|
+
label: Some("probe_resolve_pass"),
|
|
353
|
+
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
|
|
354
|
+
view: &self.ssgi_rt_view,
|
|
355
|
+
resolve_target: None,
|
|
356
|
+
depth_slice: None,
|
|
357
|
+
ops: wgpu::Operations {
|
|
358
|
+
load: wgpu::LoadOp::Clear(wgpu::Color::TRANSPARENT),
|
|
359
|
+
store: wgpu::StoreOp::Store,
|
|
360
|
+
},
|
|
361
|
+
})],
|
|
362
|
+
depth_stencil_attachment: None,
|
|
363
|
+
timestamp_writes: ts,
|
|
364
|
+
occlusion_query_set: None,
|
|
365
|
+
multiview_mask: None,
|
|
366
|
+
});
|
|
367
|
+
pass.set_pipeline(&self.probe_resolve_pipeline);
|
|
368
|
+
pass.set_bind_group(0, self.probe_resolve_bg_cache[write_idx].as_ref().unwrap(), &[]);
|
|
369
|
+
pass.draw(0..3, 0..1);
|
|
370
|
+
} else {
|
|
371
|
+
// SSGI disabled — clear the resolve target so downstream
|
|
372
|
+
// composite reads contribute zero.
|
|
373
|
+
let pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
|
|
374
|
+
label: Some("ssgi_clear"),
|
|
375
|
+
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
|
|
376
|
+
view: &self.ssgi_rt_view,
|
|
377
|
+
resolve_target: None,
|
|
378
|
+
depth_slice: None,
|
|
379
|
+
ops: wgpu::Operations {
|
|
380
|
+
load: wgpu::LoadOp::Clear(wgpu::Color::TRANSPARENT),
|
|
381
|
+
store: wgpu::StoreOp::Store,
|
|
382
|
+
},
|
|
383
|
+
})],
|
|
384
|
+
depth_stencil_attachment: None,
|
|
385
|
+
timestamp_writes: None,
|
|
386
|
+
occlusion_query_set: None,
|
|
387
|
+
multiview_mask: None,
|
|
388
|
+
});
|
|
389
|
+
drop(pass);
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
}
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
//! Screen-space reflections: stochastic ray march + temporal denoiser.
|
|
2
|
+
//! Split from end_frame_with_scene (2000-line file policy + render-graph
|
|
3
|
+
//! migration prep). Both entry points no-op when `ssr_enabled` is false.
|
|
4
|
+
|
|
5
|
+
use super::{Renderer, SsrParams, SsrTemporalParams};
|
|
6
|
+
|
|
7
|
+
impl Renderer {
|
|
8
|
+
/// Quarter-res stochastic SSR ray march (GGX-sampled directions,
|
|
9
|
+
/// jittered starts; temporal accumulation makes it converge).
|
|
10
|
+
pub(super) fn record_ssr_march(
|
|
11
|
+
&mut self,
|
|
12
|
+
encoder: &mut wgpu::CommandEncoder,
|
|
13
|
+
profiler: &mut crate::profiler::Profiler,
|
|
14
|
+
) {
|
|
15
|
+
// ============================================================
|
|
16
|
+
// SSR: view-space ray march of the depth buffer + HDR sample.
|
|
17
|
+
// ============================================================
|
|
18
|
+
// PT-1: skipped while the path tracer owns the frame — it marches
|
|
19
|
+
// the raster-lit HDR, which PT has already overwritten.
|
|
20
|
+
if self.ssr_enabled && !self.pt_owns_frame() {
|
|
21
|
+
let inv_proj = self.current_inv_proj_matrix;
|
|
22
|
+
// EN-021 — view→world rotation for the env-miss fallback: the
|
|
23
|
+
// transpose of the view matrix's 3×3 (rigid view ⇒ inverse
|
|
24
|
+
// rotation = transpose). Column j of the inverse is row j of
|
|
25
|
+
// the view rotation.
|
|
26
|
+
let v = self.current_view_matrix;
|
|
27
|
+
let inv_view_rot = [
|
|
28
|
+
[v[0][0], v[1][0], v[2][0], 0.0],
|
|
29
|
+
[v[0][1], v[1][1], v[2][1], 0.0],
|
|
30
|
+
[v[0][2], v[1][2], v[2][2], 0.0],
|
|
31
|
+
[0.0, 0.0, 0.0, 1.0],
|
|
32
|
+
];
|
|
33
|
+
let sp = SsrParams {
|
|
34
|
+
inv_proj,
|
|
35
|
+
proj: self.current_proj_matrix,
|
|
36
|
+
// n_steps lowered from 32 → 8 for stochastic SSR: the
|
|
37
|
+
// GGX-sampled ray direction + jittered start offset +
|
|
38
|
+
// temporal accumulation over 4–8 frames fills in the
|
|
39
|
+
// gaps that any single-frame coarse march leaves behind.
|
|
40
|
+
// Thickness tolerance grows proportionally with
|
|
41
|
+
// step_size so the relative-error reject heuristic
|
|
42
|
+
// still works with the larger strides.
|
|
43
|
+
params: [self.ssr_strength, 8.0, 8.0, self.taa_frame_index as f32],
|
|
44
|
+
inv_view_rot,
|
|
45
|
+
// Env max LOD 6.0 matches the material path's roughness×6
|
|
46
|
+
// mip ramp; intensity rides lighting camera_pos.w exactly
|
|
47
|
+
// like sample_env does.
|
|
48
|
+
params2: [6.0, self.lighting_uniforms.camera_pos[3], 0.0, 0.0],
|
|
49
|
+
};
|
|
50
|
+
self.queue.write_buffer(&self.ssr_uniform_buffer, 0, bytemuck::bytes_of(&sp));
|
|
51
|
+
|
|
52
|
+
if self.ssr_bg_cache.is_none() {
|
|
53
|
+
// EN-021 — env panorama (or the 1×1 default) for the miss
|
|
54
|
+
// fallback. The cache is invalidated wherever the lighting
|
|
55
|
+
// bind group swaps env sources.
|
|
56
|
+
let env_view = self
|
|
57
|
+
.sky_texture
|
|
58
|
+
.as_ref()
|
|
59
|
+
.map(|t| t.create_view(&wgpu::TextureViewDescriptor::default()))
|
|
60
|
+
.unwrap_or_else(|| {
|
|
61
|
+
self._scene_env_default_texture
|
|
62
|
+
.create_view(&wgpu::TextureViewDescriptor::default())
|
|
63
|
+
});
|
|
64
|
+
self.ssr_bg_cache = Some(self.device.create_bind_group(&wgpu::BindGroupDescriptor {
|
|
65
|
+
label: Some("ssr_bg"),
|
|
66
|
+
layout: &self.ssr_layout,
|
|
67
|
+
entries: &[
|
|
68
|
+
wgpu::BindGroupEntry { binding: 0, resource: self.ssr_uniform_buffer.as_entire_binding() },
|
|
69
|
+
wgpu::BindGroupEntry { binding: 1, resource: wgpu::BindingResource::TextureView(&self.depth_view) },
|
|
70
|
+
wgpu::BindGroupEntry { binding: 2, resource: wgpu::BindingResource::Sampler(&self.ssao_depth_sampler) },
|
|
71
|
+
wgpu::BindGroupEntry { binding: 3, resource: wgpu::BindingResource::TextureView(&self.hdr_rt_view) },
|
|
72
|
+
wgpu::BindGroupEntry { binding: 4, resource: wgpu::BindingResource::Sampler(&self.composite_sampler) },
|
|
73
|
+
wgpu::BindGroupEntry { binding: 5, resource: wgpu::BindingResource::TextureView(&self.material_rt_view) },
|
|
74
|
+
wgpu::BindGroupEntry { binding: 6, resource: wgpu::BindingResource::Sampler(&self.composite_sampler) },
|
|
75
|
+
wgpu::BindGroupEntry { binding: 7, resource: wgpu::BindingResource::TextureView(&self.albedo_rt_view) },
|
|
76
|
+
wgpu::BindGroupEntry { binding: 8, resource: wgpu::BindingResource::Sampler(&self.composite_sampler) },
|
|
77
|
+
wgpu::BindGroupEntry { binding: 9, resource: wgpu::BindingResource::TextureView(&env_view) },
|
|
78
|
+
wgpu::BindGroupEntry { binding: 10, resource: wgpu::BindingResource::Sampler(&self.composite_sampler) },
|
|
79
|
+
],
|
|
80
|
+
}));
|
|
81
|
+
}
|
|
82
|
+
let bg = self.ssr_bg_cache.as_ref().unwrap();
|
|
83
|
+
let ssr_ts = profiler.pass_timestamp_writes("ssr_pass");
|
|
84
|
+
let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
|
|
85
|
+
label: Some("ssr_pass"),
|
|
86
|
+
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
|
|
87
|
+
view: &self.ssr_rt_view,
|
|
88
|
+
resolve_target: None,
|
|
89
|
+
depth_slice: None,
|
|
90
|
+
ops: wgpu::Operations {
|
|
91
|
+
load: wgpu::LoadOp::Clear(wgpu::Color::TRANSPARENT),
|
|
92
|
+
store: wgpu::StoreOp::Store,
|
|
93
|
+
},
|
|
94
|
+
})],
|
|
95
|
+
depth_stencil_attachment: None,
|
|
96
|
+
timestamp_writes: ssr_ts,
|
|
97
|
+
occlusion_query_set: None,
|
|
98
|
+
multiview_mask: None,
|
|
99
|
+
});
|
|
100
|
+
pass.set_pipeline(&self.ssr_pipeline);
|
|
101
|
+
pass.set_bind_group(0, bg, &[]);
|
|
102
|
+
pass.draw(0..3, 0..1);
|
|
103
|
+
} else {
|
|
104
|
+
// SSR disabled — clear the RT so TAA's read returns 0
|
|
105
|
+
// (transparent black). One-time clear is cheaper than a
|
|
106
|
+
// full clear+pipeline switch every frame.
|
|
107
|
+
let pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
|
|
108
|
+
label: Some("ssr_clear"),
|
|
109
|
+
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
|
|
110
|
+
view: &self.ssr_rt_view,
|
|
111
|
+
resolve_target: None,
|
|
112
|
+
depth_slice: None,
|
|
113
|
+
ops: wgpu::Operations {
|
|
114
|
+
load: wgpu::LoadOp::Clear(wgpu::Color::TRANSPARENT),
|
|
115
|
+
store: wgpu::StoreOp::Store,
|
|
116
|
+
},
|
|
117
|
+
})],
|
|
118
|
+
depth_stencil_attachment: None,
|
|
119
|
+
timestamp_writes: None,
|
|
120
|
+
occlusion_query_set: None,
|
|
121
|
+
multiview_mask: None,
|
|
122
|
+
});
|
|
123
|
+
drop(pass);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/// SSR temporal denoiser: 3x3 pre-filter + neighborhood-clamped
|
|
128
|
+
/// history blend; compose reads ssr_history[cur].
|
|
129
|
+
pub(super) fn record_ssr_temporal(
|
|
130
|
+
&mut self,
|
|
131
|
+
encoder: &mut wgpu::CommandEncoder,
|
|
132
|
+
) {
|
|
133
|
+
// ============================================================
|
|
134
|
+
// SSR temporal denoiser: blend the noisy single-ray SSR with
|
|
135
|
+
// the reprojected previous history so 4–8 frames of GGX-sampled
|
|
136
|
+
// rays converge to a smooth reflection. 3×3 pre-filter of the
|
|
137
|
+
// noisy current frame + neighborhood clamp of reprojected
|
|
138
|
+
// history. Compose then reads ssr_history[cur] instead of
|
|
139
|
+
// ssr_rt.
|
|
140
|
+
// ============================================================
|
|
141
|
+
// PT-1: same gate as the march — no fresh rays, nothing to blend.
|
|
142
|
+
if self.ssr_enabled && !self.pt_owns_frame() {
|
|
143
|
+
let prev_idx = 1 - self.ssr_history_idx;
|
|
144
|
+
let cur_idx = self.ssr_history_idx;
|
|
145
|
+
|
|
146
|
+
// First frame: alpha=1 so we initialize history from the
|
|
147
|
+
// current noisy frame rather than blending with zeros.
|
|
148
|
+
let alpha = if self.taa_frame_index == 0 { 1.0_f32 } else { 0.1_f32 };
|
|
149
|
+
let tp = SsrTemporalParams {
|
|
150
|
+
params: [alpha, 0.0, 0.0, 0.0],
|
|
151
|
+
};
|
|
152
|
+
self.queue.write_buffer(&self.ssr_temporal_uniform_buffer, 0, bytemuck::bytes_of(&tp));
|
|
153
|
+
|
|
154
|
+
let bg = self.device.create_bind_group(&wgpu::BindGroupDescriptor {
|
|
155
|
+
label: Some("ssr_temporal_bg"),
|
|
156
|
+
layout: &self.ssr_temporal_layout,
|
|
157
|
+
entries: &[
|
|
158
|
+
wgpu::BindGroupEntry { binding: 0, resource: self.ssr_temporal_uniform_buffer.as_entire_binding() },
|
|
159
|
+
wgpu::BindGroupEntry { binding: 1, resource: wgpu::BindingResource::TextureView(&self.ssr_rt_view) },
|
|
160
|
+
wgpu::BindGroupEntry { binding: 2, resource: wgpu::BindingResource::Sampler(&self.composite_sampler) },
|
|
161
|
+
wgpu::BindGroupEntry { binding: 3, resource: wgpu::BindingResource::TextureView(&self.ssr_history_views[prev_idx]) },
|
|
162
|
+
wgpu::BindGroupEntry { binding: 4, resource: wgpu::BindingResource::Sampler(&self.composite_sampler) },
|
|
163
|
+
wgpu::BindGroupEntry { binding: 5, resource: wgpu::BindingResource::TextureView(&self.velocity_rt_view) },
|
|
164
|
+
wgpu::BindGroupEntry { binding: 6, resource: wgpu::BindingResource::Sampler(&self.composite_sampler) },
|
|
165
|
+
],
|
|
166
|
+
});
|
|
167
|
+
let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
|
|
168
|
+
label: Some("ssr_temporal_pass"),
|
|
169
|
+
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
|
|
170
|
+
view: &self.ssr_history_views[cur_idx],
|
|
171
|
+
resolve_target: None,
|
|
172
|
+
depth_slice: None,
|
|
173
|
+
ops: wgpu::Operations {
|
|
174
|
+
load: wgpu::LoadOp::Clear(wgpu::Color::TRANSPARENT),
|
|
175
|
+
store: wgpu::StoreOp::Store,
|
|
176
|
+
},
|
|
177
|
+
})],
|
|
178
|
+
depth_stencil_attachment: None,
|
|
179
|
+
timestamp_writes: None,
|
|
180
|
+
occlusion_query_set: None,
|
|
181
|
+
multiview_mask: None,
|
|
182
|
+
});
|
|
183
|
+
pass.set_pipeline(&self.ssr_temporal_pipeline);
|
|
184
|
+
pass.set_bind_group(0, &bg, &[]);
|
|
185
|
+
pass.draw(0..3, 0..1);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
}
|