Redesign iced_wgpu layering architecture

This commit is contained in:
Héctor Ramón Jiménez 2024-04-03 21:07:54 +02:00
parent 99a904112c
commit b05e61f5c8
No known key found for this signature in database
GPG key ID: 7CC46565708259A7
36 changed files with 2781 additions and 2048 deletions

View file

@ -12,11 +12,37 @@ use bytemuck::{Pod, Zeroable};
use std::mem;
#[cfg(feature = "tracing")]
use tracing::info_span;
const INITIAL_INSTANCES: usize = 2_000;
/// The properties of a quad.
#[derive(Clone, Copy, Debug, Pod, Zeroable)]
#[repr(C)]
pub struct Quad {
/// The position of the [`Quad`].
pub position: [f32; 2],
/// The size of the [`Quad`].
pub size: [f32; 2],
/// The border color of the [`Quad`], in __linear RGB__.
pub border_color: color::Packed,
/// The border radii of the [`Quad`].
pub border_radius: [f32; 4],
/// The border width of the [`Quad`].
pub border_width: f32,
/// The shadow color of the [`Quad`].
pub shadow_color: color::Packed,
/// The shadow offset of the [`Quad`].
pub shadow_offset: [f32; 2],
/// The shadow blur radius of the [`Quad`].
pub shadow_blur_radius: f32,
}
#[derive(Debug)]
pub struct Pipeline {
solid: solid::Pipeline,
@ -54,7 +80,7 @@ impl Pipeline {
}
}
pub fn prepare(
pub fn prepare_batch(
&mut self,
device: &wgpu::Device,
encoder: &mut wgpu::CommandEncoder,
@ -73,7 +99,64 @@ impl Pipeline {
self.prepare_layer += 1;
}
pub fn render<'a>(
pub fn prepare_cache(
&self,
device: &wgpu::Device,
encoder: &mut wgpu::CommandEncoder,
belt: &mut wgpu::util::StagingBelt,
cache: &mut Cache,
transformation: Transformation,
scale: f32,
) {
match cache {
Cache::Staged(_) => {
let Cache::Staged(batch) =
std::mem::replace(cache, Cache::Staged(Batch::default()))
else {
unreachable!()
};
let mut layer = Layer::new(device, &self.constant_layout);
layer.prepare(
device,
encoder,
belt,
&batch,
transformation,
scale,
);
*cache = Cache::Uploaded {
layer,
batch,
needs_reupload: false,
}
}
Cache::Uploaded {
batch,
layer,
needs_reupload,
} => {
if *needs_reupload {
layer.prepare(
device,
encoder,
belt,
batch,
transformation,
scale,
);
*needs_reupload = false;
} else {
layer.update(device, encoder, belt, transformation, scale);
}
}
}
}
pub fn render_batch<'a>(
&'a self,
layer: usize,
bounds: Rectangle<u32>,
@ -81,38 +164,59 @@ impl Pipeline {
render_pass: &mut wgpu::RenderPass<'a>,
) {
if let Some(layer) = self.layers.get(layer) {
render_pass.set_scissor_rect(
bounds.x,
bounds.y,
bounds.width,
bounds.height,
);
self.render(bounds, layer, &quads.order, render_pass);
}
}
let mut solid_offset = 0;
let mut gradient_offset = 0;
pub fn render_cache<'a>(
&'a self,
cache: &'a Cache,
bounds: Rectangle<u32>,
render_pass: &mut wgpu::RenderPass<'a>,
) {
if let Cache::Uploaded { layer, batch, .. } = cache {
self.render(bounds, layer, &batch.order, render_pass);
}
}
for (kind, count) in &quads.order {
match kind {
Kind::Solid => {
self.solid.render(
render_pass,
&layer.constants,
&layer.solid,
solid_offset..(solid_offset + count),
);
fn render<'a>(
&'a self,
bounds: Rectangle<u32>,
layer: &'a Layer,
order: &Order,
render_pass: &mut wgpu::RenderPass<'a>,
) {
render_pass.set_scissor_rect(
bounds.x,
bounds.y,
bounds.width,
bounds.height,
);
solid_offset += count;
}
Kind::Gradient => {
self.gradient.render(
render_pass,
&layer.constants,
&layer.gradient,
gradient_offset..(gradient_offset + count),
);
let mut solid_offset = 0;
let mut gradient_offset = 0;
gradient_offset += count;
}
for (kind, count) in order {
match kind {
Kind::Solid => {
self.solid.render(
render_pass,
&layer.constants,
&layer.solid,
solid_offset..(solid_offset + count),
);
solid_offset += count;
}
Kind::Gradient => {
self.gradient.render(
render_pass,
&layer.constants,
&layer.gradient,
gradient_offset..(gradient_offset + count),
);
gradient_offset += count;
}
}
}
@ -124,7 +228,49 @@ impl Pipeline {
}
#[derive(Debug)]
struct Layer {
pub enum Cache {
Staged(Batch),
Uploaded {
batch: Batch,
layer: Layer,
needs_reupload: bool,
},
}
impl Cache {
pub fn is_empty(&self) -> bool {
match self {
Cache::Staged(batch) | Cache::Uploaded { batch, .. } => {
batch.is_empty()
}
}
}
pub fn update(&mut self, new_batch: Batch) {
match self {
Self::Staged(batch) => {
*batch = new_batch;
}
Self::Uploaded {
batch,
needs_reupload,
..
} => {
*batch = new_batch;
*needs_reupload = true;
}
}
}
}
impl Default for Cache {
fn default() -> Self {
Self::Staged(Batch::default())
}
}
#[derive(Debug)]
pub struct Layer {
constants: wgpu::BindGroup,
constants_buffer: wgpu::Buffer,
solid: solid::Layer,
@ -169,9 +315,26 @@ impl Layer {
transformation: Transformation,
scale: f32,
) {
#[cfg(feature = "tracing")]
let _ = info_span!("Wgpu::Quad", "PREPARE").entered();
self.update(device, encoder, belt, transformation, scale);
if !quads.solids.is_empty() {
self.solid.prepare(device, encoder, belt, &quads.solids);
}
if !quads.gradients.is_empty() {
self.gradient
.prepare(device, encoder, belt, &quads.gradients);
}
}
pub fn update(
&mut self,
device: &wgpu::Device,
encoder: &mut wgpu::CommandEncoder,
belt: &mut wgpu::util::StagingBelt,
transformation: Transformation,
scale: f32,
) {
let uniforms = Uniforms::new(transformation, scale);
let bytes = bytemuck::bytes_of(&uniforms);
@ -183,47 +346,9 @@ impl Layer {
device,
)
.copy_from_slice(bytes);
if !quads.solids.is_empty() {
self.solid.prepare(device, encoder, belt, &quads.solids);
}
if !quads.gradients.is_empty() {
self.gradient
.prepare(device, encoder, belt, &quads.gradients);
}
}
}
/// The properties of a quad.
#[derive(Clone, Copy, Debug, Pod, Zeroable)]
#[repr(C)]
pub struct Quad {
/// The position of the [`Quad`].
pub position: [f32; 2],
/// The size of the [`Quad`].
pub size: [f32; 2],
/// The border color of the [`Quad`], in __linear RGB__.
pub border_color: color::Packed,
/// The border radii of the [`Quad`].
pub border_radius: [f32; 4],
/// The border width of the [`Quad`].
pub border_width: f32,
/// The shadow color of the [`Quad`].
pub shadow_color: [f32; 4],
/// The shadow offset of the [`Quad`].
pub shadow_offset: [f32; 2],
/// The shadow blur radius of the [`Quad`].
pub shadow_blur_radius: f32,
}
/// A group of [`Quad`]s rendered together.
#[derive(Default, Debug)]
pub struct Batch {
@ -233,10 +358,13 @@ pub struct Batch {
/// The gradient quads of the [`Layer`].
gradients: Vec<Gradient>,
/// The quad order of the [`Layer`]; stored as a tuple of the quad type & its count.
order: Vec<(Kind, usize)>,
/// The quad order of the [`Layer`].
order: Order,
}
/// The quad order of a [`Layer`]; stored as a tuple of the quad type & its count.
type Order = Vec<(Kind, usize)>;
impl Batch {
/// Returns true if there are no quads of any type in [`Quads`].
pub fn is_empty(&self) -> bool {
@ -276,6 +404,12 @@ impl Batch {
}
}
}
pub fn clear(&mut self) {
self.solids.clear();
self.gradients.clear();
self.order.clear();
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]