1use tracing::*;
2use wgpu::{
3 BindingType, Origin3d, Sampler, ShaderStages, TexelCopyBufferLayout, TexelCopyTextureInfo,
4 TextureAspect, TextureDescriptor, TextureDimension, TextureFormat, TextureUsages, TextureView,
5 TextureViewDescriptor,
6};
7
8use super::{Renderer, bind_group::BindGroupEntryBuilder};
9
10pub enum TextureDimensions {
12 D3(u32, u32, u32),
13 D2(u32, u32),
14 D1(u32),
15}
16impl TextureDimensions {
17 pub fn new_3d(width: u32, height: u32, depth: u32) -> Self {
19 Self::D3(width, height, depth)
20 }
21 pub fn new_2d(width: u32, height: u32) -> Self {
23 Self::D2(width, height)
24 }
25 pub fn new_1d(width: u32) -> Self {
27 Self::D1(width)
28 }
29
30 pub fn wgpu_texture_dimension(&self) -> TextureDimension {
32 match self {
33 Self::D3(_, _, _) => TextureDimension::D3,
34 Self::D2(_, _) => TextureDimension::D2,
35 Self::D1(_) => TextureDimension::D1,
36 }
37 }
38
39 pub fn build(self) -> wgpu::Extent3d {
41 match self {
42 Self::D3(width, height, depth) => wgpu::Extent3d {
43 width,
44 height,
45 depth_or_array_layers: depth,
46 },
47 Self::D2(width, height) => wgpu::Extent3d {
48 width,
49 height,
50 depth_or_array_layers: 1,
51 },
52 Self::D1(width) => wgpu::Extent3d {
53 width,
54 height: 1,
55 depth_or_array_layers: 1,
56 },
57 }
58 }
59}
60pub struct TextureBuilder {
62 dimension: Option<TextureDimensions>,
65
66 mip_level_count: Option<u32>,
69 sample_count: Option<u32>,
71 format: Option<TextureFormat>,
73 usage: Option<TextureUsages>,
75
76 mip_level: Option<u32>,
79 origin: Option<wgpu::Origin3d>,
81 aspect: Option<wgpu::TextureAspect>,
83
84 offset: Option<u64>,
87 bytes_per_row: Option<u32>,
89 rows_per_image: Option<u32>,
91
92 data: Option<Vec<u8>>,
95}
96impl TextureBuilder {
97 pub fn new() -> Self {
99 Self {
100 dimension: None,
101 mip_level_count: None,
102 sample_count: None,
103 format: None,
104 usage: None,
105 mip_level: None,
106 origin: None,
107 aspect: None,
108 offset: None,
109 bytes_per_row: None,
110 rows_per_image: None,
111 data: None,
112 }
113 }
114
115 pub fn mip_level_count(mut self, mip_level_count: u32) -> Self {
116 self.mip_level_count = Some(mip_level_count);
117 self
118 }
119
120 pub fn sample_count(mut self, sample_count: u32) -> Self {
121 self.sample_count = Some(sample_count);
122 self
123 }
124
125 pub fn dimension(mut self, dimension: TextureDimensions) -> Self {
126 self.dimension = Some(dimension);
127 self
128 }
129
130 pub fn format(mut self, format: TextureFormat) -> Self {
131 self.format = Some(format);
132 self
133 }
134
135 pub fn usage(mut self, usage: TextureUsages) -> Self {
136 self.usage = Some(usage);
137 self
138 }
139
140 pub fn mip_level(mut self, mip_level: u32) -> Self {
141 self.mip_level = Some(mip_level);
142 self
143 }
144
145 pub fn origin(mut self, origin: Origin3d) -> Self {
146 self.origin = Some(origin);
147 self
148 }
149
150 pub fn aspect(mut self, aspect: TextureAspect) -> Self {
151 self.aspect = Some(aspect);
152 self
153 }
154
155 pub fn buffer_offset(mut self, offset: u64) -> Self {
156 self.offset = Some(offset);
157 self
158 }
159
160 pub fn bytes_per_row(mut self, bytes_per_row: u32) -> Self {
161 self.bytes_per_row = Some(bytes_per_row);
162 self
163 }
164
165 pub fn rows_per_image(mut self, rows_per_image: u32) -> Self {
166 self.rows_per_image = Some(rows_per_image);
167 self
168 }
169
170 pub fn data(mut self, data: Vec<u8>) -> Self {
171 self.data = Some(data);
172 self
173 }
174
175 pub fn build(
176 self,
177 label: &'static str,
178 view_formats: Option<&'static [TextureFormat]>,
179 renderer: &Renderer,
180 ) -> Texture {
181 trace!("Building -- {}", label);
182 let dimensions = self.dimension.expect(
183 "Unable to find texture dimensions on {}, please use function 'dimension' to set-it",
184 );
185 let dimension = dimensions.wgpu_texture_dimension();
186 let size = dimensions.build();
187 let mip_level_count = self.mip_level_count.unwrap_or(1);
188 let sample_count = self.sample_count.unwrap_or(1);
189 let format = self.format.unwrap_or(TextureFormat::Rgba8UnormSrgb);
190 let usage = self
191 .usage
192 .unwrap_or(TextureUsages::TEXTURE_BINDING | TextureUsages::COPY_DST);
193 let label = Some(label);
194 let texture = renderer.device().create_texture(&TextureDescriptor {
197 size,
198 mip_level_count,
199 sample_count,
200 dimension,
201 format,
202 usage,
203 label,
204 view_formats: view_formats.unwrap_or(&[]),
205 });
206
207 if self.data.is_some() {
208 let data = self.data.unwrap();
209 renderer.queue().write_texture(
210 TexelCopyTextureInfo {
211 texture: &texture,
212 mip_level: self.mip_level.unwrap_or(0),
213 origin: self.origin.unwrap_or(Origin3d::ZERO),
214 aspect: self.aspect.unwrap_or(TextureAspect::All),
215 },
216 &data,
217 TexelCopyBufferLayout {
218 offset: self.offset.unwrap_or(0),
219 bytes_per_row: self.bytes_per_row.or(Some(4 * size.width)),
220 rows_per_image: self.rows_per_image.or(Some(size.height)),
221 },
222 size,
223 );
224 }
225
226 Texture {
227 texture,
228 texture_view: None,
229 texture_sampler: None,
230 }
231 }
232}
233
234pub struct Texture {
235 pub texture: wgpu::Texture,
236 pub texture_view: Option<TextureView>,
237 pub texture_sampler: Option<Sampler>,
238}
239impl Texture {
240 pub fn create_view(&self, descriptor: TextureViewDescriptor) -> TextureView {
241 self.texture.create_view(&descriptor)
242 }
243 pub fn create_sampler(
244 &self,
245 descriptor: wgpu::SamplerDescriptor,
246 renderer: &Renderer,
247 ) -> Sampler {
248 renderer.device().create_sampler(&descriptor)
249 }
250 pub fn texture_view(&mut self, descriptor: TextureViewDescriptor) {
251 self.texture_view = Some(self.texture.create_view(&descriptor));
252 }
253 pub fn texture_sampler(&mut self, descriptor: wgpu::SamplerDescriptor, renderer: &Renderer) {
254 self.texture_sampler = Some(self.create_sampler(descriptor, renderer));
255 }
256 pub fn default_bind_group(
257 &self,
258 label: &str,
259 renderer: &Renderer,
260 ) -> (wgpu::BindGroupLayout, wgpu::BindGroup) {
261 let texture_view = self
262 .texture_view
263 .as_ref()
264 .expect("TextureView is None, use 'texture_view' function to set-it");
265 let texture_sampler = self
266 .texture_sampler
267 .as_ref()
268 .expect("TextureSampler is None, use 'texture_sampler' function to set-it");
269
270 let (bind_group, bind_group_layout) = renderer.bind_group(
271 label,
272 &[
273 BindGroupEntryBuilder::new(0)
274 .on(ShaderStages::FRAGMENT)
275 .of(BindingType::Texture {
276 sample_type: wgpu::TextureSampleType::Float { filterable: true },
277 view_dimension: wgpu::TextureViewDimension::D2,
278 multisampled: false,
279 })
280 .with(wgpu::BindingResource::TextureView(texture_view)),
281 BindGroupEntryBuilder::new(1)
282 .on(ShaderStages::FRAGMENT)
283 .of(BindingType::Sampler(wgpu::SamplerBindingType::Filtering))
284 .with(wgpu::BindingResource::Sampler(texture_sampler)),
285 ],
286 );
287
288 (bind_group_layout, bind_group)
289 }
290}