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(
48        &self,
49        _pipeline: &Self::Pipeline,
50        _render_pass: &mut wgpu::RenderPass<'_>,
51    ) -> bool {
52        false
53    }
54
55    /// Renders the [`Primitive`], using the given [`wgpu::CommandEncoder`].
56    ///
57    /// This will only be called if [`draw`](Self::draw) returns `false`.
58    ///
59    /// By default, it does nothing.
60    fn render(
61        &self,
62        _pipeline: &Self::Pipeline,
63        _encoder: &mut wgpu::CommandEncoder,
64        _target: &wgpu::TextureView,
65        _clip_bounds: &Rectangle<u32>,
66    ) {
67    }
68}
69
70/// The pipeline of a graphics [`Primitive`].
71pub trait Pipeline: Any + MaybeSend + MaybeSync {
72    /// Creates the [`Pipeline`] of a [`Primitive`].
73    ///
74    /// This will only be called once, when the first [`Primitive`] with this kind
75    /// of [`Pipeline`] is encountered.
76    fn new(
77        device: &wgpu::Device,
78        queue: &wgpu::Queue,
79        format: wgpu::TextureFormat,
80    ) -> Self
81    where
82        Self: Sized;
83
84    /// Trims any cached data in the [`Pipeline`].
85    ///
86    /// This will normally be called at the end of a frame.
87    fn trim(&mut self) {}
88}
89
90pub(crate) trait Stored:
91    Debug + MaybeSend + MaybeSync + 'static
92{
93    fn prepare(
94        &self,
95        storage: &mut Storage,
96        device: &wgpu::Device,
97        queue: &wgpu::Queue,
98        format: wgpu::TextureFormat,
99        bounds: &Rectangle,
100        viewport: &Viewport,
101    );
102
103    fn draw(
104        &self,
105        storage: &Storage,
106        render_pass: &mut wgpu::RenderPass<'_>,
107    ) -> bool;
108
109    fn render(
110        &self,
111        storage: &Storage,
112        encoder: &mut wgpu::CommandEncoder,
113        target: &wgpu::TextureView,
114        clip_bounds: &Rectangle<u32>,
115    );
116}
117
118#[derive(Debug)]
119struct BlackBox<P: Primitive> {
120    primitive: P,
121}
122
123impl<P: Primitive> Stored for BlackBox<P> {
124    fn prepare(
125        &self,
126        storage: &mut Storage,
127        device: &wgpu::Device,
128        queue: &wgpu::Queue,
129        format: wgpu::TextureFormat,
130        bounds: &Rectangle,
131        viewport: &Viewport,
132    ) {
133        if !storage.has::<P>() {
134            storage.store::<P, _>(P::Pipeline::new(device, queue, format));
135        }
136
137        let renderer = storage
138            .get_mut::<P>()
139            .expect("renderer should be initialized")
140            .downcast_mut::<P::Pipeline>()
141            .expect("renderer should have the proper type");
142
143        self.primitive
144            .prepare(renderer, device, queue, bounds, viewport);
145    }
146
147    fn draw(
148        &self,
149        storage: &Storage,
150        render_pass: &mut wgpu::RenderPass<'_>,
151    ) -> bool {
152        let renderer = storage
153            .get::<P>()
154            .expect("renderer should be initialized")
155            .downcast_ref::<P::Pipeline>()
156            .expect("renderer should have the proper type");
157
158        self.primitive.draw(renderer, render_pass)
159    }
160
161    fn render(
162        &self,
163        storage: &Storage,
164        encoder: &mut wgpu::CommandEncoder,
165        target: &wgpu::TextureView,
166        clip_bounds: &Rectangle<u32>,
167    ) {
168        let renderer = storage
169            .get::<P>()
170            .expect("renderer should be initialized")
171            .downcast_ref::<P::Pipeline>()
172            .expect("renderer should have the proper type");
173
174        self.primitive
175            .render(renderer, encoder, target, clip_bounds);
176    }
177}
178
179#[derive(Debug)]
180/// An instance of a specific [`Primitive`].
181pub struct Instance {
182    /// The bounds of the [`Instance`].
183    pub(crate) bounds: Rectangle,
184
185    /// The [`Primitive`] to render.
186    pub(crate) primitive: Box<dyn Stored>,
187}
188
189impl Instance {
190    /// Creates a new [`Instance`] with the given [`Primitive`].
191    pub fn new(bounds: Rectangle, primitive: impl Primitive) -> Self {
192        Instance {
193            bounds,
194            primitive: Box::new(BlackBox { primitive }),
195        }
196    }
197}
198
199/// A renderer than can draw custom primitives.
200pub trait Renderer: core::Renderer {
201    /// Draws a custom primitive.
202    fn draw_primitive(&mut self, bounds: Rectangle, primitive: impl Primitive);
203}
204
205/// Stores custom, user-provided types.
206#[derive(Default)]
207pub struct Storage {
208    pipelines: FxHashMap<TypeId, Box<dyn Pipeline>>,
209}
210
211impl Storage {
212    /// Returns `true` if `Storage` contains a type `T`.
213    pub fn has<T: 'static>(&self) -> bool {
214        self.pipelines.contains_key(&TypeId::of::<T>())
215    }
216
217    /// Inserts the data `T` in to [`Storage`].
218    pub fn store<T: 'static, P: Pipeline>(&mut self, pipeline: P) {
219        let _ = self.pipelines.insert(TypeId::of::<T>(), Box::new(pipeline));
220    }
221
222    /// Returns a reference to the data with type `T` if it exists in [`Storage`].
223    pub fn get<T: 'static>(&self) -> Option<&dyn Any> {
224        self.pipelines
225            .get(&TypeId::of::<T>())
226            .map(|pipeline| pipeline.as_ref() as &dyn Any)
227    }
228
229    /// Returns a mutable reference to the data with type `T` if it exists in [`Storage`].
230    pub fn get_mut<T: 'static>(&mut self) -> Option<&mut dyn Any> {
231        self.pipelines
232            .get_mut(&TypeId::of::<T>())
233            .map(|pipeline| pipeline.as_mut() as &mut dyn Any)
234    }
235
236    /// Trims the cache of all the pipelines in the [`Storage`].
237    pub fn trim(&mut self) {
238        for pipeline in self.pipelines.values_mut() {
239            pipeline.trim();
240        }
241    }
242}