Skip to main content

pebble/graphics/render/
frame.rs

1use crate::graphics::render::{compute_pass::ComputePass, render_pass::RenderPass, targets::Pass};
2
3/// The acquired swapchain frame for this tick — access it via
4/// [`CurrentFrame::active`], not directly.
5pub struct Frame {
6    encoder: wgpu::CommandEncoder,
7    view: wgpu::TextureView,
8    surface: wgpu::SurfaceTexture
9}
10
11impl Frame {
12    pub(crate) fn new(encoder: wgpu::CommandEncoder, view: wgpu::TextureView, surface: wgpu::SurfaceTexture) -> Self {
13        Self { encoder, view, surface }
14    }
15
16    pub(crate) fn finish(self) -> (wgpu::CommandEncoder, wgpu::SurfaceTexture) {
17        (self.encoder, self.surface)
18    }
19
20    /// Begins a render pass for `pass`'s color/depth targets — an
21    /// unattached color target falls back to the swapchain's own view.
22    pub fn begin<'a>(&'a mut self, pass: Pass) -> RenderPass<'a> {
23        let color_attachments: Vec<_> = pass
24            .colors
25            .iter()
26            .map(|target| {
27                let view = target.attachment.map(|t| t.raw()).unwrap_or(&self.view);
28
29                Some(wgpu::RenderPassColorAttachment {
30                    view,
31                    resolve_target: None,
32                    depth_slice: None,
33                    ops: wgpu::Operations {
34                        load: target
35                            .clear
36                            .map(|[r, g, b, a]| {
37                                wgpu::LoadOp::Clear(wgpu::Color {
38                                    r: r as f64,
39                                    g: g as f64,
40                                    b: b as f64,
41                                    a: a as f64,
42                                })
43                            })
44                            .unwrap_or(wgpu::LoadOp::Load),
45                        store: wgpu::StoreOp::Store
46                    }
47                })
48            }).collect();
49
50        let depth_stencil_attachment = pass.depth.as_ref().map(|d| wgpu::RenderPassDepthStencilAttachment {
51            view: d.attachment.raw(),
52            depth_ops: Some(wgpu::Operations {
53                load: d.clear.map(wgpu::LoadOp::Clear).unwrap_or(wgpu::LoadOp::Load),
54                store: wgpu::StoreOp::Store,
55            }),
56            stencil_ops: None
57        });
58
59        RenderPass::new(self.encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
60            label: None,
61            color_attachments: &color_attachments,
62            depth_stencil_attachment,
63            timestamp_writes: None,
64            occlusion_query_set: None,
65            multiview_mask: None
66        }))
67    }
68
69    /// Begins a compute pass on this frame's command encoder, so compute
70    /// work is recorded and submitted together with the frame's render
71    /// passes and keeps its ordering relative to them.
72    ///
73    /// [`Backend::dispatch_compute`](crate::graphics::render::Backend::dispatch_compute)
74    /// remains the right call for compute that doesn't need a frame — it
75    /// records into its own encoder and submits immediately.
76    pub fn begin_compute<'a>(&'a mut self, label: Option<&str>) -> ComputePass<'a> {
77        ComputePass::new(
78            self.encoder
79                .begin_compute_pass(&wgpu::ComputePassDescriptor { label, timestamp_writes: None }),
80        )
81    }
82}
83
84/// A frame known to be acquired, from [`CurrentFrame::active`]. `Deref`s to
85/// [`Frame`].
86pub struct ActiveFrame<'a> {
87    frame: &'a mut Frame
88}
89
90impl<'a> ActiveFrame<'a>{
91    /// Begins a render pass — see [`Frame::begin`].
92    pub fn begin_pass(&'a mut self, pass: Pass) -> RenderPass<'a> {
93        self.frame.begin(pass)
94    }
95}
96
97impl<'a> std::ops::Deref for ActiveFrame<'a> {
98    type Target = Frame;
99    
100    fn deref(&self) -> &Self::Target {
101        self.frame
102    }
103}
104
105impl<'a> std::ops::DerefMut for ActiveFrame<'a> {
106    fn deref_mut(&mut self) -> &mut Self::Target {
107        self.frame
108    }
109}
110
111/// Resource holding this tick's acquired frame, if any — `None` when the
112/// surface couldn't be acquired (occluded, resizing, etc.), in which case
113/// rendering this tick should just be skipped.
114#[derive(Default)]
115pub struct CurrentFrame {
116    frame: Option<Frame>
117}
118
119impl CurrentFrame {
120    pub(crate) fn set(&mut self, frame: Frame) {
121        self.frame = Some(frame);
122    }
123
124    pub(crate) fn take(&mut self) -> Option<Frame> {
125        self.frame.take()
126    }
127
128    /// The active frame to render into, if one was acquired this tick.
129    pub fn active<'a>(&'a mut self) -> Option<ActiveFrame<'a>>{
130        self.frame.as_mut().map(|f| ActiveFrame {
131            frame: f
132        })
133    }
134}