iced_wgpu/
primitive.rs

1//! Draw custom primitives.
2use crate::core::{self, Rectangle};
3use crate::graphics::Viewport;
4use crate::graphics::futures::{MaybeSend, MaybeSync};
5
6use rustc_hash::FxHashMap;
7use std::any::{Any, TypeId};
8use std::fmt::Debug;
9
10/// A batch of primitives.
11pub type Batch = Vec<Instance>;
12
13/// A set of methods which allows a [`Primitive`] to be rendered.
14pub trait Primitive: Debug + MaybeSend + MaybeSync + 'static {
15    /// The shared renderer of this [`Primitive`].
16    ///
17    /// Normally, this will contain a bunch of [`wgpu`] state; like
18    /// a rendering pipeline, buffers, and textures.
19    ///
20    /// All instances of this [`Primitive`] type will share the same
21    /// [`Renderer`].
22    type Pipeline: Pipeline + MaybeSend + MaybeSync;
23
24    /// Processes the [`Primitive`], allowing for GPU buffer allocation.
25    fn prepare(
26        &self,
27        pipeline: &mut Self::Pipeline,
28        device: &wgpu::Device,
29        queue: &wgpu::Queue,
30        bounds: &Rectangle,
31        viewport: &Viewport,
32    );
33
34    /// Draws the [`Primitive`] in the given [`wgpu::RenderPass`].
35    ///
36    /// When possible, this should be implemented over [`render`](Self::render)
37    /// since reusing the existing render pass should be considerably more
38    /// efficient than issuing a new one.
39    ///
40    /// The viewport and scissor rect of the render pass provided is set
41    /// to the bounds and clip bounds of the [`Primitive`], respectively.
42    ///
43    /// If you have complex composition needs, then you can leverage
44    /// [`render`](Self::render) by returning `false` here.
45    ///
46    /// By default, it does nothing and returns `false`.
47    fn draw(&self, _pipeline: &Self::Pipeline, _render_pass: &mut wgpu::RenderPass<'_>) -> bool {
48        false
49    }
50
51    /// Renders the [`Primitive`], using the given [`wgpu::CommandEncoder`].
52    ///
53    /// This will only be called if [`draw`](Self::draw) returns `false`.
54    ///
55    /// By default, it does nothing.
56    fn render(
57        &self,
58        _pipeline: &Self::Pipeline,
59        _encoder: &mut wgpu::CommandEncoder,
60        _target: &wgpu::TextureView,
61        _clip_bounds: &Rectangle<u32>,
62    ) {
63    }
64}
65
66/// The pipeline of a graphics [`Primitive`].
67pub trait Pipeline: Any + MaybeSend + MaybeSync {
68    /// Creates the [`Pipeline`] of a [`Primitive`].
69    ///
70    /// This will only be called once, when the first [`Primitive`] with this kind
71    /// of [`Pipeline`] is encountered.
72    fn new(device: &wgpu::Device, queue: &wgpu::Queue, format: wgpu::TextureFormat) -> Self
73    where
74        Self: Sized;
75
76    /// Trims any cached data in the [`Pipeline`].
77    ///
78    /// This will normally be called at the end of a frame.
79    fn trim(&mut self) {}
80}
81
82pub(crate) trait Stored: Debug + MaybeSend + MaybeSync + 'static {
83    fn prepare(
84        &self,
85        storage: &mut Storage,
86        device: &wgpu::Device,
87        queue: &wgpu::Queue,
88        format: wgpu::TextureFormat,
89        bounds: &Rectangle,
90        viewport: &Viewport,
91    );
92
93    fn draw(&self, storage: &Storage, render_pass: &mut wgpu::RenderPass<'_>) -> bool;
94
95    fn render(
96        &self,
97        storage: &Storage,
98        encoder: &mut wgpu::CommandEncoder,
99        target: &wgpu::TextureView,
100        clip_bounds: &Rectangle<u32>,
101    );
102}
103
104#[derive(Debug)]
105struct BlackBox<P: Primitive> {
106    primitive: P,
107}
108
109impl<P: Primitive> Stored for BlackBox<P> {
110    fn prepare(
111        &self,
112        storage: &mut Storage,
113        device: &wgpu::Device,
114        queue: &wgpu::Queue,
115        format: wgpu::TextureFormat,
116        bounds: &Rectangle,
117        viewport: &Viewport,
118    ) {
119        if !storage.has::<P>() {
120            storage.store::<P, _>(P::Pipeline::new(device, queue, format));
121        }
122
123        let renderer = storage
124            .get_mut::<P>()
125            .expect("renderer should be initialized")
126            .downcast_mut::<P::Pipeline>()
127            .expect("renderer should have the proper type");
128
129        self.primitive
130            .prepare(renderer, device, queue, bounds, viewport);
131    }
132
133    fn draw(&self, storage: &Storage, render_pass: &mut wgpu::RenderPass<'_>) -> bool {
134        let renderer = storage
135            .get::<P>()
136            .expect("renderer should be initialized")
137            .downcast_ref::<P::Pipeline>()
138            .expect("renderer should have the proper type");
139
140        self.primitive.draw(renderer, render_pass)
141    }
142
143    fn render(
144        &self,
145        storage: &Storage,
146        encoder: &mut wgpu::CommandEncoder,
147        target: &wgpu::TextureView,
148        clip_bounds: &Rectangle<u32>,
149    ) {
150        let renderer = storage
151            .get::<P>()
152            .expect("renderer should be initialized")
153            .downcast_ref::<P::Pipeline>()
154            .expect("renderer should have the proper type");
155
156        self.primitive
157            .render(renderer, encoder, target, clip_bounds);
158    }
159}
160
161#[derive(Debug)]
162/// An instance of a specific [`Primitive`].
163pub struct Instance {
164    /// The bounds of the [`Instance`].
165    pub(crate) bounds: Rectangle,
166
167    /// The [`Primitive`] to render.
168    pub(crate) primitive: Box<dyn Stored>,
169}
170
171impl Instance {
172    /// Creates a new [`Instance`] with the given [`Primitive`].
173    pub fn new(bounds: Rectangle, primitive: impl Primitive) -> Self {
174        Instance {
175            bounds,
176            primitive: Box::new(BlackBox { primitive }),
177        }
178    }
179}
180
181/// A renderer than can draw custom primitives.
182pub trait Renderer: core::Renderer {
183    /// Draws a custom primitive.
184    fn draw_primitive(&mut self, bounds: Rectangle, primitive: impl Primitive);
185}
186
187/// Stores custom, user-provided types.
188#[derive(Default)]
189pub struct Storage {
190    pipelines: FxHashMap<TypeId, Box<dyn Pipeline>>,
191}
192
193impl Storage {
194    /// Returns `true` if `Storage` contains a type `T`.
195    pub fn has<T: 'static>(&self) -> bool {
196        self.pipelines.contains_key(&TypeId::of::<T>())
197    }
198
199    /// Inserts the data `T` in to [`Storage`].
200    pub fn store<T: 'static, P: Pipeline>(&mut self, pipeline: P) {
201        let _ = self.pipelines.insert(TypeId::of::<T>(), Box::new(pipeline));
202    }
203
204    /// Returns a reference to the data with type `T` if it exists in [`Storage`].
205    pub fn get<T: 'static>(&self) -> Option<&dyn Any> {
206        self.pipelines
207            .get(&TypeId::of::<T>())
208            .map(|pipeline| pipeline.as_ref() as &dyn Any)
209    }
210
211    /// Returns a mutable reference to the data with type `T` if it exists in [`Storage`].
212    pub fn get_mut<T: 'static>(&mut self) -> Option<&mut dyn Any> {
213        self.pipelines
214            .get_mut(&TypeId::of::<T>())
215            .map(|pipeline| pipeline.as_mut() as &mut dyn Any)
216    }
217
218    /// Trims the cache of all the pipelines in the [`Storage`].
219    pub fn trim(&mut self) {
220        for pipeline in self.pipelines.values_mut() {
221            pipeline.trim();
222        }
223    }
224}