est_render/gpu/texture/
mod.rs

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("Graphics context is invalid".to_string());
30            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; // For RGBA8/BGRA8, etc. Adjust if needed.
391        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        // Remove row padding
437        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        // Cast to T
456        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.blend == other.blend &&
544        // self.sampler_info == other.sampler_info &&
545        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    /// Create the texture with file path.
638    pub fn set_file(mut self, file_path: &'a str) -> Self {
639        self.data = TextureBuilderData::File(file_path);
640        self
641    }
642
643    /// Sets the texture data from a file byte data.
644    pub fn set_file_data(mut self, data: &'a [u8]) -> Self {
645        self.data = TextureBuilderData::Data(data);
646        self
647    }
648
649    /// Initializes a texture with raw image data.
650    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    /// Initializes a texture as a render target.
660    ///
661    /// This method sets the texture as a render target with the specified size and format.
662    /// The size must be non-zero, and the format can be specified or defaulted to the swapchain format or RGBA8_UNORM_SRGB if the
663    /// swapchain format is not available.
664    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    /// Sets the sample count for the texture.
674    ///
675    /// This method allows you to specify the sample count for the texture. The default is 1.
676    /// **NOTE:** Will panic in WASM (not supported atm) which only support 1x and 4x.
677    pub fn set_sample_count(mut self, sample_count: SampleCount) -> Self {
678        self.sample_count = sample_count.into();
679        self
680    }
681
682    /// Initializes a texture as a depth stencil texture.
683    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    /// Sets the number of mip levels for the texture.
696    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    /// Sets the usage of the texture.
702    ///
703    /// This method allows you to specify the usage of the texture. However it cannot set the texture as
704    /// a render target, as that must be done using the `with_render_target` method.
705    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}