Skip to main content

alpha2_render/
target.rs

1use crate::{RenderError, Shared};
2
3/// A GPU texture produced by the renderer. Image passes store premultiplied
4/// sRGB RGBA; raw solid clears retain the supplied component values.
5///
6/// Read it back with [`crate::Renderer::read_rgba8`] or the renderer's other
7/// readbacks, or draw it as a source through [`SourceView`].
8#[derive(Clone, Debug)]
9pub struct RenderedTexture {
10    pub(crate) size: RenderSize,
11    pub(crate) format: wgpu::TextureFormat,
12    pub(crate) texture: wgpu::Texture,
13    view: wgpu::TextureView,
14    _recycle: Option<Shared<RecycleGuard>>,
15}
16
17#[derive(Debug)]
18struct RecycleGuard {
19    texture: wgpu::Texture,
20    view: wgpu::TextureView,
21    token: Option<crate::pool::RecycleToken>,
22}
23
24impl Drop for RecycleGuard {
25    fn drop(&mut self) {
26        if let Some(token) = self.token.take() {
27            token.recycle(self.texture.clone(), self.view.clone());
28        }
29    }
30}
31
32impl RenderedTexture {
33    #[cfg(test)]
34    pub(crate) fn owned(
35        size: RenderSize,
36        format: wgpu::TextureFormat,
37        texture: wgpu::Texture,
38    ) -> Self {
39        let view = crate::pool::default_view(&texture);
40        Self {
41            size,
42            format,
43            texture,
44            view,
45            _recycle: None,
46        }
47    }
48
49    /// Texture dimensions in pixels.
50    #[must_use]
51    pub const fn size(&self) -> RenderSize {
52        self.size
53    }
54
55    /// The GPU texture format.
56    #[cfg_attr(not(feature = "wgpu-interop"), doc(hidden))]
57    #[must_use]
58    pub const fn format(&self) -> wgpu::TextureFormat {
59        self.format
60    }
61
62    /// The texture this result owns.
63    #[cfg_attr(not(feature = "wgpu-interop"), doc(hidden))]
64    #[must_use]
65    pub const fn texture(&self) -> &wgpu::Texture {
66        &self.texture
67    }
68
69    /// Returns the texture's default view, made once with the texture: a
70    /// pooled texture keeps it across every result it holds.
71    #[cfg_attr(not(feature = "wgpu-interop"), doc(hidden))]
72    #[must_use]
73    pub const fn view(&self) -> &wgpu::TextureView {
74        &self.view
75    }
76
77    pub(crate) fn pooled(
78        size: RenderSize,
79        format: wgpu::TextureFormat,
80        lease: crate::pool::TextureLease,
81    ) -> Self {
82        let (texture, view, recycle) = lease.into_parts();
83        Self {
84            size,
85            format,
86            texture: texture.clone(),
87            view: view.clone(),
88            _recycle: Some(Shared::new(RecycleGuard {
89                texture,
90                view,
91                token: Some(recycle),
92            })),
93        }
94    }
95}
96
97/// A texture the renderer draws from as a source, borrowed from whoever owns
98/// it: a [`RenderedTexture`], an [`crate::UploadTexture`] (what
99/// [`crate::Renderer::upload_rgba8`] returns), or, through the `wgpu`
100/// interop, any texture view of the renderer's device.
101///
102/// A source texture stores premultiplied, sRGB-encoded RGBA in a non-sRGB
103/// format. The owner keeps it alive and unchanged for as long as the
104/// renderer may read it: until the frame drawn from it is submitted.
105#[derive(Clone, Copy, Debug)]
106pub struct SourceView<'a> {
107    view: &'a wgpu::TextureView,
108}
109
110impl<'a> SourceView<'a> {
111    /// A source drawn from `view`, a view of a texture on the renderer's
112    /// device.
113    #[cfg_attr(not(feature = "wgpu-interop"), doc(hidden))]
114    #[must_use]
115    pub const fn from_wgpu(view: &'a wgpu::TextureView) -> Self {
116        Self { view }
117    }
118
119    /// The texture view the renderer samples.
120    #[cfg_attr(not(feature = "wgpu-interop"), doc(hidden))]
121    #[must_use]
122    pub const fn wgpu_view(self) -> &'a wgpu::TextureView {
123        self.view
124    }
125}
126
127impl<'a> From<&'a RenderedTexture> for SourceView<'a> {
128    fn from(texture: &'a RenderedTexture) -> Self {
129        Self {
130            view: &texture.view,
131        }
132    }
133}
134
135impl<'a> From<&'a crate::UploadTexture> for SourceView<'a> {
136    fn from(texture: &'a crate::UploadTexture) -> Self {
137        Self {
138            view: texture.view(),
139        }
140    }
141}
142
143/// A non-zero render-target extent.
144#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
145pub struct RenderSize {
146    /// Width in pixels.
147    pub width: u32,
148    /// Height in pixels.
149    pub height: u32,
150}
151
152impl RenderSize {
153    /// Largest render-target edge accepted by [`RenderSize::new`].
154    pub const MAX_DIMENSION: u32 = alpha2_media::MAX_FRAME_DIMENSION;
155    /// Largest render-target area accepted by [`RenderSize::new`].
156    pub const MAX_PIXELS: u64 = alpha2_media::MAX_FRAME_PIXELS;
157
158    /// Creates a non-zero render size.
159    ///
160    /// # Errors
161    ///
162    /// Returns [`RenderError::ZeroDimension`] for a zero width or height.
163    pub const fn new(width: u32, height: u32) -> Result<Self, RenderError> {
164        if width == 0 || height == 0 {
165            Err(RenderError::ZeroDimension)
166        } else if width > Self::MAX_DIMENSION
167            || height > Self::MAX_DIMENSION
168            || (width as u64) * (height as u64) > Self::MAX_PIXELS
169        {
170            Err(RenderError::DimensionTooLarge)
171        } else {
172            Ok(Self { width, height })
173        }
174    }
175}