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