Skip to main content

pebble/rendering/
backend.rs

1use crate::{
2    prelude::GPUSurfaceHandle, rendering::errors::AcquireError, rendering::sync::InitSender,
3};
4
5/// Describes a render pass: a set of color attachments and an optional depth
6/// attachment to render into.
7pub struct Pass<'a, F: FrameOperations + ?Sized> {
8    /// Color attachments for this pass.
9    pub colors: &'a [ColorTarget<'a, F>],
10    /// Optional depth attachment for this pass.
11    pub depth: Option<DepthTarget<'a, F>>,
12}
13
14/// Operations that can be performed on a single acquired frame.
15///
16/// Implemented by the per-frame type returned from [`Backend::acquire`].
17pub trait FrameOperations: Sync + Send + 'static {
18    /// The render-pass context (e.g. a command encoder or render pass handle).
19    type Context<'a>;
20    /// A color attachment (e.g. a texture view).
21    type Attachment;
22    /// A depth attachment (e.g. a depth-stencil texture view).
23    type DepthAttachment;
24
25    /// Begin a render pass and return the rendering context.
26    fn begin(&mut self, pass: Pass<'_, Self>) -> Self::Context<'_>;
27}
28
29/// Specifies a color attachment for a render pass.
30pub enum ColorTarget<'a, F: FrameOperations + ?Sized> {
31    /// Use the backend's default surface attachment.
32    Default {
33        /// `Some(color)` to clear to that color, `None` to load existing contents.
34        clear: Option<[f32; 4]>,
35    },
36    /// Use a custom attachment (e.g. an off-screen texture).
37    Custom {
38        attachment: &'a F::Attachment,
39        /// `Some(color)` to clear to that color, `None` to load existing contents.
40        clear: Option<[f32; 4]>,
41    },
42}
43
44impl<'a, F: FrameOperations> ColorTarget<'a, F> {
45    /// Default attachment, cleared to `clear`.
46    pub fn default(clear: [f32; 4]) -> Self {
47        Self::Default { clear: Some(clear) }
48    }
49    /// Default attachment, loading existing contents (no clear).
50    pub fn default_load() -> Self {
51        Self::Default { clear: None }
52    }
53    /// Custom attachment, cleared to `clear`.
54    pub fn custom(attachment: &'a F::Attachment, clear: [f32; 4]) -> Self {
55        Self::Custom {
56            attachment,
57            clear: Some(clear),
58        }
59    }
60    /// Custom attachment, loading existing contents (no clear).
61    pub fn custom_load(attachment: &'a F::Attachment) -> Self {
62        Self::Custom {
63            attachment,
64            clear: None,
65        }
66    }
67}
68
69/// Specifies the depth attachment for a render pass.
70pub struct DepthTarget<'a, F: FrameOperations + ?Sized> {
71    pub attachment: &'a F::DepthAttachment,
72    /// `Some(depth)` to clear to that value, `None` to load existing contents.
73    pub clear: Option<f32>,
74}
75
76impl<'a, F: FrameOperations> DepthTarget<'a, F> {
77    /// Depth attachment cleared to `clear`.
78    pub fn new(attachment: &'a F::DepthAttachment, clear: f32) -> Self {
79        Self {
80            attachment,
81            clear: Some(clear),
82        }
83    }
84    /// Depth attachment, loading existing contents (no clear).
85    pub fn load(attachment: &'a F::DepthAttachment) -> Self {
86        Self {
87            attachment,
88            clear: None,
89        }
90    }
91}
92
93/// A unified graphics backend for both native and web targets.
94///
95/// Implement this trait to integrate a concrete graphics API (e.g. wgpu).
96/// Initialisation is always done via the [`InitSender`] channel so that
97/// implementations can choose to do it synchronously or on a background thread.
98pub trait Backend: Sized + Sync + Send + 'static {
99    /// The per-frame type that exposes rendering operations.
100    type Frame: FrameOperations;
101
102    /// Begin initialisation. Send the finished backend through `sender` when ready.
103    fn init(handle: impl GPUSurfaceHandle, width: u32, height: u32, sender: InitSender<Self>);
104
105    /// Called when the window is resized. Override to recreate the swapchain.
106    fn resize(&mut self, width: u32, height: u32) {
107        width;
108        height;
109    }
110
111    /// Acquire the next frame for rendering.
112    ///
113    /// Returns [`AcquireError::Transient`] for recoverable failures (e.g.
114    /// swapchain out of date) and [`AcquireError::Fatal`] for unrecoverable ones.
115    fn acquire(&mut self) -> Result<Self::Frame, AcquireError>;
116
117    /// Present the completed frame to the display.
118    fn present(&mut self, frame: Self::Frame);
119}
120
121/// Implement for types that can issue draw commands into a render context.
122pub trait Drawable<B: Backend> {
123    fn draw(&self, pass: &mut <B::Frame as FrameOperations>::Context<'_>);
124}
125
126/// Implement for types that can bind themselves (e.g. pipelines, bind groups)
127/// into a render context.
128pub trait Bindable<B: Backend> {
129    fn bind(&self, pass: &mut <B::Frame as FrameOperations>::Context<'_>);
130}
131
132/// Resource holding the frame acquired at the start of each render tick.
133///
134/// Populated by [`RenderPlugin`](crate::rendering::render_plugin::RenderPlugin)
135/// during [`PreRender`](crate::app::SystemStage::PreRender) and consumed during
136/// [`PostRender`](crate::app::SystemStage::PostRender). Check
137/// [`is_active`](CurrentFrame::is_active) before attempting to render.
138pub struct CurrentFrame<B: Backend> {
139    pub(crate) frame: Option<B::Frame>,
140}
141
142impl<B: Backend> CurrentFrame<B> {
143    /// Returns `true` if a frame was successfully acquired this tick.
144    pub fn is_active(&self) -> bool {
145        self.frame.is_some()
146    }
147
148    /// Begin a simple full-screen color pass, clearing to `clear`.
149    ///
150    /// Returns `None` if no frame is active.
151    pub fn render_context(
152        &mut self,
153        clear: [f32; 4],
154    ) -> Option<<B::Frame as FrameOperations>::Context<'_>> {
155        self.begin_pass(Pass {
156            colors: &[ColorTarget::Default { clear: Some(clear) }],
157            depth: None,
158        })
159    }
160
161    /// Begin an arbitrary render pass described by `pass`.
162    ///
163    /// Returns `None` if no frame is active.
164    pub fn begin_pass(
165        &mut self,
166        pass: Pass<B::Frame>,
167    ) -> Option<<B::Frame as FrameOperations>::Context<'_>> {
168        self.frame.as_mut().map(|f| f.begin(pass))
169    }
170}