Skip to main content

pebble/wgpu/
cubemap.rs

1pub struct CubemapSpec {
2    pub size: u32, // cubemaps are square per face
3    pub format: wgpu::TextureFormat,
4    /// `Some` uploads 6 faces of pixel data up front (wgpu's expected
5    /// order: +X, -X, +Y, -Y, +Z, -Z). `None` allocates an empty cubemap
6    /// meant to be filled later by rendering into per-face views — e.g. an
7    /// environment-map capture pass — in which case [`wgpu_descriptor`](Self::wgpu_descriptor)
8    /// adds `RENDER_ATTACHMENT` usage instead of requiring upload data.
9    pub faces: Option<[Vec<u8>; 6]>,
10}
11
12impl CubemapSpec {
13    pub fn wgpu_descriptor(&self) -> wgpu::TextureDescriptor<'static> {
14        let usage = match self.faces {
15            Some(_) => wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST,
16            None => {
17                wgpu::TextureUsages::TEXTURE_BINDING
18                    | wgpu::TextureUsages::COPY_DST
19                    | wgpu::TextureUsages::RENDER_ATTACHMENT
20            }
21        };
22
23        wgpu::TextureDescriptor {
24            label: None,
25            size: wgpu::Extent3d {
26                width: self.size,
27                height: self.size,
28                depth_or_array_layers: 6,
29            },
30            mip_level_count: 1,
31            sample_count: 1,
32            dimension: wgpu::TextureDimension::D2,
33            format: self.format,
34            usage,
35            view_formats: &[],
36        }
37    }
38
39    /// The one part that's easy to get wrong by hand: a cubemap's VIEW
40    /// must be created with `TextureViewDimension::Cube`, not the
41    /// default. This is the view you sample from; use
42    /// [`face_view_descriptor`](Self::face_view_descriptor) to get a
43    /// single-layer `D2` view for rendering into one face.
44    pub fn view_descriptor(&self) -> wgpu::TextureViewDescriptor<'static> {
45        wgpu::TextureViewDescriptor {
46            dimension: Some(wgpu::TextureViewDimension::Cube),
47            array_layer_count: Some(6),
48            ..Default::default()
49        }
50    }
51
52    /// A single-face `D2` view over layer `face` (0..6, wgpu's +X, -X, +Y,
53    /// -Y, +Z, -Z order) — what you attach as a `RenderPassColorAttachment`
54    /// to draw into that one face of an [`empty`](Self::empty) cubemap.
55    pub fn face_view_descriptor(&self, face: u32) -> wgpu::TextureViewDescriptor<'static> {
56        wgpu::TextureViewDescriptor {
57            dimension: Some(wgpu::TextureViewDimension::D2),
58            base_array_layer: face,
59            array_layer_count: Some(1),
60            ..Default::default()
61        }
62    }
63
64    /// Create the texture and, if [`faces`](Self::faces) is `Some`, upload
65    /// all 6 faces. Returns the texture and a `Cube`-dimension view over it
66    /// (suitable for sampling). For an [`empty`](Self::empty) cubemap,
67    /// create per-face views yourself with [`face_view_descriptor`](Self::face_view_descriptor)
68    /// to render into it.
69    pub fn upload(&self, device: &wgpu::Device, queue: &wgpu::Queue) -> (wgpu::Texture, wgpu::TextureView) {
70        let texture = device.create_texture(&self.wgpu_descriptor());
71
72        if let Some(faces) = &self.faces {
73            for (face, data) in faces.iter().enumerate() {
74                queue.write_texture(
75                    wgpu::TexelCopyTextureInfo {
76                        texture: &texture,
77                        mip_level: 0,
78                        origin: wgpu::Origin3d {
79                            x: 0,
80                            y: 0,
81                            z: face as u32,
82                        },
83                        aspect: wgpu::TextureAspect::All,
84                    },
85                    data,
86                    wgpu::TexelCopyBufferLayout {
87                        offset: 0,
88                        bytes_per_row: Some(4 * self.size),
89                        rows_per_image: Some(self.size),
90                    },
91                    wgpu::Extent3d {
92                        width: self.size,
93                        height: self.size,
94                        depth_or_array_layers: 1,
95                    },
96                );
97            }
98        }
99
100        let view = texture.create_view(&self.view_descriptor());
101        (texture, view)
102    }
103}
104
105impl CubemapSpec {
106    pub fn new(size: u32, faces: [Vec<u8>; 6]) -> Self {
107        Self {
108            size,
109            format: wgpu::TextureFormat::Rgba8Unorm,
110            faces: Some(faces),
111        }
112    }
113
114    /// An empty cubemap with no data uploaded — allocated with
115    /// `RENDER_ATTACHMENT` usage so it can be filled later by rendering
116    /// into each face (via [`face_view_descriptor`](Self::face_view_descriptor)),
117    /// e.g. an environment-map capture pass.
118    pub fn empty(size: u32) -> Self {
119        Self {
120            size,
121            format: wgpu::TextureFormat::Rgba8Unorm,
122            faces: None,
123        }
124    }
125
126    pub fn with_format(mut self, format: wgpu::TextureFormat) -> Self {
127        self.format = format;
128        self
129    }
130}