use valo_geometry::{Point, Rect};
use crate::frame::{replace_msaa, PassColor, PlannedPass, Step, TextureCopy};
use crate::pipelines::PipelineKind;
use super::Planner;
impl Planner<'_> {
pub(super) fn emit_segment(&mut self) {
{
let frame = self.frame();
let first = !frame.first_segment_emitted;
if frame.steps.is_empty() && frame.pre_copies.is_empty() && !first {
return;
}
}
if self.frame().last_pass.is_some() && self.frame().transient {
self.swap_to_persistent_attachments();
}
let index = self.passes.len();
let frame = self.frame_mut();
let first = !frame.first_segment_emitted;
frame.first_segment_emitted = true;
let resumed = frame.last_pass.replace(index);
let color = match &frame.color {
PassColor::Main { msaa } => PassColor::Main { msaa: msaa.clone() },
PassColor::Layer { msaa, resolve } => PassColor::Layer {
msaa: msaa.clone(),
resolve: resolve.clone(),
},
PassColor::Filter { .. } => unreachable!("filter passes never open a frame"),
};
let mut hoisted = 0;
let steps = reorder_segment(std::mem::take(&mut frame.steps), &mut hoisted);
let pass = PlannedPass {
color,
depth: Some(frame.depth.clone()),
clear: if first { frame.clear } else { None },
clear_depth: first,
store: frame.clear.is_none(),
pre_copies: std::mem::take(&mut frame.pre_copies),
steps,
};
self.stats.opaque_reordered += hoisted;
if let Some(prev) = resumed {
self.passes[prev].store = true;
}
self.passes.push(pass);
}
pub(super) fn break_pass(&mut self, coverage: &Rect) -> wgpu::TextureView {
self.stats.snapshots += 1;
self.emit_segment();
let (size, origin, src) = {
let frame = self.frame();
(frame.size, frame.origin, frame.src_texture.clone())
};
let snapshot = self.pool.take_snapshot(size, self.format);
if let Some((origin, extent)) = snapshot_region(coverage, origin, size) {
self.frame_mut().pre_copies.push(TextureCopy {
src,
dst: snapshot.texture.clone(),
origin,
size: extent,
});
}
snapshot.view
}
fn swap_to_persistent_attachments(&mut self) {
let size = self.frame().size;
let scratch = self.pool.main_scratch(size, self.format, false);
let previous = self.frame().last_pass;
let frame = self.frame_mut();
frame.transient = false;
frame.depth = scratch.depth.clone();
replace_msaa(&mut frame.color, &scratch.msaa);
if let Some(previous) = previous {
let pass = &mut self.passes[previous];
pass.depth = Some(scratch.depth);
replace_msaa(&mut pass.color, &scratch.msaa);
}
}
}
pub(super) fn snapshot_region(
coverage: &Rect,
origin: Point,
size: [u32; 2],
) -> Option<([u32; 2], [u32; 2])> {
let x0 = (coverage.x - origin.x).floor().max(0.0) as u32;
let y0 = (coverage.y - origin.y).floor().max(0.0) as u32;
let x1 = ((coverage.x + coverage.width - origin.x).ceil().max(0.0) as u32).min(size[0]);
let y1 = ((coverage.y + coverage.height - origin.y).ceil().max(0.0) as u32).min(size[1]);
(x1 > x0 && y1 > y0).then_some(([x0, y0], [x1 - x0, y1 - y0]))
}
fn reorder_segment(steps: Vec<Step>, hoisted: &mut u32) -> Vec<Step> {
let mut out = Vec::with_capacity(steps.len());
let mut chunk: Vec<Vec<Step>> = Vec::new(); let mut unit: Vec<Step> = Vec::new();
for step in steps {
let fan = matches!(step.key.kind, PipelineKind::StencilFan { .. });
let barrier = matches!(step.key.kind, PipelineKind::ClipCover { .. });
unit.push(step);
if fan {
continue; }
if barrier {
flush_chunk(&mut chunk, &mut out, hoisted);
out.append(&mut unit);
} else {
chunk.push(std::mem::take(&mut unit));
}
}
chunk.push(unit); flush_chunk(&mut chunk, &mut out, hoisted);
out
}
fn flush_chunk(chunk: &mut Vec<Vec<Step>>, out: &mut Vec<Step>, hoisted: &mut u32) {
let is_opaque = |unit: &Vec<Step>| {
unit.last().is_some_and(|s| {
matches!(
s.key.kind,
PipelineKind::OpaqueDraw(_) | PipelineKind::OpaqueCover(_)
)
})
};
let mut seen_blended = false;
let mut opaque: Vec<Vec<Step>> = Vec::new();
let mut blended: Vec<Vec<Step>> = Vec::new();
for unit in chunk.drain(..) {
if is_opaque(&unit) {
if seen_blended {
*hoisted += 1; }
opaque.push(unit);
} else if !unit.is_empty() {
seen_blended = true;
blended.push(unit);
}
}
opaque.sort_by(|a, b| {
let za = a.last().map_or(0.0, |s| s.sort_z);
let zb = b.last().map_or(0.0, |s| s.sort_z);
zb.total_cmp(&za)
});
out.extend(opaque.into_iter().flatten());
out.extend(blended.into_iter().flatten());
}