Skip to content

Commit 8594643

Browse files
committed
Use packed light cookie atlases
1 parent 3a78d20 commit 8594643

27 files changed

Lines changed: 917 additions & 262 deletions

File tree

crates/renderide/shaders/modules/frame/globals.wgsl

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@
77
//! layout at pipeline creation for storage-backed frame resources.
88
//!
99
//! CPU packing must match [`crate::gpu::frame_globals::FrameGpuUniforms`],
10-
//! [`crate::backend::light_gpu::GpuLight`], and [`crate::backend::cluster_gpu`] cluster buffers.
10+
//! [`crate::backend::light_gpu::GpuLight`], [`crate::gpu::GpuLightCookieRect`], and
11+
//! [`crate::backend::cluster_gpu`] cluster buffers.
1112

1213
#define_import_path renderide::frame::globals
1314

@@ -26,12 +27,13 @@
2627
@group(0) @binding(10) var reflection_probe_specular_sampler: sampler;
2728
@group(0) @binding(11) var ibl_dfg_lut: texture_2d<f32>;
2829
@group(0) @binding(12) var<storage, read> reflection_probes: array<ft::GpuReflectionProbe>;
29-
@group(0) @binding(13) var light_cookie_2d_atlas: texture_2d_array<f32>;
30-
@group(0) @binding(14) var light_cookie_point_atlas: texture_2d_array<f32>;
30+
@group(0) @binding(13) var light_cookie_2d_atlas: texture_2d<f32>;
31+
@group(0) @binding(14) var light_cookie_point_atlas: texture_2d<f32>;
3132
@group(0) @binding(15) var light_cookie_sampler: sampler;
3233
@group(0) @binding(16) var<storage, read> shadow_views: array<ft::GpuShadowView>;
3334
@group(0) @binding(17) var shadow_atlas: texture_depth_2d_array;
3435
@group(0) @binding(18) var shadow_sampler: sampler_comparison;
36+
@group(0) @binding(19) var<storage, read> light_cookie_rects: array<ft::GpuLightCookieRect>;
3537

3638
/// View index encoded in a material varying.
3739
fn view_index_from_layer(view_layer: u32) -> u32 {
@@ -183,7 +185,8 @@ fn retain_globals_additive(color: vec4<f32>) -> vec4<f32> {
183185
f32(cluster_light_indices[0u] & 255u) * 1e-10;
184186
let probe_touch = reflection_probes[0u].params.x * 1e-10;
185187
let cookie_touch =
186-
textureSampleLevel(light_cookie_2d_atlas, light_cookie_sampler, vec2<f32>(0.5), 0, 0.0).r * 1e-10 +
187-
textureSampleLevel(light_cookie_point_atlas, light_cookie_sampler, vec2<f32>(0.5), 0, 0.0).r * 1e-10;
188+
textureSampleLevel(light_cookie_2d_atlas, light_cookie_sampler, vec2<f32>(0.5), 0.0).r * 1e-10 +
189+
textureSampleLevel(light_cookie_point_atlas, light_cookie_sampler, vec2<f32>(0.5), 0.0).r * 1e-10 +
190+
light_cookie_rects[0u].origin_scale.x * 1e-10;
188191
return color + vec4<f32>(vec3<f32>(f32(lit) * 1e-10 + cluster_touch + probe_touch + cookie_touch), 0.0);
189192
}

crates/renderide/shaders/modules/frame/types.wgsl

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,10 @@ struct GpuShadowView {
5555
light_params: vec4<f32>,
5656
}
5757

58+
struct GpuLightCookieRect {
59+
origin_scale: vec4<f32>,
60+
}
61+
5862
struct GpuReflectionProbe {
5963
/// World-space AABB minimum; `.w` stores the sanitized blend distance.
6064
box_min: vec4<f32>,

crates/renderide/shaders/modules/lighting/light_cookies.wgsl

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,12 +39,16 @@ fn wrap_cookie_uv(uv: vec2<f32>, bits: u32) -> vec2<f32> {
3939
);
4040
}
4141

42+
fn atlas_cookie_uv(rect_index: u32, uv: vec2<f32>) -> vec2<f32> {
43+
let rect = rg::light_cookie_rects[rect_index].origin_scale;
44+
return rect.xy + uv * rect.zw;
45+
}
46+
4247
fn sample_2d_cookie(light: ft::GpuLight, uv: vec2<f32>) -> f32 {
4348
return textureSample(
4449
rg::light_cookie_2d_atlas,
4550
rg::light_cookie_sampler,
46-
wrap_cookie_uv(uv, light.cookie_reserved),
47-
i32(light.cookie_layer),
51+
atlas_cookie_uv(light.cookie_layer, wrap_cookie_uv(uv, light.cookie_reserved)),
4852
).r;
4953
}
5054

@@ -114,8 +118,12 @@ fn point_cookie_multiplier(light: ft::GpuLight, world_pos: vec3<f32>) -> f32 {
114118
dot(from_light, light.direction),
115119
) * inverseSqrt(len_sq);
116120
let face_uv = cube_face_uv(local);
117-
let layer = light.cookie_layer + u32(face_uv.z);
118-
return textureSample(rg::light_cookie_point_atlas, rg::light_cookie_sampler, face_uv.xy, i32(layer)).r;
121+
let rect_index = light.cookie_layer + u32(face_uv.z);
122+
return textureSample(
123+
rg::light_cookie_point_atlas,
124+
rg::light_cookie_sampler,
125+
atlas_cookie_uv(rect_index, face_uv.xy),
126+
).r;
119127
}
120128

