Introduce Pipeline trait in wgpu::primitive

Co-authored-by: roguetechh <roguedotllc@gmail.com>
This commit is contained in:
Héctor Ramón Jiménez 2025-11-25 09:36:25 +01:00
parent a4829306cb
commit e937bf2bac
No known key found for this signature in database
GPG key ID: 7CC46565708259A7
5 changed files with 61 additions and 44 deletions

View file

@ -128,16 +128,7 @@ impl Primitive {
} }
impl shader::Primitive for Primitive { impl shader::Primitive for Primitive {
type Renderer = Pipeline; type Pipeline = Pipeline;
fn initialize(
&self,
device: &wgpu::Device,
queue: &wgpu::Queue,
format: wgpu::TextureFormat,
) -> Pipeline {
Pipeline::new(device, queue, format)
}
fn prepare( fn prepare(
&self, &self,
@ -183,3 +174,13 @@ fn rnd_origin() -> Vec3 {
rand::thread_rng().gen_range(-4.0..2.0), rand::thread_rng().gen_range(-4.0..2.0),
) )
} }
impl shader::Pipeline for Pipeline {
fn new(
device: &wgpu::Device,
queue: &wgpu::Queue,
format: wgpu::TextureFormat,
) -> Pipeline {
Self::new(device, queue, format)
}
}

View file

@ -66,4 +66,13 @@ impl Engine {
&self._shell, &self._shell,
) )
} }
pub fn trim(&mut self) {
self.text_pipeline.trim();
self.primitive_storage
.write()
.expect("primitive storage should be writable")
.trim();
}
} }

View file

