1pub mod atlas;
2
3mod types;
4pub use types::*;
5
6use std::sync::atomic::AtomicUsize;
7use crate::{gpu::ArcRef, math::Point2};
8
9use super::{
10 GPUInner,
11 buffer::{BufferBuilder, BufferUsage},
12};
13
14#[derive(Debug, Clone)]
15pub struct Texture {
16 pub(crate) graphics: ArcRef<GPUInner>,
17 pub(crate) inner: ArcRef<TextureInner>,
18
19 pub(crate) mapped_buffer: Vec<u8>,
20 pub(crate) mapped_type: TextureMappedType,
21}
22
23static TEXTURE_REF_ID: AtomicUsize = AtomicUsize::new(0);
24
25impl Texture {
26 pub(crate) fn from_builder(builder: TextureBuilder) -> Result<Self, TextureError> {
27 if builder.graphics.borrow().is_invalid {
28 return Err(TextureError::InvalidGPUContext);
30 }
31
32 let texture = match builder.data {
33 TextureBuilderData::Data(data) => {
34 let image = image::load_from_memory(data).map_err(|e| e.to_string());
35 if image.is_err() {
36 crate::dbg_log!(
37 "Failed to load image from memory: {}",
38 image.as_ref().err().unwrap()
39 );
40 return Err(TextureError::InvalidTextureData);
41 }
42
43 let image = image.unwrap();
44
45 let rgba = image.to_rgba8();
46 let dimensions = rgba.dimensions();
47 let size = Point2::new(dimensions.0 as i32, dimensions.1 as i32);
48
49 let texture = Self::create_texture(
50 builder.graphics,
51 size,
52 builder.sample_count,
53 builder.mip_level_count,
54 wgpu::TextureDimension::D2,
55 TextureFormat::Rgba8Unorm,
56 builder.usage,
57 );
58
59 if texture.is_err() {
60 crate::dbg_log!(
61 "Failed to create texture: {}",
62 texture.as_ref().err().unwrap()
63 );
64 return Err(TextureError::InvalidTextureData);
65 }
66
67 let mut texture = texture.unwrap();
68
69 if let Err(e) = texture.write::<u8>(&rgba) {
70 return Err(e);
71 }
72
73 Ok(texture)
74 }
75
76 TextureBuilderData::File(file_path) => {
77 let image = image::open(file_path).map_err(|e| e.to_string());
78 if image.is_err() {
79 crate::dbg_log!(
80 "Failed to load image from file: {}",
81 image.as_ref().err().unwrap()
82 );
83 return Err(TextureError::InvalidTextureData);
84 }
85
86 let image = image.unwrap();
87
88 let rgba = image.to_rgba8();
89 let dimensions = rgba.dimensions();
90 let size = Point2::new(dimensions.0 as i32, dimensions.1 as i32);
91
92 let texture = Self::create_texture(
93 builder.graphics,
94 size,
95 builder.sample_count,
96 builder.mip_level_count,
97 wgpu::TextureDimension::D2,
98 TextureFormat::Rgba8Unorm,
99 builder.usage,
100 );
101
102 if texture.is_err() {
103 crate::dbg_log!(
104 "Failed to create texture: {}",
105 texture.as_ref().err().unwrap()
106 );
107 return Err(TextureError::InvalidTextureData);
108 }
109
110 let mut texture = texture.unwrap();
111
112 if let Err(e) = texture.write::<u8>(&rgba) {
113 crate::dbg_log!("Failed to write texture data: {}", e);
114 return Err(e);
115 }
116
117 Ok(texture)
118 }
119
120 TextureBuilderData::Raw(size, data, format) => {
121 let texture = Self::create_texture(
122 builder.graphics,
123 size,
124 builder.sample_count,
125 builder.mip_level_count,
126 wgpu::TextureDimension::D2,
127 format,
128 builder.usage,
129 );
130
131 if texture.is_err() {
132 crate::dbg_log!(
133 "Failed to create texture: {}",
134 texture.as_ref().err().unwrap()
135 );
136 return Err(TextureError::InvalidTextureData);
137 }
138
139 let mut texture = texture.unwrap();
140 if let Err(e) = texture.write::<u8>(data) {
141 crate::dbg_log!("Failed to write texture data: {}", e);
142 return Err(e);
143 }
144
145 Ok(texture)
146 }
147
148 TextureBuilderData::DepthStencil(size, format) => {
149 let texture = Self::create_texture(
150 builder.graphics,
151 size,
152 builder.sample_count,
153 builder.mip_level_count,
154 wgpu::TextureDimension::D2,
155 format.unwrap(),
156 builder.usage | TextureUsage::RenderAttachment,
157 );
158
159 if texture.is_err() {
160 crate::dbg_log!(
161 "Failed to create depth stencil texture: {}",
162 texture.as_ref().err().unwrap()
163 );
164 return Err(TextureError::InvalidTextureData);
165 }
166
167 texture
168 }
169
170 TextureBuilderData::RenderTarget(size, format) => {
171 let format = {
172 if format.is_none() {
173 let graphics_ref = builder.graphics.borrow();
174
175 if graphics_ref.config.is_none() {
176 crate::dbg_log!(
177 "Using default format (RGBA8_UNORM_SRGB) for render target texture"
178 );
179 TextureFormat::Rgba8UnormSrgb
180 } else {
181 let config = graphics_ref.config.as_ref().unwrap();
182 crate::dbg_log!(
183 "Using swapchain format ({:?}) for render target texture",
184 config.format
185 );
186 config.format.into()
187 }
188 } else {
189 format.unwrap()
190 }
191 };
192
193 let texture = Self::create_texture(
194 builder.graphics,
195 size,
196 builder.sample_count,
197 builder.mip_level_count,
198 wgpu::TextureDimension::D2,
199 TextureFormat::from(format),
200 builder.usage | TextureUsage::RenderAttachment,
201 );
202
203 if texture.is_err() {
204 crate::dbg_log!(
205 "Failed to create render target texture: {}",
206 texture.as_ref().err().unwrap()
207 );
208 return Err(TextureError::InvalidTextureData);
209 }
210
211 texture
212 }
213
214 _ => {
215 return Err(TextureError::InvalidTextureData);
216 }
217 };
218
219 texture
220 }
221
222 fn create_texture(
223 graphics: ArcRef<GPUInner>,
224 size: Point2,
225 sample_count: SampleCount,
226 mip_level_count: u32,
227 dimension: wgpu::TextureDimension,
228 format: TextureFormat,
229 usages: TextureUsage,
230 ) -> Result<Self, TextureError> {
231 if size.x == 0 || size.y == 0 {
232 return Err(TextureError::InvalidTextureSize);
233 }
234
235 let texture_size = wgpu::Extent3d {
236 width: size.x as u32,
237 height: size.y as u32,
238 depth_or_array_layers: 1,
239 };
240
241 let ref_id_label = TEXTURE_REF_ID.fetch_add(1, std::sync::atomic::Ordering::SeqCst);
242 let tex_label = format!("Texture {}", ref_id_label);
243 let view_label = format!("Texture View {}", ref_id_label);
244
245 let texture_create_info = wgpu::TextureDescriptor {
246 size: texture_size,
247 mip_level_count,
248 sample_count: sample_count.clone().into(),
249 dimension,
250 format: format.clone().into(),
251 usage: (wgpu::TextureUsages::COPY_DST | wgpu::TextureUsages::COPY_SRC)
252 | usages.clone().into(),
253 label: Some(tex_label.as_str()),
254 view_formats: &[],
255 };
256
257 let graphics_ref = graphics.borrow();
258 let texture = graphics_ref
259 .device()
260 .create_texture(&texture_create_info);
261
262 let view = texture.create_view(&wgpu::TextureViewDescriptor {
263 label: Some(view_label.as_str()),
264 ..Default::default()
265 });
266
267 let inner = TextureInner {
268 wgpu_texture: texture,
269 wgpu_view: view,
270
271 sample_count,
272 usages,
273 size,
274 format,
275
276 mapped: false,
277 };
278
279 Ok(Self {
280 graphics: ArcRef::clone(&graphics),
281 inner: ArcRef::new(inner),
282 mapped_buffer: vec![],
283 mapped_type: TextureMappedType::Write,
284 })
285 }
286
287 pub fn size(&self) -> Point2 {
288 self.inner.borrow().size
289 }
290
291 pub fn format(&self) -> TextureFormat {
292 self.inner.borrow().format
293 }
294
295 pub fn sample_count(&self) -> SampleCount {
296 self.inner.borrow().sample_count
297 }
298
299 pub fn usages(&self) -> TextureUsage {
300 self.inner.borrow().usages
301 }
302
303 pub fn write<T: bytemuck::Pod>(&mut self, data: &[T]) -> Result<(), TextureError> {
304 if data.is_empty() {
305 return Err(TextureError::InvalidTextureData);
306 }
307
308 let inner = self.inner.borrow();
309
310 let data: Vec<u8> = bytemuck::cast_slice(data).to_vec();
311 let bytes_per_pixel = inner.format.get_size();
312 let unpadded_bytes_per_row = bytes_per_pixel * inner.size.x as u32;
313 let align = wgpu::COPY_BYTES_PER_ROW_ALIGNMENT;
314 let padded_bytes_per_row = ((unpadded_bytes_per_row + align - 1) / align) * align;
315
316 let mut padded_data =
317 Vec::with_capacity((padded_bytes_per_row * inner.size.y as u32) as usize);
318
319 for row in 0..inner.size.y as usize {
320 let start = row * unpadded_bytes_per_row as usize;
321 let end = start + unpadded_bytes_per_row as usize;
322 padded_data.extend_from_slice(&data[start..end]);
323 padded_data.extend(vec![
324 0;
325 (padded_bytes_per_row - unpadded_bytes_per_row) as usize
326 ]);
327 }
328
329 let buffer = BufferBuilder::<u8>::new(self.graphics.clone())
330 .set_data_vec(padded_data)
331 .set_usage(BufferUsage::COPY_SRC)
332 .build();
333
334 if buffer.is_err() {
335 return Err(TextureError::FailedToWrite);
336 }
337
338 let buffer = buffer.unwrap();
339
340 let mut encoder = self.graphics.borrow().device().create_command_encoder(
341 &wgpu::CommandEncoderDescriptor {
342 label: Some("texture write encoder"),
343 },
344 );
345
346 encoder.copy_buffer_to_texture(
347 wgpu::TexelCopyBufferInfoBase {
348 buffer: &buffer.inner.borrow().buffer,
349 layout: wgpu::TexelCopyBufferLayout {
350 offset: 0,
351 bytes_per_row: Some(padded_bytes_per_row),
352 rows_per_image: Some(inner.size.y as u32),
353 },
354 },
355 wgpu::TexelCopyTextureInfo {
356 texture: &inner.wgpu_texture,
357 mip_level: 0,
358 origin: wgpu::Origin3d::ZERO,
359 aspect: wgpu::TextureAspect::All,
360 },
361 wgpu::Extent3d {
362 width: inner.size.x as u32,
363 height: inner.size.y as u32,
364 depth_or_array_layers: 1,
365 },
366 );
367
368 self.graphics
369 .borrow()
370 .queue()
371 .submit(Some(encoder.finish()));
372 _ = self
373 .graphics
374 .borrow()
375 .device()
376 .poll(wgpu::PollType::Wait);
377
378 Ok(())
379 }
380
381 pub fn read<T: bytemuck::Pod>(&self) -> Result<Vec<T>, TextureError> {
382 if self.inner.borrow().size.x == 0 || self.inner.borrow().size.y == 0 {
383 return Err(TextureError::InvalidTextureSize);
384 }
385
386 let inner = self.inner.borrow();
387 let inner_graphics = self.graphics.borrow();
388
389 let bytes_per_pixel = 4; let unpadded_bytes_per_row = bytes_per_pixel * inner.size.x as u32;
391 let align = wgpu::COPY_BYTES_PER_ROW_ALIGNMENT;
392 let padded_bytes_per_row = ((unpadded_bytes_per_row + align - 1) / align) * align;
393
394 let buffer = BufferBuilder::<u8>::new(self.graphics.clone())
395 .set_data_empty((padded_bytes_per_row * inner.size.y as u32) as usize)
396 .set_usage(BufferUsage::COPY_DST | BufferUsage::MAP_READ)
397 .build();
398
399 if buffer.is_err() {
400 return Err(TextureError::FailedToRead);
401 }
402
403 let buffer = buffer.unwrap();
404
405 let mut encoder =
406 inner_graphics
407 .device()
408 .create_command_encoder(&wgpu::CommandEncoderDescriptor {
409 label: Some("texture read encoder"),
410 });
411
412 encoder.copy_texture_to_buffer(
413 wgpu::TexelCopyTextureInfo {
414 texture: &inner.wgpu_texture,
415 mip_level: 0,
416 origin: wgpu::Origin3d::ZERO,
417 aspect: wgpu::TextureAspect::All,
418 },
419 wgpu::TexelCopyBufferInfo {
420 buffer: &buffer.inner.borrow().buffer,
421 layout: wgpu::TexelCopyBufferLayout {
422 offset: 0,
423 bytes_per_row: Some(padded_bytes_per_row),
424 rows_per_image: Some(inner.size.y as u32),
425 },
426 },
427 inner.size.into(),
428 );
429
430 inner_graphics.queue().submit(Some(encoder.finish()));
431 _ = inner_graphics.device().poll(wgpu::PollType::Wait);
432
433 drop(inner_graphics);
434
435 let raw = buffer.read::<u8>();
437
438 if raw.is_err() {
439 return Err(TextureError::FailedToRead);
440 }
441
442 let raw = raw.unwrap();
443
444 let height = inner.size.y as u32;
445 let padded_bytes_per_row = padded_bytes_per_row as u32;
446
447 let mut result = Vec::with_capacity((unpadded_bytes_per_row * height) as usize);
448 for row in 0..height as usize {
449 let start = row * padded_bytes_per_row as usize;
450 let end = start + unpadded_bytes_per_row as usize;
451 result.extend_from_slice(&raw[start..end]);
452 }
453
454 let ptr = result.as_ptr();
456 let len = result.len() / std::mem::size_of::<T>();
457 let mut out = Vec::with_capacity(len);
458 unsafe {
459 out.set_len(len);
460 std::ptr::copy_nonoverlapping(ptr as *const T, out.as_mut_ptr(), len);
461 }
462 Ok(out)
463 }
464
465 pub fn map(&mut self, map_type: TextureMappedType) -> Result<&mut Vec<u8>, TextureError> {
466 let mut inner = self.inner.borrow_mut();
467 if inner.mapped {
468 crate::dbg_log!("Texture is already mapped");
469 return Err(TextureError::AlreadyMapped);
470 }
471
472 match map_type {
473 TextureMappedType::Read => {
474 inner.mapped = true;
475 drop(inner);
476
477 self.mapped_type = TextureMappedType::Read;
478 self.mapped_buffer = self.read::<u8>()?;
479
480 return Ok(&mut self.mapped_buffer);
481 }
482 TextureMappedType::Write => {
483 inner.mapped = true;
484 drop(inner);
485
486 self.mapped_type = TextureMappedType::Write;
487 self.mapped_buffer =
488 vec![0; (self.inner.borrow().size.x * self.inner.borrow().size.y * 4) as usize];
489
490 return Ok(&mut self.mapped_buffer);
491 }
492 }
493 }
494
495 pub fn unmap(&mut self) -> Result<(), TextureError> {
496 let mut inner = self.inner.borrow_mut();
497 if !inner.mapped {
498 crate::dbg_log!("Texture is not mapped");
499 return Err(TextureError::NotMapped);
500 }
501
502 match self.mapped_type {
503 TextureMappedType::Read => {
504 inner.mapped = false;
505 self.mapped_buffer.clear();
506 }
507 TextureMappedType::Write => {
508 inner.mapped = false;
509
510 drop(inner);
511
512 let buffer = self.mapped_buffer.clone();
513
514 if let Err(e) = self.write::<u8>(&buffer) {
515 crate::dbg_log!("Failed to write texture data: {}", e);
516 return Err(e);
517 }
518
519 self.mapped_buffer = vec![];
520 }
521 }
522
523 Ok(())
524 }
525}
526
527impl PartialEq for Texture {
528 fn eq(&self, other: &Self) -> bool {
529 self.inner == other.inner
530 }
531}
532
533impl Eq for Texture {}
534
535impl PartialEq for TextureInner {
536 fn eq(&self, other: &Self) -> bool {
537 self.wgpu_texture == other.wgpu_texture &&
538 self.wgpu_view == other.wgpu_view &&
539 self.size == other.size &&
540 self.usages == other.usages &&
541 self.sample_count == other.sample_count &&
542 self.format == other.format
545 }
546}
547
548impl Eq for TextureInner {}
549
550pub struct TextureInner {
551 pub(crate) wgpu_texture: wgpu::Texture,
552 pub(crate) wgpu_view: wgpu::TextureView,
553
554 pub(crate) size: Point2,
555 pub(crate) usages: TextureUsage,
556 pub(crate) sample_count: SampleCount,
557 pub(crate) format: TextureFormat,
558
559 pub(crate) mapped: bool,
560}
561
562#[derive(Debug, Clone, Copy, PartialEq, Eq)]
563pub enum TextureMappedType {
564 Read,
565 Write,
566}
567
568impl std::fmt::Display for TextureMappedType {
569 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
570 match self {
571 TextureMappedType::Read => write!(f, "Read"),
572 TextureMappedType::Write => write!(f, "Write"),
573 }
574 }
575}
576
577#[derive(Debug, Clone, Copy)]
578pub enum TextureError {
579 InvalidGPUContext,
580 InvalidTextureData,
581 InvalidTextureSize,
582 InvalidTextureFormat,
583 FailedToWrite,
584 FailedToRead,
585 AlreadyMapped,
586 NotMapped,
587}
588
589impl std::fmt::Display for TextureError {
590 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
591 match self {
592 TextureError::InvalidGPUContext => write!(f, "Invalid GPU context"),
593 TextureError::InvalidTextureData => write!(f, "Invalid texture data"),
594 TextureError::InvalidTextureSize => write!(f, "Invalid texture size"),
595 TextureError::InvalidTextureFormat => write!(f, "Invalid texture format"),
596 TextureError::FailedToWrite => write!(f, "Failed to write to texture"),
597 TextureError::FailedToRead => write!(f, "Failed to read from texture"),
598 TextureError::AlreadyMapped => write!(f, "Texture is already mapped"),
599 TextureError::NotMapped => write!(f, "Texture is not mapped"),
600 }
601 }
602}
603
604pub enum TextureBuilderData<'a> {
605 None,
606 File(&'a str),
607 Data(&'a [u8]),
608 Raw(Point2, &'a [u8], TextureFormat),
609 DepthStencil(Point2, Option<TextureFormat>),
610 RenderTarget(Point2, Option<TextureFormat>),
611}
612
613pub struct TextureBuilder<'a> {
614 pub(crate) graphics: ArcRef<GPUInner>,
615 pub(crate) sample_count: SampleCount,
616 pub(crate) mip_level_count: u32,
617 pub(crate) usage: TextureUsage,
618 pub(crate) data: TextureBuilderData<'a>,
619}
620
621impl<'a> TextureBuilder<'a> {
622 pub(crate) fn new(graphics: ArcRef<GPUInner>) -> Self {
623 if graphics.borrow().is_invalid {
624 panic!("Graphics context is invalid");
625 }
626
627 Self {
628 graphics,
629 sample_count: SampleCount::SampleCount1,
630 mip_level_count: 1,
631 usage: TextureUsage::None,
632 data: TextureBuilderData::None,
633 }
634 }
635
636 pub fn set_file(mut self, file_path: &'a str) -> Self {
638 self.data = TextureBuilderData::File(file_path);
639 self
640 }
641
642 pub fn set_file_data(mut self, data: &'a [u8]) -> Self {
644 self.data = TextureBuilderData::Data(data);
645 self
646 }
647
648 pub fn set_raw_image(mut self, data: &'a [u8], size: Point2, format: TextureFormat) -> Self {
650 if format >= TextureFormat::Stencil8 && format <= TextureFormat::Depth32FloatStencil8 {
651 panic!("Depth and stencil formats are not supported in raw data");
652 }
653
654 self.data = TextureBuilderData::Raw(size, data, format);
655 self
656 }
657
658 pub fn set_render_target(mut self, size: Point2, format: Option<TextureFormat>) -> Self {
664 if size.x == 0 || size.y == 0 {
665 panic!("Render target texture must have a size");
666 }
667
668 self.data = TextureBuilderData::RenderTarget(size, format);
669 self
670 }
671
672 pub fn set_sample_count(mut self, sample_count: SampleCount) -> Self {
677 self.sample_count = sample_count.into();
678 self
679 }
680
681 pub fn set_depth_stencil(mut self, size: Point2, format: Option<TextureFormat>) -> Self {
683 if size.x == 0 || size.y == 0 {
684 panic!("Depth stencil texture must have a size");
685 }
686
687 self.data = TextureBuilderData::DepthStencil(
688 size,
689 Some(format.unwrap_or(TextureFormat::Depth32Float)),
690 );
691 self
692 }
693
694 pub fn set_mip_level_count(mut self, mip_level_count: u32) -> Self {
696 self.mip_level_count = mip_level_count;
697 self
698 }
699
700 pub fn set_usage(mut self, usage: TextureUsage) -> Self {
705 if usage.contains(TextureUsage::RenderAttachment) {
706 panic!("Render attachment textures must be created with the render target method");
707 }
708
709 self.usage = usage;
710 self
711 }
712
713 pub fn build(self) -> Result<Texture, TextureError> {
714 Texture::from_builder(self)
715 }
716}