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