steamengine_renderer/
texture.rs

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
10/// This structure contrains the dimensions of the texture
11pub enum TextureDimensions {
12    D3(u32, u32, u32),
13    D2(u32, u32),
14    D1(u32),
15}
16impl TextureDimensions {
17    /// Create a new 3d texture dimension
18    pub fn new_3d(width: u32, height: u32, depth: u32) -> Self {
19        Self::D3(width, height, depth)
20    }
21    /// Create a new 2d texture dimension
22    pub fn new_2d(width: u32, height: u32) -> Self {
23        Self::D2(width, height)
24    }
25    /// Create a new 1d texture dimension
26    pub fn new_1d(width: u32) -> Self {
27        Self::D1(width)
28    }
29
30    /// Convert the dimension to wgpu::TextureDimension
31    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    /// Build the dimension to an Extent3d
40    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}
60/// This is the builder of the texture
61pub struct TextureBuilder {
62    // Texture Size
63    /// this is the size of the texture
64    dimension: Option<TextureDimensions>,
65
66    // Texture Descriptor
67    /// default 1
68    mip_level_count: Option<u32>,
69    /// default 1
70    sample_count: Option<u32>,
71    /// default Bgra8UnormSrgb
72    format: Option<TextureFormat>,
73    /// default TextureUsages::TEXTURE_BINDING | TextureUsages::COPY_DST
74    usage: Option<TextureUsages>,
75
76    // Texel info
77    /// default 0
78    mip_level: Option<u32>,
79    /// default ZERO
80    origin: Option<wgpu::Origin3d>,
81    /// default All
82    aspect: Option<wgpu::TextureAspect>,
83
84    // buffer info
85    /// default 0
86    offset: Option<u64>,
87    /// default 4 * size.width
88    bytes_per_row: Option<u32>,
89    /// default size.height
90    rows_per_image: Option<u32>,
91
92    // image data
93    /// this is the data of the texture
94    data: Option<Vec<u8>>,
95}
96impl TextureBuilder {
97    /// create a new texture builder
98    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 data = self.data.expect("Image don't uploaded to texture");
195
196        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}