use super::{Backend, IntermediateTextureAllocations, Schedule, ScheduleStorage};
use crate::paint::PaintResolver;
use crate::schedule::round::DrawPass;
use crate::target::RootTarget;
use crate::{RenderError, Scene};
use alloc::vec;
use alloc::vec::Vec;
use vello_common::filter_effects::Filter;
use vello_common::geometry::{RectU16, SizeU16};
use vello_common::kurbo::{Rect, Shape};
use vello_common::peniko::BlendMode;
use vello_common::peniko::color::palette::css::BLUE;
pub(super) struct SceneCase {
pub(super) scene: Scene,
}
impl SceneCase {
pub(super) fn new(width: u16, height: u16) -> Self {
Self {
scene: Scene::new(width, height),
}
}
pub(super) fn draw(&mut self, rect: Rect, alpha: f32) {
self.scene.set_paint(BLUE.with_alpha(alpha));
self.scene.fill_rect(&rect);
}
pub(super) fn draw_at(&mut self, x: f64, alpha: f32) {
self.draw(Rect::new(x, 0.0, x + 4.0, 4.0), alpha);
}
pub(super) fn layer(&mut self, f: impl FnOnce(&mut Self)) {
self.layer_with(None, None, None, f);
}
pub(super) fn layer_with(
&mut self,
clip: Option<Rect>,
blend_mode: Option<BlendMode>,
filter: Option<Filter>,
f: impl FnOnce(&mut Self),
) {
let clip = clip.map(|rect| rect.to_path(0.1));
self.scene
.push_layer(clip.as_ref(), blend_mode, None, None, filter);
f(self);
self.scene.pop_layer();
}
pub(super) fn schedule(
&self,
root_target: RootTarget,
texture_size: SizeU16,
max_textures: usize,
use_depth_buffer: bool,
) -> Result<ScheduledCase, RenderError> {
let mut storage = ScheduleStorage::default();
let encoded = &self.scene.encoded_paints;
let offsets = vec![0; encoded.len()];
let schedule = Schedule::try_new(
&mut storage,
&self.scene,
root_target,
use_depth_buffer,
PaintResolver::new(encoded, &offsets),
texture_size,
IntermediateTextureAllocations::default(),
Some(max_textures),
)?;
Ok(ScheduledCase {
schedule,
storage,
root_target,
})
}
pub(super) fn schedule_into(
&self,
storage: &mut ScheduleStorage,
root_target: RootTarget,
texture_size: SizeU16,
max_textures: usize,
use_depth_buffer: bool,
) -> Result<Schedule, RenderError> {
let encoded = &self.scene.encoded_paints;
let offsets = vec![0; encoded.len()];
Schedule::try_new(
storage,
&self.scene,
root_target,
use_depth_buffer,
PaintResolver::new(encoded, &offsets),
texture_size,
IntermediateTextureAllocations::default(),
Some(max_textures),
)
}
pub(super) fn schedule_root(&self, use_depth_buffer: bool) -> ScheduledCase {
self.schedule(
RootTarget::UserSurface,
SizeU16::new(64),
8,
use_depth_buffer,
)
.unwrap()
}
}
#[derive(Debug, PartialEq, Eq)]
pub(super) struct DrawView {
pub(super) x: Vec<u16>,
pub(super) has_child_layer: bool,
}
#[derive(Debug, PartialEq, Eq)]
pub(super) struct RoundView {
pub(super) binding: [Option<u16>; 2],
pub(super) even: DrawView,
pub(super) odd: DrawView,
pub(super) root: DrawView,
pub(super) clears: [Vec<RectU16>; 2],
pub(super) filter_passes: [usize; 2],
pub(super) blend_passes: [usize; 2],
}
pub(super) struct ScheduledCase {
pub(super) schedule: Schedule,
pub(super) storage: ScheduleStorage,
root_target: RootTarget,
}
impl ScheduledCase {
pub(super) fn execute<R: Backend>(mut self, renderer: &mut R)
where
R::Error: core::fmt::Debug,
{
super::execute(renderer, &mut self.storage, self.schedule, self.root_target).unwrap();
}
pub(super) fn page_counts(&self) -> [usize; 2] {
self.schedule
.intermediate_texture_requirements()
.allocations
.layer_pages
}
pub(super) fn scratch_texture(&self) -> bool {
self.schedule
.intermediate_texture_requirements()
.allocations
.scratch
}
pub(super) fn views(&self) -> Vec<RoundView> {
self.schedule
.rounds
.iter()
.map(|round| {
let mut draws = [None, None];
let mut filter_passes = [0; 2];
let mut blend_passes = [0; 2];
for pass in round.layer_passes(&self.storage.buffers) {
let index = pass.texture_parity.get_parity();
if let Some(draw) = pass.draw {
draws[index] = Some(Self::draw_view(draw));
}
if let Some(filter) = pass.filter {
filter_passes[index] = filter.filters.len();
}
if let Some(blend) = pass.blend {
blend_passes[index] = blend.blends.len();
}
}
let mut clears = core::array::from_fn(|_| Vec::new());
for clear in round.clear_passes() {
clears[clear.target.texture_parity.get_parity()].extend_from_slice(clear.rects);
}
RoundView {
binding: round.texture_binding.page_indices(),
even: draws[0].take().unwrap_or_else(Self::empty_draw_view),
odd: draws[1].take().unwrap_or_else(Self::empty_draw_view),
root: round
.root_draw_pass(&self.storage.buffers, self.root_target)
.map(Self::draw_view)
.unwrap_or_else(Self::empty_draw_view),
clears,
filter_passes,
blend_passes,
}
})
.collect()
}
pub(super) fn opaque_x(&self) -> Vec<u16> {
self.storage
.buffers
.draw_buffers
.opaque
.strips()
.iter()
.map(|strip| strip.x)
.collect()
}
pub(super) fn total_clears(&self) -> usize {
self.schedule
.rounds
.iter()
.flat_map(|round| round.clear_passes())
.map(|clear| clear.rects.len())
.sum()
}
fn draw_view(draw: DrawPass<'_>) -> DrawView {
DrawView {
x: draw.strips.iter().map(|strip| strip.x).collect(),
has_child_layer: draw.bindings.child.is_some(),
}
}
fn empty_draw_view() -> DrawView {
DrawView {
x: Vec::new(),
has_child_layer: false,
}
}
}