121129
fn multiplier(light: ft::GpuLight, world_pos: vec3<f32>) -> f32 {

crates/renderide/src/backend/facade/graph_access.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -379,6 +379,20 @@ impl GraphExecutionBackend for BackendGraphAccess<'_> {
379379
);
380380
}
381381

382+
fn pre_record_sync_for_views(
383+
&mut self,
384+
device: &wgpu::Device,
385+
uploads: GraphUploadSink<'_>,
386+
view_layouts: &[PreRecordViewResourceLayout],
387+
) {
388+
self.frame_resources.pre_record_sync_for_views(
389+
device,
390+
uploads,
391+
self.asset_transfers,
392+
view_layouts,
393+
);
394+
}
395+
382396
fn view_blackboard_preparer(&self) -> Box<dyn GraphViewBlackboardPreparer + '_> {
383397
Box::new(BackendViewBlackboardPreparer {
384398
world_mesh_frame_planner: self.world_mesh_frame_planner,

crates/renderide/src/backend/frame_gpu.rs

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ struct FrameBindGroupInputs<'a> {
119119
reflection_probes: ReflectionProbeSpecularBindGroupResources<'a>,
120120
/// Integrated BRDF lookup texture view for binding 11.
121121
ibl_dfg_lut_view: &'a wgpu::TextureView,
122-
/// Light-cookie atlas textures and sampler for bindings 13 through 15.
122+
/// Light-cookie atlas textures, sampler, and rect metadata for bindings 13 through 15 and 19.
123123
light_cookies: &'a LightCookieAtlasResources,
124124
/// Shadow-map metadata, atlas texture, and comparison sampler for bindings 16 through 18.
125125
shadows: &'a ShadowAtlasResources,
@@ -128,7 +128,7 @@ struct FrameBindGroupInputs<'a> {
128128
fn frame_bind_group_entries<'a>(
129129
inputs: &FrameBindGroupInputs<'a>,
130130
) -> Vec<wgpu::BindGroupEntry<'a>> {
131-
let mut entries = Vec::with_capacity(19);
131+
let mut entries = Vec::with_capacity(20);
132132
append_frame_and_cluster_entries(&mut entries, inputs);
133133
append_scene_snapshot_entries(&mut entries, inputs);
134134
append_reflection_probe_entries(&mut entries, inputs);
@@ -233,6 +233,10 @@ fn append_light_cookie_entries<'a>(
233233
binding: 15,
234234
resource: wgpu::BindingResource::Sampler(inputs.light_cookies.sampler()),
235235
},
236+
wgpu::BindGroupEntry {
237+
binding: 19,
238+
resource: inputs.light_cookies.metadata_buffer().as_entire_binding(),
239+
},
236240
]);
237241
}
238242

