1use super::RenderError;
2use image::DynamicImage;
3use wgpu::Extent3d;
4
5pub struct Texture {
7 pub tex: wgpu::Texture,
8 pub view: wgpu::TextureView,
9 pub sampler: wgpu::Sampler,
10 size: Extent3d,
11 format: wgpu::TextureFormat,
13}
14
15impl Texture {
16 pub fn new(
17 device: &wgpu::Device,
18 queue: &wgpu::Queue,
19 image: &DynamicImage,
20 filter_method: Option<wgpu::FilterMode>,
21 address_mode: Option<wgpu::AddressMode>,
22 ) -> Result<Self, RenderError> {
23 let format = match image {
24 DynamicImage::ImageLuma8(_) => wgpu::TextureFormat::R8Unorm,
25 DynamicImage::ImageLumaA8(_) => panic!("ImageLuma8 unsupported"),
26 DynamicImage::ImageRgb8(_) => panic!("ImageRgb8 unsupported"),
27 DynamicImage::ImageRgba8(_) => wgpu::TextureFormat::Rgba8UnormSrgb,
28 DynamicImage::ImageLuma16(_) => panic!("ImageLuma16 unsupported"),
29 DynamicImage::ImageLumaA16(_) => panic!("ImageLumaA16 unsupported"),
30 DynamicImage::ImageRgb16(_) => panic!("ImageRgb16 unsupported"),
31 DynamicImage::ImageRgba16(_) => panic!("ImageRgba16 unsupported"),
32 _ => panic!("unsupported format"),
33 };
34
35 let buffer = image.as_flat_samples_u8().ok_or_else(|| {
37 RenderError::CustomError(
38 "We currently do not support color formats using more than 4 bytes / pixel.".into(),
39 )
40 })?;
41
42 let bytes_per_pixel = u32::from(buffer.layout.channels);
43
44 let size = Extent3d {
45 width: image.width(),
46 height: image.height(),
47 depth_or_array_layers: 1,
48 };
49
50 let tex = device.create_texture(&wgpu::TextureDescriptor {
51 label: None,
52 size,
53 mip_level_count: 1,
54 sample_count: 1,
55 dimension: wgpu::TextureDimension::D2,
56 format,
57 usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST,
58 view_formats: &[],
59 });
60
61 queue.write_texture(
62 wgpu::TexelCopyTextureInfo {
63 texture: &tex,
64 mip_level: 0,
65 origin: wgpu::Origin3d::ZERO,
66 aspect: wgpu::TextureAspect::All,
67 },
68 buffer.as_slice(),
69 wgpu::TexelCopyBufferLayout {
70 offset: 0,
71 bytes_per_row: Some(image.width() * bytes_per_pixel),
72 rows_per_image: Some(image.height()),
73 },
74 Extent3d {
75 width: image.width(),
76 height: image.height(),
77 depth_or_array_layers: 1,
78 },
79 );
80
81 let sampler_info = wgpu::SamplerDescriptor {
82 label: None,
83 address_mode_u: address_mode.unwrap_or(wgpu::AddressMode::ClampToEdge),
84 address_mode_v: address_mode.unwrap_or(wgpu::AddressMode::ClampToEdge),
85 address_mode_w: address_mode.unwrap_or(wgpu::AddressMode::ClampToEdge),
86 mag_filter: filter_method.unwrap_or(wgpu::FilterMode::Nearest),
87 min_filter: filter_method.unwrap_or(wgpu::FilterMode::Nearest),
88 mipmap_filter: wgpu::FilterMode::Nearest,
89 ..Default::default()
90 };
91
92 let view = tex.create_view(&wgpu::TextureViewDescriptor {
93 label: None,
94 format: Some(format),
95 dimension: Some(wgpu::TextureViewDimension::D2),
96 usage: None,
97 aspect: wgpu::TextureAspect::All,
98 base_mip_level: 0,
99 mip_level_count: None,
100 base_array_layer: 0,
101 array_layer_count: None,
102 });
103
104 Ok(Self {
105 tex,
106 view,
107 sampler: device.create_sampler(&sampler_info),
108 size,
109 format,
110 })
111 }
112
113 pub fn new_dynamic(
114 device: &wgpu::Device,
115 queue: &wgpu::Queue,
116 width: u32,
117 height: u32,
118 ) -> Self {
119 let size = Extent3d {
120 width,
121 height,
122 depth_or_array_layers: 1,
123 };
124
125 let tex_info = wgpu::TextureDescriptor {
126 label: None,
127 size,
128 mip_level_count: 1,
129 sample_count: 1,
130 dimension: wgpu::TextureDimension::D2,
131 format: wgpu::TextureFormat::Rgba8UnormSrgb,
132 usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST,
134 view_formats: &[],
135 };
136
137 let sampler_info = wgpu::SamplerDescriptor {
138 label: None,
139 address_mode_u: wgpu::AddressMode::ClampToEdge,
140 address_mode_v: wgpu::AddressMode::ClampToEdge,
141 address_mode_w: wgpu::AddressMode::ClampToEdge,
142 mag_filter: wgpu::FilterMode::Linear,
143 min_filter: wgpu::FilterMode::Linear,
144 mipmap_filter: wgpu::FilterMode::Nearest,
145 ..Default::default()
146 };
147
148 let view_info = wgpu::TextureViewDescriptor {
149 label: None,
150 format: Some(wgpu::TextureFormat::Rgba8UnormSrgb),
151 dimension: Some(wgpu::TextureViewDimension::D2),
152 usage: None,
153 aspect: wgpu::TextureAspect::All,
154 base_mip_level: 0,
155 mip_level_count: None,
156 base_array_layer: 0,
157 array_layer_count: None,
158 };
159
160 let texture = Self::new_raw(device, &tex_info, &view_info, &sampler_info);
161 texture.clear(queue); texture
163 }
164
165 pub fn new_raw(
168 device: &wgpu::Device,
169 texture_info: &wgpu::TextureDescriptor,
170 view_info: &wgpu::TextureViewDescriptor,
171 sampler_info: &wgpu::SamplerDescriptor,
172 ) -> Self {
173 let tex = device.create_texture(texture_info);
174 let view = tex.create_view(view_info);
175
176 Self {
177 tex,
178 view,
179 sampler: device.create_sampler(sampler_info),
180 size: texture_info.size,
181 format: texture_info.format,
182 }
183 }
184
185 pub fn clear(&self, queue: &wgpu::Queue) {
187 let size = self.size;
188 let byte_len = size.width as usize
189 * size.height as usize
190 * size.depth_or_array_layers as usize
191 * self.format.block_copy_size(None).unwrap() as usize;
192 let zeros = vec![0; byte_len];
193
194 self.update(queue, [0, 0], [size.width, size.height], &zeros);
195 }
196
197 pub fn update(&self, queue: &wgpu::Queue, offset: [u32; 2], size: [u32; 2], data: &[u8]) {
200 let bytes_per_pixel = self.format.block_copy_size(None).unwrap();
201
202 debug_assert_eq!(
203 data.len(),
204 size[0] as usize * size[1] as usize * bytes_per_pixel as usize
205 );
206 queue.write_texture(
208 wgpu::TexelCopyTextureInfo {
209 texture: &self.tex,
210 mip_level: 0,
211 origin: wgpu::Origin3d {
212 x: offset[0],
213 y: offset[1],
214 z: 0,
215 },
216 aspect: wgpu::TextureAspect::All,
217 },
218 data,
219 wgpu::TexelCopyBufferLayout {
220 offset: 0,
221 bytes_per_row: Some(size[0] * bytes_per_pixel),
222 rows_per_image: Some(size[1]),
223 },
224 Extent3d {
225 width: size[0],
226 height: size[1],
227 depth_or_array_layers: 1,
228 },
229 );
230 }
231
232 pub fn get_dimensions(&self) -> vek::Vec3<u32> {
235 vek::Vec3::new(
236 self.size.width,
237 self.size.height,
238 self.size.depth_or_array_layers,
239 )
240 }
241}