Skip to main content

wrgpgpu/bindings/
texture.rs

1use std::{marker::PhantomData, sync::Arc};
2
3use image::RgbaImage;
4use wgpu::util::DeviceExt;
5
6use super::Bind;
7
8/// Type of texture binding
9pub trait TextureBindType {
10	fn binding_type(format: wgpu::TextureFormat) -> wgpu::BindingType;
11	fn texture_usages() -> wgpu::TextureUsages;
12}
13
14#[derive(Debug)]
15/// Normal Texture Binding
16pub struct TextureType;
17#[derive(Debug)]
18/// Read/Write Storage Texture Binding
19///
20/// Needs a special features in wgpu to use, and doesn't work on web
21pub struct StorageReadWriteTextureType;
22#[derive(Debug)]
23/// Read Only Storage Texture Binding
24///
25/// Needs a special features in wgpu to use, and doesn't work on web
26pub struct StorageReadOnlyTextureType;
27#[derive(Debug)]
28/// Write Storage Texture Binding
29pub struct StorageWriteOnlyTextureType;
30
31impl TextureBindType for TextureType {
32	fn binding_type(_format: wgpu::TextureFormat) -> wgpu::BindingType {
33		wgpu::BindingType::Texture {
34			sample_type: wgpu::TextureSampleType::Float { filterable: true },
35			view_dimension: wgpu::TextureViewDimension::D2,
36			multisampled: false,
37		}
38	}
39	fn texture_usages() -> wgpu::TextureUsages {
40		wgpu::TextureUsages::TEXTURE_BINDING
41	}
42}
43impl TextureBindType for StorageReadWriteTextureType {
44	fn binding_type(format: wgpu::TextureFormat) -> wgpu::BindingType {
45		wgpu::BindingType::StorageTexture {
46			access: wgpu::StorageTextureAccess::ReadWrite,
47			format,
48			view_dimension: wgpu::TextureViewDimension::D2,
49		}
50	}
51	fn texture_usages() -> wgpu::TextureUsages {
52		wgpu::TextureUsages::STORAGE_BINDING
53	}
54}
55impl TextureBindType for StorageReadOnlyTextureType {
56	fn binding_type(format: wgpu::TextureFormat) -> wgpu::BindingType {
57		wgpu::BindingType::StorageTexture {
58			access: wgpu::StorageTextureAccess::ReadOnly,
59			format,
60			view_dimension: wgpu::TextureViewDimension::D2,
61		}
62	}
63	fn texture_usages() -> wgpu::TextureUsages {
64		wgpu::TextureUsages::STORAGE_BINDING
65	}
66}
67impl TextureBindType for StorageWriteOnlyTextureType {
68	fn binding_type(format: wgpu::TextureFormat) -> wgpu::BindingType {
69		wgpu::BindingType::StorageTexture {
70			access: wgpu::StorageTextureAccess::WriteOnly,
71			format,
72			view_dimension: wgpu::TextureViewDimension::D2,
73		}
74	}
75	fn texture_usages() -> wgpu::TextureUsages {
76		wgpu::TextureUsages::STORAGE_BINDING
77	}
78}
79
80/// A regular texture bind, does not have a sampler
81pub type PlainTextureBind<I> = TextureBind<I, TextureType>;
82/// A storage texture bind, can only be written
83pub type StorageTextureBind<I> = TextureBind<I, StorageWriteOnlyTextureType>;
84/// A storage texture bind backed by an [`image::RgbaImage`]
85pub type RgbaStorageTextureBind = TextureBind<RgbaImage, StorageWriteOnlyTextureType>;
86
87/// Images that can be bound to the gpu
88pub trait BindableImage {
89	fn from_vec(width: u32, height: u32, vec: Vec<u8>) -> Self;
90	fn size(&self) -> (u32, u32);
91	fn as_slice(&self) -> &[u8];
92	fn format() -> wgpu::TextureFormat;
93}
94
95impl BindableImage for RgbaImage {
96	fn from_vec(width: u32, height: u32, vec: Vec<u8>) -> Self {
97		RgbaImage::from_vec(width, height, vec).unwrap()
98	}
99
100	fn size(&self) -> (u32, u32) {
101		(self.width(), self.height())
102	}
103
104	fn as_slice(&self) -> &[u8] {
105		self.as_raw()
106	}
107
108	fn format() -> wgpu::TextureFormat {
109		wgpu::TextureFormat::Rgba8Unorm
110	}
111}
112
113/// Represents a texture binding on the gpu
114pub struct TextureBind<I: BindableImage, Y: TextureBindType> {
115	pub texture: Arc<wgpu::Texture>,
116	view: wgpu::TextureView,
117	_img: PhantomData<I>,
118	_ty: PhantomData<Y>,
119}
120
121impl<I: BindableImage, Y: TextureBindType> Bind for TextureBind<I, Y> {
122	type Data = I;
123	type CreateInfo = (u32, u32);
124
125	fn binding_type() -> wgpu::BindingType {
126		Y::binding_type(I::format())
127	}
128
129	fn bind(&self) -> wgpu::BindingResource {
130		wgpu::BindingResource::TextureView(&self.view)
131	}
132
133	fn download(&self, device: &crate::Device) -> Self::Data {
134		let extent = self.texture.size();
135		let download_buffer = Arc::new(device.device.create_buffer(&wgpu::BufferDescriptor {
136			size: (extent.width
137				* extent.height
138				* extent.depth_or_array_layers
139				* self.texture.format().block_copy_size(None).unwrap_or(4)) as u64,
140			usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::MAP_READ,
141			mapped_at_creation: false,
142			label: None,
143		}));
144
145		let mut encoder = device
146			.device
147			.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });
148		encoder.copy_texture_to_buffer(
149			wgpu::TexelCopyTextureInfo {
150				texture: &self.texture,
151				mip_level: 0,
152				origin: wgpu::Origin3d { x: 0, y: 0, z: 0 },
153				aspect: wgpu::TextureAspect::All,
154			},
155			wgpu::TexelCopyBufferInfo {
156				buffer: &download_buffer,
157				layout: wgpu::TexelCopyBufferLayout {
158					offset: 0,
159					bytes_per_row: Some(((extent.width * 4 + 255) / 256) * 256),
160					rows_per_image: None,
161				},
162			},
163			extent,
164		);
165		let command_buffer: wgpu::CommandBuffer = encoder.finish();
166		device.queue.submit(Some(command_buffer));
167
168		let buffer_content = Arc::new(std::sync::Mutex::new(Some(Vec::new())));
169		let buffer_content_clone = buffer_content.clone();
170		download_buffer
171			.clone()
172			.slice(..)
173			.map_async(wgpu::MapMode::Read, move |result| {
174				result.unwrap();
175				buffer_content_clone
176					.lock()
177					.unwrap()
178					.as_mut()
179					.unwrap()
180					.extend_from_slice(&download_buffer.slice(..).get_mapped_range());
181			});
182		device.device.poll(wgpu::Maintain::Wait);
183		let data = buffer_content.lock().unwrap().take().unwrap();
184		I::from_vec(extent.width, extent.height, data)
185	}
186
187	fn new_empty(device: &crate::Device, size: (u32, u32)) -> Self {
188		let texture = device.device.create_texture(&wgpu::TextureDescriptor {
189			label: Some(std::any::type_name::<I>()),
190			size: wgpu::Extent3d {
191				width: size.0,
192				height: size.1,
193				depth_or_array_layers: 1,
194			},
195			mip_level_count: 1,
196			sample_count: 1,
197			dimension: wgpu::TextureDimension::D2,
198			format: I::format(),
199			usage: wgpu::TextureUsages::COPY_SRC
200				| wgpu::TextureUsages::COPY_DST
201				| wgpu::TextureUsages::TEXTURE_BINDING
202				| Y::texture_usages(),
203			view_formats: &[],
204		});
205		let view = texture.create_view(&wgpu::TextureViewDescriptor {
206			label: Some(std::any::type_name::<I>()),
207			usage: None,
208			format: Some(I::format()),
209			dimension: Some(wgpu::TextureViewDimension::D2),
210			aspect: wgpu::TextureAspect::All,
211			base_mip_level: 0,
212			mip_level_count: None,
213			base_array_layer: 0,
214			array_layer_count: None,
215		});
216		Self {
217			texture: Arc::new(texture),
218			view,
219			_img: PhantomData,
220			_ty: PhantomData,
221		}
222	}
223
224	fn new_init(device: &crate::Device, inner: Self::Data) -> Self {
225		let size = inner.size();
226		let texture = device.device.create_texture_with_data(
227			&device.queue,
228			&wgpu::TextureDescriptor {
229				label: Some(std::any::type_name::<I>()),
230				size: wgpu::Extent3d {
231					width: size.0,
232					height: size.1,
233					depth_or_array_layers: 1,
234				},
235				mip_level_count: 1,
236				sample_count: 1,
237				dimension: wgpu::TextureDimension::D2,
238				format: I::format(),
239				usage: wgpu::TextureUsages::COPY_SRC
240					| wgpu::TextureUsages::COPY_DST
241					| wgpu::TextureUsages::TEXTURE_BINDING
242					| Y::texture_usages(),
243				view_formats: &[],
244			},
245			wgpu::util::TextureDataOrder::LayerMajor,
246			inner.as_slice(),
247		);
248		let view = texture.create_view(&wgpu::TextureViewDescriptor {
249			label: Some(std::any::type_name::<I>()),
250			usage: None,
251			format: Some(I::format()),
252			dimension: Some(wgpu::TextureViewDimension::D2),
253			aspect: wgpu::TextureAspect::All,
254			base_mip_level: 0,
255			mip_level_count: None,
256			base_array_layer: 0,
257			array_layer_count: None,
258		});
259		Self {
260			texture: Arc::new(texture),
261			view,
262			_img: PhantomData,
263			_ty: PhantomData,
264		}
265	}
266}