1use std::hash::Hash;
2
3use uuid::Uuid;
4
5use crate::{context::Context, RenderTexture};
6
7#[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#[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 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 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 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 #[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 #[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 {}