@@ -623,6 +627,25 @@ impl FrameGpuResources {
623627
self.light_cookies.has_requests()
624628
}
625629

630+
/// Current light-cookie atlas bind-resource version for per-view bind-group invalidation.
631+
pub fn light_cookie_resources_version(&self) -> u64 {
632+
self.light_cookies.version()
633+
}
634+
635+
/// Synchronizes light-cookie atlas capacity and rect metadata before graph recording.
636+
pub fn sync_light_cookie_resources(
637+
&mut self,
638+
device: &wgpu::Device,
639+
uploads: GraphUploadSink<'_>,
640+
assets: &dyn crate::render_graph::GraphAssetResources,
641+
) -> bool {
642+
let changed = self.light_cookies.sync(device, uploads, assets);
643+
if changed {
644+
self.rebuild_bind_group(device);
645+
}
646+
changed
647+
}
648+
626649
/// Records light-cookie atlas updates.
627650
pub(in crate::backend) fn encode_light_cookie_atlas(
628651
&self,

crates/renderide/src/backend/frame_gpu/light_cookies.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ mod atlas;
88
mod blit;
99
/// Texture format and sampler compatibility helpers.
1010
mod format;
11+
/// CPU-side packed atlas layout.
12+
mod packing;
1113
/// Render-graph pass wrapper for atlas updates.
1214
mod pass;
1315
/// Persistent GPU resources for light-cookie atlases.

crates/renderide/src/backend/frame_gpu/light_cookies/assignment.rs

Lines changed: 28 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,20 @@ use crate::shared::LightType;
99

1010
use super::POINT_COOKIE_FACE_COUNT;
1111

12-
/// One requested light-cookie source assigned to an atlas layer.
12+
/// Fallback metadata row for white cookie sampling.
13+
pub(super) const LIGHT_COOKIE_FALLBACK_RECT_INDEX: u32 = 0;
14+
/// Maximum resident 2D cookies.
15+
pub(super) const COOKIE_2D_RECT_CAP: u32 = 64;
16+
/// Maximum resident point-light cookie cubemaps.
17+
pub(super) const POINT_COOKIE_CUBEMAP_CAP: u32 = 16;
18+
/// First metadata row reserved for point-light cubemap face cookies.
19+
pub(super) const POINT_COOKIE_RECT_BASE: u32 =
20+
LIGHT_COOKIE_FALLBACK_RECT_INDEX + 1 + COOKIE_2D_RECT_CAP;
21+
/// Total light-cookie metadata rows bound for all atlas kinds.
22+
pub(super) const LIGHT_COOKIE_RECT_CAPACITY: usize =
23+
(POINT_COOKIE_RECT_BASE + POINT_COOKIE_CUBEMAP_CAP * POINT_COOKIE_FACE_COUNT) as usize;
24+
25+
/// One requested light-cookie source assigned to atlas metadata rows.
1326
#[derive(Clone, Copy, Debug)]
1427
pub(super) struct LightCookieRequest {
1528
/// Packed host texture handle.
@@ -18,7 +31,7 @@ pub(super) struct LightCookieRequest {
1831
pub(super) asset_id: i32,
1932
/// Unpacked host texture kind.
2033
pub(super) kind: HostTextureAssetKind,
21-
/// 2D atlas layer or first point face layer.
34+
/// 2D atlas rect index or first point face rect index.
2235
pub(super) layer: u32,
2336
}
2437

@@ -37,10 +50,10 @@ pub(super) struct LightCookieAssignment {
3750
pub(super) wrap_bits: u32,
3851
}
3952

40-
/// Atlas slot state for a packed host texture handle.
53+
/// Atlas metadata slot state for a packed host texture handle.
4154
#[derive(Clone, Copy, Debug)]
4255
struct LightCookieSlot {
43-
/// Atlas layer assigned to this packed handle.
56+
/// Atlas metadata row assigned to this packed handle.
4457
layer: u32,
4558
/// Whether this slot is referenced by the current frame's packed lights.
4659
requested_this_frame: bool,
@@ -89,12 +102,7 @@ impl LightCookieAtlasState {
89102
}
90103

91104
/// Assigns a cookie atlas binding for one resolved light.
92-
pub(super) fn assign(
93-
&mut self,
94-
assignment: LightCookieAssignment,
95-
two_d_layers: u32,
96-
point_layers: u32,
97-
) -> LightCookieBinding {
105+
pub(super) fn assign(&mut self, assignment: LightCookieAssignment) -> LightCookieBinding {
98106
match (assignment.light_type, assignment.kind) {
99107
(
100108
LightType::Spot,
@@ -105,7 +113,6 @@ impl LightCookieAtlasState {
105113
assignment.packed_id,
106114
assignment.asset_id,
107115
assignment.kind,
108-
two_d_layers,
109116
LIGHT_COOKIE_KIND_SPOT_2D,
110117
assignment.wrap_bits,
111118
),
@@ -118,35 +125,30 @@ impl LightCookieAtlasState {
118125
assignment.packed_id,
119126
assignment.asset_id,
120127
assignment.kind,
121-
two_d_layers,
122128
LIGHT_COOKIE_KIND_DIRECTIONAL_2D,
123129
assignment.wrap_bits,
124130
),
125-
(LightType::Point, HostTextureAssetKind::Cubemap) => self.assign_point(
126-
assignment.packed_id,
127-
assignment.asset_id,
128-
assignment.kind,
129-
point_layers,
130-
),
131+
(LightType::Point, HostTextureAssetKind::Cubemap) => {
132+
self.assign_point(assignment.packed_id, assignment.asset_id, assignment.kind)
133+
}
131134
_ => LightCookieBinding::NONE,
132135
}
133136
}
134137

135-
/// Assigns a 2D cookie layer.
138+
/// Assigns a 2D cookie metadata row.
136139
fn assign_2d(
137140
&mut self,
138141
packed_id: i32,
139142
asset_id: i32,
140143
kind: HostTextureAssetKind,
141-
layers: u32,
142144
cookie_kind: u32,
143145
wrap_bits: u32,
144146
) -> LightCookieBinding {
145147
let Some(layer) = assign_cookie_layer(
146148
&mut self.two_d_slots,
147149
packed_id,
148-
1,
149-
layers,
150+
LIGHT_COOKIE_FALLBACK_RECT_INDEX + 1,
151+
POINT_COOKIE_RECT_BASE,
150152
1,
151153
&mut self.two_d_overflow_logged,
152154
"2D",
@@ -171,19 +173,18 @@ impl LightCookieAtlasState {
171173
}
172174
}
173175

174-
/// Assigns six 2D-array layers for a point-light cubemap cookie.
176+
/// Assigns six metadata rows for a point-light cubemap cookie.
175177
fn assign_point(
176178
&mut self,
177179
packed_id: i32,
178180
asset_id: i32,
179181
kind: HostTextureAssetKind,
180-
layers: u32,
181182
) -> LightCookieBinding {
182183
let Some(layer) = assign_cookie_layer(
183184
&mut self.point_slots,
184185
packed_id,
185-
1,
186-
layers,
186+
POINT_COOKIE_RECT_BASE,
187+
LIGHT_COOKIE_RECT_CAPACITY as u32,
187188
POINT_COOKIE_FACE_COUNT,
188189
&mut self.point_overflow_logged,
189190
"point",
@@ -219,7 +220,7 @@ impl LightCookieAtlasState {
219220
}
220221
}
221222

222-
/// Assigns or reuses one atlas layer block.
223+
/// Assigns or reuses one atlas metadata row block.
223224
fn assign_cookie_layer(
224225
slots: &mut HashMap<i32, LightCookieSlot>,
225226
packed_id: i32,

0 commit comments

Comments
 (0)