use valo_geometry::{Color, Matrix};
use crate::frame::{Draw, TextureCopy};
use crate::pipelines::{PipelineBlend, PipelineKind};
use super::draw_state::DrawState;
use super::filter_output::FilterOutput;
use super::snapshot::Snapshot;
use super::Planner;
impl Planner<'_> {
pub(super) fn emit_segment(&mut self) {
let context = self.contexts.top_mut();
let first = !context.first_pass_emitted;
if !first && context.draws.is_empty() {
return;
}
context.first_pass_emitted = true;
let clear = if first {
context.clear
} else {
Color::TRANSPARENT
};
let draws = std::mem::take(&mut context.draws);
let attachments = context.attachments.clone();
self.plan.push_context_pass(attachments, clear, draws);
}
pub(super) fn split_for_copy(&mut self, reader_z: f32) -> Snapshot {
self.plan.stats.snapshots += 1;
self.emit_segment();
let copy = self.draw_back();
self.replay_clips(reader_z);
copy
}
pub(super) fn start_from_existing_pixels(&mut self) {
self.draw_back();
}
fn draw_back(&mut self) -> Snapshot {
let context = self.contexts.top();
let copy = self
.tools
.pool
.take_copy_texture(context.area.size(), self.tools.format);
self.plan.queue_copy(TextureCopy {
src: context.src_texture.clone(),
dst: copy.texture,
size: context.area.size(),
});
let picture = Snapshot {
view: copy.view,
placement: context.area.placement(),
}
.placed_where_drawn(true);
let floor = context.depth.floor();
self.drawing().draw_filter_output(
&FilterOutput::Snapshot(picture.clone()),
PipelineBlend::SrcOver,
&DrawState::in_layer(Matrix::IDENTITY, floor),
);
picture
}
fn replay_clips(&mut self, reader_z: f32) {
let context = self.contexts.top_mut();
context.clips.retain(|clip| clip.z > reader_z);
let replayed = context.clips.clone();
context.draws.extend(replayed);
}
}
pub(super) fn reorder_draws(draws: Vec<Draw>, hoisted: &mut u32) -> Vec<Draw> {
let mut out = Vec::with_capacity(draws.len());
let mut chunk: Vec<Draw> = Vec::new(); for draw in draws {
if matches!(draw.key.kind, PipelineKind::ClipCover { .. }) {
flush_chunk(&mut chunk, &mut out, hoisted);
out.push(draw);
} else {
chunk.push(draw);
}
}
flush_chunk(&mut chunk, &mut out, hoisted);
out
}
fn flush_chunk(chunk: &mut Vec<Draw>, out: &mut Vec<Draw>, hoisted: &mut u32) {
let is_opaque = |draw: &Draw| {
matches!(
draw.key.kind,
PipelineKind::OpaqueDraw(_) | PipelineKind::OpaqueCover(_)
)
};
let mut seen_blended = false;
let mut opaque: Vec<Draw> = Vec::new();
let mut blended: Vec<Draw> = Vec::new();
for draw in chunk.drain(..) {
if is_opaque(&draw) {
if seen_blended {
*hoisted += 1; }
opaque.push(draw);
} else {
seen_blended = true;
blended.push(draw);
}
}
opaque.sort_by(|a, b| b.z.total_cmp(&a.z));
out.extend(opaque);
out.extend(blended);
}
#[cfg(test)]
mod tests {
use super::*;
use crate::frame::{Bindings, Geometry, Mesh, Stencil};
use crate::host_buffer::{UniformSlot, VertexSlot};
use crate::pipelines::{Frag, PipelineBlend, PipelineKey};
const FORMAT: wgpu::TextureFormat = wgpu::TextureFormat::Rgba8Unorm;
fn draw(kind: PipelineKind, z: f32, stencilled: bool) -> Draw {
let uniforms = UniformSlot {
block: 0,
offset: (z * 1000.0) as u32,
};
let stencil = stencilled.then(|| Stencil {
key: PipelineKey::new(
FORMAT,
PipelineBlend::SrcOver,
PipelineKind::StencilFan { even_odd: false },
),
uniforms,
mesh: Mesh {
slot: VertexSlot {
block: 0,
offset: 0,
bytes: 24,
},
vertices: 3,
},
});
Draw {
stencil,
key: PipelineKey::new(FORMAT, PipelineBlend::SrcOver, kind),
uniforms,
bindings: Bindings::Plain,
geometry: Geometry::Quad,
z,
}
}
fn order(draws: &[Draw]) -> Vec<(f32, bool)> {
draws
.iter()
.map(|draw| (draw.z, draw.stencil.is_some()))
.collect()
}
#[test]
fn opaque_draws_go_first_front_to_back_with_their_stencils() {
let draws = vec![
draw(PipelineKind::Cover(Frag::Solid), 0.1, true),
draw(PipelineKind::OpaqueCover(Frag::Solid), 0.2, true),
draw(PipelineKind::OpaqueDraw(Frag::Solid), 0.3, false),
];
let mut hoisted = 0;
let reordered = reorder_draws(draws, &mut hoisted);
assert_eq!(order(&reordered), [(0.3, false), (0.2, true), (0.1, true)]);
assert_eq!(hoisted, 2);
}
#[test]
fn nothing_crosses_a_clip() {
let draws = vec![
draw(PipelineKind::Draw(Frag::Solid), 0.1, false),
draw(PipelineKind::ClipCover { difference: false }, 0.5, true),
draw(PipelineKind::OpaqueDraw(Frag::Solid), 0.3, false),
];
let mut hoisted = 0;
let reordered = reorder_draws(draws, &mut hoisted);
assert_eq!(order(&reordered), [(0.1, false), (0.5, true), (0.3, false)]);
assert_eq!(hoisted, 0);
}
}