Skip to main content

wisp/texture/
video_texture.rs

1//! `VideoTexture` — texture backed by per-frame BGRA uploads.
2//!
3//! Used by the recorder to feed decoded screen-capture frames into the scene.
4//! Wraps a [`crate::texture::Texture`] with `Bgra8UnormSrgb` format and
5//! `TEXTURE_BINDING | COPY_DST` usage; each frame the host calls
6//! [`VideoTexture::upload_bgra`].
7
8use crate::application::Application;
9use crate::texture::Texture;
10
11/// GPU texture backed by per-frame BGRA8 uploads.
12#[derive(Clone, Debug)]
13pub struct VideoTexture {
14    texture: Texture,
15}
16
17impl VideoTexture {
18    /// Allocate a new `VideoTexture` with the given dimensions.
19    #[must_use]
20    pub fn new(app: &Application, width: u32, height: u32) -> Self {
21        Self {
22            texture: Texture::empty(app, width, height, wgpu::TextureFormat::Bgra8UnormSrgb),
23        }
24    }
25
26    /// Upload a frame of BGRA8 bytes. `bytes.len()` must equal `width * height * 4`.
27    ///
28    /// # Panics
29    ///
30    /// Panics if the byte slice length doesn't match the texture dimensions.
31    pub fn upload_bgra(&self, app: &Application, bytes: &[u8]) {
32        let expected = (self.texture.width() as usize)
33            .checked_mul(self.texture.height() as usize)
34            .and_then(|n| n.checked_mul(4))
35            .expect("VideoTexture: dimensions overflow usize");
36        assert_eq!(
37            bytes.len(),
38            expected,
39            "VideoTexture::upload_bgra: byte length mismatch"
40        );
41
42        app.queue().write_texture(
43            wgpu::TexelCopyTextureInfo {
44                texture: self.texture.wgpu_texture(),
45                mip_level: 0,
46                origin: wgpu::Origin3d::ZERO,
47                aspect: wgpu::TextureAspect::All,
48            },
49            bytes,
50            wgpu::TexelCopyBufferLayout {
51                offset: 0,
52                bytes_per_row: Some(self.texture.width() * 4),
53                rows_per_image: Some(self.texture.height()),
54            },
55            wgpu::Extent3d {
56                width: self.texture.width(),
57                height: self.texture.height(),
58                depth_or_array_layers: 1,
59            },
60        );
61    }
62
63    /// Borrow the underlying [`Texture`] for sampling.
64    #[must_use]
65    pub fn texture(&self) -> &Texture {
66        &self.texture
67    }
68
69    /// Width in pixels.
70    #[must_use]
71    pub fn width(&self) -> u32 {
72        self.texture.width()
73    }
74
75    /// Height in pixels.
76    #[must_use]
77    pub fn height(&self) -> u32 {
78        self.texture.height()
79    }
80}