HD: bind groups cached until a resize or a new source, shadow passes draw only the entities that cast shadows
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -459,6 +459,8 @@ pub struct Renderer {
|
||||
entity_buffer: wgpu::Buffer,
|
||||
entity_capacity: u64,
|
||||
entity_batch: EntityBatch,
|
||||
/// AirDef fork: the runs of entity quads (first, end) that cast shadows this frame.
|
||||
shadow_casters: Vec<(u32, u32)>,
|
||||
group: wgpu::BindGroup,
|
||||
globals: wgpu::Buffer,
|
||||
lightmap: wgpu::Texture,
|
||||
@@ -2744,6 +2746,7 @@ impl Renderer {
|
||||
entity_buffer,
|
||||
entity_capacity,
|
||||
entity_batch: EntityBatch::default(),
|
||||
shadow_casters: Vec::new(),
|
||||
group,
|
||||
globals,
|
||||
lightmap,
|
||||
@@ -4980,6 +4983,30 @@ impl Renderer {
|
||||
}
|
||||
// The sun's (or moon's) shadow cascades: the world's globals swapped for the light's.
|
||||
if shadow_strength > 0.0 {
|
||||
// The quads the shadow shader would not discard (overlays 4, 9, 10 and 40:
|
||||
// glowing mod geometry), as runs, so those are not vertex-processed twice.
|
||||
self.shadow_casters.clear();
|
||||
let mut start = None;
|
||||
let quads = self.entity_batch.combined.as_chunks::<4>().0;
|
||||
for (i, quad) in quads.iter().take(entity_quads as usize).enumerate() {
|
||||
let caster = !matches!(quad[0].layer, 4 | 9 | 10 | 40);
|
||||
match (caster, start) {
|
||||
(true, None) => start = Some(i as u32),
|
||||
(false, Some(first)) => {
|
||||
self.shadow_casters.push((first, i as u32));
|
||||
start = None;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
if let Some(first) = start {
|
||||
self.shadow_casters.push((first, entity_quads));
|
||||
}
|
||||
// Many tiny runs cost more in draw calls than they save: draw everything.
|
||||
if self.shadow_casters.len() > 64 {
|
||||
self.shadow_casters.clear();
|
||||
self.shadow_casters.push((0, entity_quads));
|
||||
}
|
||||
self.queue
|
||||
.write_buffer(&hd.main_globals, 0, bytemuck::bytes_of(&globals));
|
||||
for (c, cascade) in hd_cascades.iter().enumerate() {
|
||||
@@ -5021,11 +5048,13 @@ impl Renderer {
|
||||
let instance = hd_shadow_first + k as u32;
|
||||
pass.draw_indexed(0..s.solid * 6, s.start as i32, instance..instance + 1);
|
||||
}
|
||||
if entity_quads > 0 {
|
||||
if !self.shadow_casters.is_empty() {
|
||||
pass.set_pipeline(&self.shadow_entity);
|
||||
pass.set_bind_group(0, &self.entity_group, &[]);
|
||||
pass.set_vertex_buffer(0, self.entity_buffer.slice(..));
|
||||
pass.draw_indexed(0..entity_quads * 6, 0, 0..1);
|
||||
for &(first, end) in &self.shadow_casters {
|
||||
pass.draw_indexed(first * 6..end * 6, 0, 0..1);
|
||||
}
|
||||
}
|
||||
}
|
||||
encoder.copy_buffer_to_buffer(&hd.main_globals, 0, &self.globals, 0, globals_size);
|
||||
@@ -5586,7 +5615,7 @@ impl Renderer {
|
||||
);
|
||||
} else if let Some((color, depth)) = &scaled
|
||||
&& hd_on
|
||||
&& let Some(hd) = &self.hd
|
||||
&& let Some(hd) = &mut self.hd
|
||||
{
|
||||
// AirDef fork: lit by the mods' lights into the HD target, the mod's post pass on
|
||||
// that, then bloom and the tone curve onto the screen.
|
||||
@@ -5607,9 +5636,9 @@ impl Renderer {
|
||||
self.mod_params.1,
|
||||
);
|
||||
post.run(&self.device, &mut encoder, (&hd.lit, depth), &hd.fx);
|
||||
hd.finish(&self.device, &mut encoder, &hd.fx, &view_target);
|
||||
hd.finish(&self.device, &mut encoder, hd::HdSource::Fx, &view_target);
|
||||
}
|
||||
_ => hd.finish(&self.device, &mut encoder, &hd.lit, &view_target),
|
||||
_ => hd.finish(&self.device, &mut encoder, hd::HdSource::Lit, &view_target),
|
||||
}
|
||||
} else if let Some((color, depth)) = &scaled
|
||||
&& mod_post
|
||||
|
||||
@@ -359,6 +359,21 @@ fn target(
|
||||
Target { view }
|
||||
}
|
||||
|
||||
/// The outside views and sampler the lighting group reads.
|
||||
type LightKey = (
|
||||
wgpu::TextureView,
|
||||
wgpu::TextureView,
|
||||
wgpu::TextureView,
|
||||
wgpu::Sampler,
|
||||
);
|
||||
|
||||
/// What bloom and the tone curve read: the lit world, or the post pass's output.
|
||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||
pub enum HdSource {
|
||||
Lit,
|
||||
Fx,
|
||||
}
|
||||
|
||||
pub struct Hd {
|
||||
size: [u32; 2],
|
||||
pub albedo: wgpu::TextureView,
|
||||
@@ -377,6 +392,15 @@ pub struct Hd {
|
||||
pub lit: wgpu::TextureView,
|
||||
pub fx: wgpu::TextureView,
|
||||
bloom: Vec<Target>,
|
||||
/// Bind groups, rebuilt only when their views change: `resize` drops all of them, the
|
||||
/// lighting and the source-reading ones are keyed by the views they read.
|
||||
light_group: Option<(LightKey, wgpu::BindGroup)>,
|
||||
prefilter_group: Option<(wgpu::TextureView, wgpu::BindGroup)>,
|
||||
tone_group: Option<(wgpu::TextureView, wgpu::BindGroup)>,
|
||||
/// Reading bloom level k - 1 (for the down pass into level k, index k - 1) and level k (for
|
||||
/// the up pass onto level k - 1, index k - 1).
|
||||
down_groups: Vec<wgpu::BindGroup>,
|
||||
up_groups: Vec<wgpu::BindGroup>,
|
||||
frame: wgpu::Buffer,
|
||||
lights: wgpu::Buffer,
|
||||
sampler: wgpu::Sampler,
|
||||
@@ -656,6 +680,11 @@ impl Hd {
|
||||
lit: target(device, "HD lit", [1, 1], HDR).view,
|
||||
fx: target(device, "HD post", [1, 1], HDR).view,
|
||||
bloom: Vec::new(),
|
||||
light_group: None,
|
||||
prefilter_group: None,
|
||||
tone_group: None,
|
||||
down_groups: Vec::new(),
|
||||
up_groups: Vec::new(),
|
||||
frame,
|
||||
lights,
|
||||
sampler,
|
||||
@@ -690,6 +719,16 @@ impl Hd {
|
||||
target(device, "HD bloom", s, HDR)
|
||||
})
|
||||
.collect();
|
||||
// Every group names a view that was just replaced.
|
||||
self.light_group = None;
|
||||
self.prefilter_group = None;
|
||||
self.tone_group = None;
|
||||
self.down_groups = (1..self.bloom.len())
|
||||
.map(|k| self.bloom_group(device, &self.bloom[k - 1].view))
|
||||
.collect();
|
||||
self.up_groups = (1..self.bloom.len())
|
||||
.map(|k| self.bloom_group(device, &self.bloom[k].view))
|
||||
.collect();
|
||||
}
|
||||
|
||||
/// This frame's camera, sun, fog, lights and settings.
|
||||
@@ -829,73 +868,85 @@ impl Hd {
|
||||
|
||||
/// Lights the world (its colour and depth) into `lit`.
|
||||
pub fn light(
|
||||
&self,
|
||||
&mut self,
|
||||
device: &wgpu::Device,
|
||||
encoder: &mut wgpu::CommandEncoder,
|
||||
world: (&wgpu::TextureView, &wgpu::TextureView),
|
||||
lightmap: (&wgpu::TextureView, &wgpu::Sampler),
|
||||
) {
|
||||
let group = device.create_bind_group(&wgpu::BindGroupDescriptor {
|
||||
label: Some("HD lighting"),
|
||||
layout: &self.light_layout,
|
||||
entries: &[
|
||||
wgpu::BindGroupEntry {
|
||||
binding: 0,
|
||||
resource: self.frame.as_entire_binding(),
|
||||
},
|
||||
wgpu::BindGroupEntry {
|
||||
binding: 1,
|
||||
resource: wgpu::BindingResource::TextureView(world.0),
|
||||
},
|
||||
wgpu::BindGroupEntry {
|
||||
binding: 2,
|
||||
resource: wgpu::BindingResource::TextureView(world.1),
|
||||
},
|
||||
wgpu::BindGroupEntry {
|
||||
binding: 3,
|
||||
resource: wgpu::BindingResource::TextureView(&self.albedo),
|
||||
},
|
||||
wgpu::BindGroupEntry {
|
||||
binding: 4,
|
||||
resource: wgpu::BindingResource::TextureView(&self.normal),
|
||||
},
|
||||
wgpu::BindGroupEntry {
|
||||
binding: 5,
|
||||
resource: wgpu::BindingResource::TextureView(&self.depth),
|
||||
},
|
||||
wgpu::BindGroupEntry {
|
||||
binding: 6,
|
||||
resource: self.lights.as_entire_binding(),
|
||||
},
|
||||
wgpu::BindGroupEntry {
|
||||
binding: 7,
|
||||
resource: wgpu::BindingResource::TextureView(&self.levels),
|
||||
},
|
||||
wgpu::BindGroupEntry {
|
||||
binding: 8,
|
||||
resource: wgpu::BindingResource::TextureView(&self.shadow_array),
|
||||
},
|
||||
wgpu::BindGroupEntry {
|
||||
binding: 9,
|
||||
resource: wgpu::BindingResource::Sampler(&self.shadow_sampler),
|
||||
},
|
||||
wgpu::BindGroupEntry {
|
||||
binding: 10,
|
||||
resource: wgpu::BindingResource::TextureView(lightmap.0),
|
||||
},
|
||||
wgpu::BindGroupEntry {
|
||||
binding: 11,
|
||||
resource: wgpu::BindingResource::Sampler(lightmap.1),
|
||||
},
|
||||
],
|
||||
});
|
||||
let key = (
|
||||
world.0.clone(),
|
||||
world.1.clone(),
|
||||
lightmap.0.clone(),
|
||||
lightmap.1.clone(),
|
||||
);
|
||||
if self.light_group.as_ref().is_none_or(|(k, _)| *k != key) {
|
||||
let group = device.create_bind_group(&wgpu::BindGroupDescriptor {
|
||||
label: Some("HD lighting"),
|
||||
layout: &self.light_layout,
|
||||
entries: &[
|
||||
wgpu::BindGroupEntry {
|
||||
binding: 0,
|
||||
resource: self.frame.as_entire_binding(),
|
||||
},
|
||||
wgpu::BindGroupEntry {
|
||||
binding: 1,
|
||||
resource: wgpu::BindingResource::TextureView(world.0),
|
||||
},
|
||||
wgpu::BindGroupEntry {
|
||||
binding: 2,
|
||||
resource: wgpu::BindingResource::TextureView(world.1),
|
||||
},
|
||||
wgpu::BindGroupEntry {
|
||||
binding: 3,
|
||||
resource: wgpu::BindingResource::TextureView(&self.albedo),
|
||||
},
|
||||
wgpu::BindGroupEntry {
|
||||
binding: 4,
|
||||
resource: wgpu::BindingResource::TextureView(&self.normal),
|
||||
},
|
||||
wgpu::BindGroupEntry {
|
||||
binding: 5,
|
||||
resource: wgpu::BindingResource::TextureView(&self.depth),
|
||||
},
|
||||
wgpu::BindGroupEntry {
|
||||
binding: 6,
|
||||
resource: self.lights.as_entire_binding(),
|
||||
},
|
||||
wgpu::BindGroupEntry {
|
||||
binding: 7,
|
||||
resource: wgpu::BindingResource::TextureView(&self.levels),
|
||||
},
|
||||
wgpu::BindGroupEntry {
|
||||
binding: 8,
|
||||
resource: wgpu::BindingResource::TextureView(&self.shadow_array),
|
||||
},
|
||||
wgpu::BindGroupEntry {
|
||||
binding: 9,
|
||||
resource: wgpu::BindingResource::Sampler(&self.shadow_sampler),
|
||||
},
|
||||
wgpu::BindGroupEntry {
|
||||
binding: 10,
|
||||
resource: wgpu::BindingResource::TextureView(lightmap.0),
|
||||
},
|
||||
wgpu::BindGroupEntry {
|
||||
binding: 11,
|
||||
resource: wgpu::BindingResource::Sampler(lightmap.1),
|
||||
},
|
||||
],
|
||||
});
|
||||
self.light_group = Some((key, group));
|
||||
}
|
||||
let Some((_, group)) = &self.light_group else {
|
||||
return;
|
||||
};
|
||||
Self::draw(
|
||||
encoder,
|
||||
"HD lighting",
|
||||
&self.lit,
|
||||
false,
|
||||
&self.light_pipeline,
|
||||
&group,
|
||||
group,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -922,12 +973,53 @@ impl Hd {
|
||||
|
||||
/// Bloom from `source` (the lit world or the post pass's output), then the tone curve onto `screen`.
|
||||
pub fn finish(
|
||||
&self,
|
||||
&mut self,
|
||||
device: &wgpu::Device,
|
||||
encoder: &mut wgpu::CommandEncoder,
|
||||
source: &wgpu::TextureView,
|
||||
source: HdSource,
|
||||
screen: &wgpu::TextureView,
|
||||
) {
|
||||
let source = match source {
|
||||
HdSource::Lit => self.lit.clone(),
|
||||
HdSource::Fx => self.fx.clone(),
|
||||
};
|
||||
if self
|
||||
.prefilter_group
|
||||
.as_ref()
|
||||
.is_none_or(|(v, _)| *v != source)
|
||||
{
|
||||
let group = self.bloom_group(device, &source);
|
||||
self.prefilter_group = Some((source.clone(), group));
|
||||
}
|
||||
if self.tone_group.as_ref().is_none_or(|(v, _)| *v != source) {
|
||||
let group = device.create_bind_group(&wgpu::BindGroupDescriptor {
|
||||
label: Some("HD tone mapping"),
|
||||
layout: &self.tone_layout,
|
||||
entries: &[
|
||||
wgpu::BindGroupEntry {
|
||||
binding: 0,
|
||||
resource: self.frame.as_entire_binding(),
|
||||
},
|
||||
wgpu::BindGroupEntry {
|
||||
binding: 1,
|
||||
resource: wgpu::BindingResource::TextureView(&source),
|
||||
},
|
||||
wgpu::BindGroupEntry {
|
||||
binding: 2,
|
||||
resource: wgpu::BindingResource::TextureView(&self.bloom[0].view),
|
||||
},
|
||||
wgpu::BindGroupEntry {
|
||||
binding: 3,
|
||||
resource: wgpu::BindingResource::Sampler(&self.sampler),
|
||||
},
|
||||
],
|
||||
});
|
||||
self.tone_group = Some((source, group));
|
||||
}
|
||||
let (Some((_, prefilter)), Some((_, tone))) = (&self.prefilter_group, &self.tone_group)
|
||||
else {
|
||||
return;
|
||||
};
|
||||
// Down: the prefilter into the first level, then each level from the one above.
|
||||
Self::draw(
|
||||
encoder,
|
||||
@@ -935,61 +1027,30 @@ impl Hd {
|
||||
&self.bloom[0].view,
|
||||
false,
|
||||
&self.prefilter,
|
||||
&self.bloom_group(device, source),
|
||||
prefilter,
|
||||
);
|
||||
for k in 1..self.bloom.len() {
|
||||
let g = self.bloom_group(device, &self.bloom[k - 1].view);
|
||||
Self::draw(
|
||||
encoder,
|
||||
"HD bloom down",
|
||||
&self.bloom[k].view,
|
||||
false,
|
||||
&self.down,
|
||||
&g,
|
||||
&self.down_groups[k - 1],
|
||||
);
|
||||
}
|
||||
// Up: each level added onto the bigger one.
|
||||
for k in (1..self.bloom.len()).rev() {
|
||||
let g = self.bloom_group(device, &self.bloom[k].view);
|
||||
Self::draw(
|
||||
encoder,
|
||||
"HD bloom up",
|
||||
&self.bloom[k - 1].view,
|
||||
true,
|
||||
&self.up,
|
||||
&g,
|
||||
&self.up_groups[k - 1],
|
||||
);
|
||||
}
|
||||
let group = device.create_bind_group(&wgpu::BindGroupDescriptor {
|
||||
label: Some("HD tone mapping"),
|
||||
layout: &self.tone_layout,
|
||||
entries: &[
|
||||
wgpu::BindGroupEntry {
|
||||
binding: 0,
|
||||
resource: self.frame.as_entire_binding(),
|
||||
},
|
||||
wgpu::BindGroupEntry {
|
||||
binding: 1,
|
||||
resource: wgpu::BindingResource::TextureView(source),
|
||||
},
|
||||
wgpu::BindGroupEntry {
|
||||
binding: 2,
|
||||
resource: wgpu::BindingResource::TextureView(&self.bloom[0].view),
|
||||
},
|
||||
wgpu::BindGroupEntry {
|
||||
binding: 3,
|
||||
resource: wgpu::BindingResource::Sampler(&self.sampler),
|
||||
},
|
||||
],
|
||||
});
|
||||
Self::draw(
|
||||
encoder,
|
||||
"HD tone mapping",
|
||||
screen,
|
||||
false,
|
||||
&self.tone,
|
||||
&group,
|
||||
);
|
||||
Self::draw(encoder, "HD tone mapping", screen, false, &self.tone, tone);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user