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}