1pub struct TextureSpec {
2 pub width: u32,
3 pub height: u32,
4 pub format: wgpu::TextureFormat,
5 pub data: Vec<u8>,
9 pub generate_mips: bool,
10}
11
12impl TextureSpec {
13 pub fn mip_level_count(&self) -> u32 {
16 if self.generate_mips {
17 32 - self.width.max(self.height).max(1).leading_zeros()
18 } else {
19 1
20 }
21 }
22
23 pub fn wgpu_descriptor(&self) -> wgpu::TextureDescriptor<'static> {
29 wgpu::TextureDescriptor {
30 label: None,
31 size: wgpu::Extent3d {
32 width: self.width,
33 height: self.height,
34 depth_or_array_layers: 1,
35 },
36 mip_level_count: self.mip_level_count(),
37 sample_count: 1,
38 dimension: wgpu::TextureDimension::D2,
39 format: self.format,
40 usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST,
41 view_formats: &[],
42 }
43 }
44
45 fn downsample(data: &[u8], width: u32, height: u32) -> (Vec<u8>, u32, u32) {
47 let next_width = (width / 2).max(1);
48 let next_height = (height / 2).max(1);
49 let mut out = vec![0u8; (next_width * next_height * 4) as usize];
50
51 for y in 0..next_height {
52 for x in 0..next_width {
53 let x0 = (x * 2).min(width - 1);
54 let x1 = (x * 2 + 1).min(width - 1);
55 let y0 = (y * 2).min(height - 1);
56 let y1 = (y * 2 + 1).min(height - 1);
57
58 for c in 0..4 {
59 let sample = |sx: u32, sy: u32| -> u32 {
60 data[((sy * width + sx) * 4 + c) as usize] as u32
61 };
62 let avg = (sample(x0, y0) + sample(x1, y0) + sample(x0, y1) + sample(x1, y1))
63 / 4;
64 out[((y * next_width + x) * 4 + c) as usize] = avg as u8;
65 }
66 }
67 }
68
69 (out, next_width, next_height)
70 }
71
72 pub fn upload(&self, device: &wgpu::Device, queue: &wgpu::Queue) -> (wgpu::Texture, wgpu::TextureView) {
76 let texture = device.create_texture(&self.wgpu_descriptor());
77 let mip_count = self.mip_level_count();
78
79 let mut level_data = self.data.clone();
80 let mut level_width = self.width;
81 let mut level_height = self.height;
82
83 for level in 0..mip_count {
84 queue.write_texture(
85 wgpu::TexelCopyTextureInfo {
86 texture: &texture,
87 mip_level: level,
88 origin: wgpu::Origin3d::default(),
89 aspect: wgpu::TextureAspect::All,
90 },
91 &level_data,
92 wgpu::TexelCopyBufferLayout {
93 offset: 0,
94 bytes_per_row: Some(4 * level_width),
95 rows_per_image: Some(level_height),
96 },
97 wgpu::Extent3d {
98 width: level_width,
99 height: level_height,
100 depth_or_array_layers: 1,
101 },
102 );
103
104 if level + 1 < mip_count {
105 let (next_data, next_width, next_height) =
106 Self::downsample(&level_data, level_width, level_height);
107 level_data = next_data;
108 level_width = next_width;
109 level_height = next_height;
110 }
111 }
112
113 let view = texture.create_view(&wgpu::TextureViewDescriptor::default());
114 (texture, view)
115 }
116}
117
118impl TextureSpec {
119 pub fn new(width: u32, height: u32, data: Vec<u8>, generate_mips: bool) -> Self {
120 Self {
121 width,
122 height,
123 format: wgpu::TextureFormat::Rgba8Unorm,
124 data,
125 generate_mips,
126 }
127 }
128
129 pub fn with_format(mut self, format: wgpu::TextureFormat) -> Self {
130 self.format = format;
131 self
132 }
133}