simple_wgpu/
texture.rs

1use std::hash::Hash;
2
3use uuid::Uuid;
4
5use crate::{context::Context, RenderTexture};
6
7/// A handle to a GPU texture
8///
9/// The equivalent to [wgpu::Texture]
10#[derive(Clone, Debug)]
11pub struct Texture {
12    id: Uuid,
13    texture: wgpu::Texture,
14    base_mip_level: u32,
15    mip_level_count: u32,
16    sample_count: u32,
17}
18
19/// How to bind a [Texture] to a [BindGroup](crate::BindGroup)
20#[derive(Clone, PartialEq, Eq, Hash, Debug)]
21pub struct TextureBinding {
22    pub(crate) texture: Texture,
23    pub(crate) binding_type: wgpu::BindingType,
24}
25
26impl Texture {
27    /// Create a new empty texture
28    pub fn new(desc: &wgpu::TextureDescriptor, context: &Context) -> Self {
29        let texture = context.device().create_texture(desc);
30
31        Self {
32            id: Uuid::new_v4(),
33            texture,
34            base_mip_level: 0,
35            mip_level_count: desc.mip_level_count,
36            sample_count: desc.sample_count,
37        }
38    }
39
40    /// Create a texture from pixel data
41    pub fn with_data(
42        desc: &wgpu::TextureDescriptor,
43        data: &[u8],
44        bytes_per_row: Option<u32>,
45        context: &Context,
46    ) -> Self {
47        let texture = context.device().create_texture(desc);
48
49        context.queue().write_texture(
50            texture.as_image_copy(),
51            data,
52            wgpu::TexelCopyBufferLayout {
53                offset: 0,
54                // todo: derive automatically from format?
55                bytes_per_row,
56                rows_per_image: None,
57            },
58            desc.size,
59        );
60
61        Self {
62            id: Uuid::new_v4(),
63            texture,
64            base_mip_level: 0,
65            mip_level_count: desc.mip_level_count,
66            sample_count: desc.sample_count,
67        }
68    }
69
70    pub fn size(&self) -> wgpu::Extent3d {
71        self.texture.size()
72    }
73
74    pub fn dimension(&self) -> wgpu::TextureDimension {
75        self.texture.dimension()
76    }
77
78    fn sample_type(&self) -> wgpu::TextureSampleType {
79        match self.texture.format() {
80            wgpu::TextureFormat::R8Unorm
81            | wgpu::TextureFormat::R8Snorm
82            | wgpu::TextureFormat::Rg8Unorm
83            | wgpu::TextureFormat::Rg8Snorm
84            | wgpu::TextureFormat::Rgba8Unorm
85            | wgpu::TextureFormat::Rgba8Snorm
86            | wgpu::TextureFormat::Rgba8UnormSrgb
87            | wgpu::TextureFormat::Bgra8Unorm
88            | wgpu::TextureFormat::Bgra8UnormSrgb
89            | wgpu::TextureFormat::R16Float
90            | wgpu::TextureFormat::Rgba16Float
91            | wgpu::TextureFormat::Rgb10a2Unorm => {
92                wgpu::TextureSampleType::Float { filterable: true }
93            }
94            wgpu::TextureFormat::R8Uint
95            | wgpu::TextureFormat::Rg8Uint
96            | wgpu::TextureFormat::Rgba8Uint
97            | wgpu::TextureFormat::R16Uint
98            | wgpu::TextureFormat::Rg16Uint
99            | wgpu::TextureFormat::Rgba16Uint
100            | wgpu::TextureFormat::R32Uint
101            | wgpu::TextureFormat::Rg32Uint
102            | wgpu::TextureFormat::Rgba32Uint => wgpu::TextureSampleType::Uint,
103            wgpu::TextureFormat::R8Sint
104            | wgpu::TextureFormat::Rg8Sint
105            | wgpu::TextureFormat::Rgba8Sint
106            | wgpu::TextureFormat::R16Sint
107            | wgpu::TextureFormat::Rg16Sint
108            | wgpu::TextureFormat::Rgba16Sint
109            | wgpu::TextureFormat::R32Sint
110            | wgpu::TextureFormat::Rg32Sint
111            | wgpu::TextureFormat::Rgba32Sint => wgpu::TextureSampleType::Sint,
112            _ => wgpu::TextureSampleType::Float { filterable: false },
113        }
114    }
115
116    pub fn view(&self, base_mip_level: u32, mip_level_count: u32) -> Texture {
117        Self {
118            id: self.id.clone(),
119            texture: self.texture.clone(),
120            base_mip_level,
121            mip_level_count,
122            sample_count: self.sample_count,
123        }
124    }
125
126    pub fn as_render_texture(&self, context: &Context) -> RenderTexture {
127        RenderTexture {
128            view: self.get_or_build(context),
129            format: self.texture.format(),
130        }
131    }
132
133    /// Bind this texture for sampling. Must be passed to a [BindGroup](crate::BindGroup)
134    #[must_use]
135    pub fn texture_binding(&self) -> TextureBinding {
136        let view_dimension = match self.texture.dimension() {
137            wgpu::TextureDimension::D1 => wgpu::TextureViewDimension::D1,
138            wgpu::TextureDimension::D2 => wgpu::TextureViewDimension::D2,
139            wgpu::TextureDimension::D3 => wgpu::TextureViewDimension::D3,
140        };
141
142        TextureBinding {
143            texture: self.clone(),
144            binding_type: wgpu::BindingType::Texture {
145                sample_type: self.sample_type(),
146                view_dimension,
147                multisampled: self.sample_count > 1,
148            },
149        }
150    }
151
152    /// Bind this texture as a storage texture. Must be passed to a [BindGroup](crate::BindGroup)
153    #[must_use]
154    pub fn storage_binding(&self) -> TextureBinding {
155        let view_dimension = match self.texture.dimension() {
156            wgpu::TextureDimension::D1 => wgpu::TextureViewDimension::D1,
157            wgpu::TextureDimension::D2 => wgpu::TextureViewDimension::D2,
158            wgpu::TextureDimension::D3 => wgpu::TextureViewDimension::D3,
159        };
160
161        TextureBinding {
162            texture: self.clone(),
163            binding_type: wgpu::BindingType::StorageTexture {
164                access: wgpu::StorageTextureAccess::WriteOnly,
165                format: self.texture.format(),
166                view_dimension,
167            },
168        }
169    }
170
171    pub(crate) fn get_or_build(&self, context: &Context) -> wgpu::TextureView {
172        let mut texture_view_cache = context.caches.texture_view_cache.borrow_mut();
173
174        texture_view_cache
175            .get_or_insert_with(self.clone(), || {
176                self.texture.create_view(&wgpu::TextureViewDescriptor {
177                    label: None,
178                    format: None,
179                    dimension: None,
180                    aspect: wgpu::TextureAspect::All,
181                    base_mip_level: self.base_mip_level,
182                    mip_level_count: Some(self.mip_level_count),
183                    base_array_layer: 0,
184                    array_layer_count: None,
185                    usage: None,
186                })
187            })
188            .clone()
189    }
190}
191
192impl Hash for Texture {
193    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
194        self.id.hash(state);
195        self.base_mip_level.hash(state);
196        self.mip_level_count.hash(state);
197    }
198}
199
200impl PartialEq for Texture {
201    fn eq(&self, other: &Self) -> bool {
202        self.id == other.id
203            && self.base_mip_level == other.base_mip_level
204            && self.mip_level_count == other.mip_level_count
205    }
206}
207
208impl Eq for Texture {}