@ -149,8 +149,8 @@ impl Renderer {
self.triangle.trim(); self.triangle.trim();
self.text.trim(); self.text.trim();
// TODO: Move to runtime! // TODO: Provide window id (?)
self.engine.text_pipeline.trim(); self.engine.trim();
#[cfg(any(feature = "svg", feature = "image"))] #[cfg(any(feature = "svg", feature = "image"))]
{ {

View file

@ -19,23 +19,12 @@ pub trait Primitive: Debug + MaybeSend + MaybeSync + 'static {
/// ///
/// All instances of this [`Primitive`] type will share the same /// All instances of this [`Primitive`] type will share the same
/// [`Renderer`]. /// [`Renderer`].
type Renderer: MaybeSend + MaybeSync; type Pipeline: Pipeline + MaybeSend + MaybeSync;
/// Initializes the [`Renderer`](Self::Renderer) of the [`Primitive`].
///
/// This will only be called once, when the first [`Primitive`] of this kind
/// is encountered.
fn initialize(
&self,
device: &wgpu::Device,
queue: &wgpu::Queue,
format: wgpu::TextureFormat,
) -> Self::Renderer;
/// Processes the [`Primitive`], allowing for GPU buffer allocation. /// Processes the [`Primitive`], allowing for GPU buffer allocation.
fn prepare( fn prepare(
&self, &self,
renderer: &mut Self::Renderer, pipeline: &mut Self::Pipeline,
device: &wgpu::Device, device: &wgpu::Device,
queue: &wgpu::Queue, queue: &wgpu::Queue,
bounds: &Rectangle, bounds: &Rectangle,
@ -57,7 +46,7 @@ pub trait Primitive: Debug + MaybeSend + MaybeSync + 'static {
/// By default, it does nothing and returns `false`. /// By default, it does nothing and returns `false`.
fn draw( fn draw(
&self, &self,
_renderer: &Self::Renderer, _pipeline: &Self::Pipeline,
_render_pass: &mut wgpu::RenderPass<'_>, _render_pass: &mut wgpu::RenderPass<'_>,
) -> bool { ) -> bool {
false false
@ -70,7 +59,7 @@ pub trait Primitive: Debug + MaybeSend + MaybeSync + 'static {
/// By default, it does nothing. /// By default, it does nothing.
fn render( fn render(
&self, &self,
_renderer: &Self::Renderer, _pipeline: &Self::Pipeline,
_encoder: &mut wgpu::CommandEncoder, _encoder: &mut wgpu::CommandEncoder,
_target: &wgpu::TextureView, _target: &wgpu::TextureView,
_clip_bounds: &Rectangle<u32>, _clip_bounds: &Rectangle<u32>,
@ -78,6 +67,26 @@ pub trait Primitive: Debug + MaybeSend + MaybeSync + 'static {
} }
} }
/// The pipeline of a graphics [`Primitive`].
pub trait Pipeline: Any + MaybeSend + MaybeSync {
/// Creates the [`Pipeline`] of a [`Primitive`].
///
/// This will only be called once, when the first [`Primitive`] with this kind
/// of [`Pipeline`] is encountered.
fn new(
device: &wgpu::Device,
queue: &wgpu::Queue,
format: wgpu::TextureFormat,
) -> Self
where
Self: Sized;
/// Trims any cached data in the [`Pipeline`].
///
/// This will normally be called at the end of a frame.
fn trim(&mut self) {}
}
pub(crate) trait Stored: pub(crate) trait Stored:
Debug + MaybeSend + MaybeSync + 'static Debug + MaybeSend + MaybeSync + 'static
{ {
@ -122,15 +131,13 @@ impl<P: Primitive> Stored for BlackBox<P> {
viewport: &Viewport, viewport: &Viewport,
) { ) {
if !storage.has::<P>() { if !storage.has::<P>() {
storage.store::<P, _>( storage.store::<P, _>(P::Pipeline::new(device, queue, format));
self.primitive.initialize(device, queue, format),
);
} }
let renderer = storage let renderer = storage
.get_mut::<P>() .get_mut::<P>()
.expect("renderer should be initialized") .expect("renderer should be initialized")
.downcast_mut::<P::Renderer>() .downcast_mut::<P::Pipeline>()
.expect("renderer should have the proper type"); .expect("renderer should have the proper type");
self.primitive self.primitive
@ -145,7 +152,7 @@ impl<P: Primitive> Stored for BlackBox<P> {
let renderer = storage let renderer = storage
.get::<P>() .get::<P>()
.expect("renderer should be initialized") .expect("renderer should be initialized")
.downcast_ref::<P::Renderer>() .downcast_ref::<P::Pipeline>()
.expect("renderer should have the proper type"); .expect("renderer should have the proper type");
self.primitive.draw(renderer, render_pass) self.primitive.draw(renderer, render_pass)
@ -161,7 +168,7 @@ impl<P: Primitive> Stored for BlackBox<P> {
let renderer = storage let renderer = storage
.get::<P>() .get::<P>()
.expect("renderer should be initialized") .expect("renderer should be initialized")
.downcast_ref::<P::Renderer>() .downcast_ref::<P::Pipeline>()
.expect("renderer should have the proper type"); .expect("renderer should have the proper type");
self.primitive self.primitive
@ -198,7 +205,7 @@ pub trait Renderer: core::Renderer {
/// Stores custom, user-provided types. /// Stores custom, user-provided types.
#[derive(Default)] #[derive(Default)]
pub struct Storage { pub struct Storage {
pipelines: FxHashMap<TypeId, Box<dyn AnyConcurrent>>, pipelines: FxHashMap<TypeId, Box<dyn Pipeline>>,
} }
impl Storage { impl Storage {
@ -208,11 +215,8 @@ impl Storage {
} }
/// Inserts the data `T` in to [`Storage`]. /// Inserts the data `T` in to [`Storage`].
pub fn store<T: 'static, D: Any + MaybeSend + MaybeSync>( pub fn store<T: 'static, P: Pipeline>(&mut self, pipeline: P) {
&mut self, let _ = self.pipelines.insert(TypeId::of::<T>(), Box::new(pipeline));
data: D,
) {
let _ = self.pipelines.insert(TypeId::of::<T>(), Box::new(data));
} }
/// Returns a reference to the data with type `T` if it exists in [`Storage`]. /// Returns a reference to the data with type `T` if it exists in [`Storage`].
@ -228,8 +232,11 @@ impl Storage {
.get_mut(&TypeId::of::<T>()) .get_mut(&TypeId::of::<T>())
.map(|pipeline| pipeline.as_mut() as &mut dyn Any) .map(|pipeline| pipeline.as_mut() as &mut dyn Any)
} }
/// Trims the cache of all the pipelines in the [`Storage`].
pub fn trim(&mut self) {
for pipeline in self.pipelines.values_mut() {
pipeline.trim();
}
}
} }
trait AnyConcurrent: Any + MaybeSend + MaybeSync {}
impl<T> AnyConcurrent for T where T: Any + MaybeSend + MaybeSync {}

View file

@ -16,7 +16,7 @@ use std::marker::PhantomData;
pub use crate::Action; pub use crate::Action;
pub use crate::graphics::Viewport; pub use crate::graphics::Viewport;
pub use primitive::{Primitive, Storage}; pub use primitive::{Pipeline, Primitive, Storage};
/// A widget which can render custom shaders with Iced's `wgpu` backend. /// A widget which can render custom shaders with Iced's `wgpu` backend.
/// ///