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