makepad_platform/
texture.rs

1use {
2    crate::{
3        id_pool::*,
4        cx::Cx,
5        makepad_math::*,
6        os::CxOsTexture,
7    },
8    std::rc::Rc,
9};
10
11
12#[derive(Debug, Clone, PartialEq)]
13pub struct Texture(Rc<PoolId>);
14
15#[derive(Clone, Debug, PartialEq, Copy)]
16pub struct TextureId(pub (crate) usize, u64);
17
18impl Texture {
19    pub fn texture_id(&self) -> TextureId {TextureId(self.0.id, self.0.generation)}
20}
21
22#[derive(Default)]
23pub struct CxTexturePool(pub (crate) IdPool<CxTexture>);
24
25impl CxTexturePool {
26    // Allocates a new texture in the pool, potentially reusing an existing texture slot.
27    ///
28    /// This method attempts to find a compatible texture slot for reuse. If found, it preserves
29    /// the old os-specific resources for proper cleanup. If not, it allocates a new slot.
30    ///
31    /// # Arguments
32    /// * `requested_format` - The format of the texture to be allocated.
33    ///
34    /// # Returns
35    /// A `Texture` instance representing the allocated or reused texture.
36    ///
37    /// # Note
38    /// When a texture slot is reused, the old platofrm-specific resources are stored in the `previous_platform_resource` field
39    /// of the new `CxTexture`. This allows for proper resource management and cleanup in the corresponding platform.
40    pub fn alloc(&mut self, requested_format: TextureFormat) -> Texture {
41        let is_video = requested_format.is_video();
42        let cx_texture = CxTexture {
43            format: requested_format,
44            alloc: None,
45            ..Default::default()
46        };
47
48        let (new_id, previous_item) = self.0.alloc_with_reuse_filter(|item| {
49            // Check for compatibility, intentionally not using `is_compatible_with` to avoid passing the whole format and cloning vec contents    
50            is_video == item.item.format.is_video()
51        }, cx_texture);
52
53        if let Some(previous_item) = previous_item {
54            // We know this index is valid because it was just reused
55            self.0.pool[new_id.id].item.previous_platform_resource = Some(previous_item.os);
56        }
57
58        Texture(Rc::new(new_id))
59    }
60}
61
62impl std::ops::Index<TextureId> for CxTexturePool {
63    type Output = CxTexture;
64    fn index(&self, index: TextureId) -> &Self::Output {
65        let d = &self.0.pool[index.0];
66        if d.generation != index.1 {
67            error!("Texture id generation wrong {} {} {}", index.0, d.generation, index.1)
68        }
69        &d.item
70    }
71}
72
73impl std::ops::IndexMut<TextureId> for CxTexturePool {
74    fn index_mut(&mut self, index: TextureId) -> &mut Self::Output {
75        let d = &mut self.0.pool[index.0];
76        if d.generation != index.1 {
77            error!("Texture id generation wrong {} {} {}", index.0, d.generation, index.1)
78        }
79        &mut d.item
80    }
81}
82
83
84#[derive(Clone, Debug)]
85pub enum TextureSize {
86    Auto,
87    Fixed{width: usize, height: usize}
88}
89
90impl TextureSize{
91    fn width_height(&self, w:usize, h:usize)->(usize,usize){
92        match self{
93            TextureSize::Auto=>(w,h),
94            TextureSize::Fixed{width, height}=>(*width,*height)
95        }
96    }
97}
98
99
100#[derive(Clone)]
101pub enum TextureFormat {
102    Unknown,
103    VecBGRAu8_32{width:usize, height:usize, data:Option<Vec<u32>>, updated: TextureUpdated},
104    VecMipBGRAu8_32{width:usize, height:usize, data:Option<Vec<u32>>, max_level:Option<usize>, updated: TextureUpdated},
105    VecRGBAf32{width:usize, height:usize, data:Option<Vec<f32>>, updated: TextureUpdated},
106    VecRu8{width:usize, height:usize, data:Option<Vec<u8>>, unpack_row_length:Option<usize>, updated: TextureUpdated},
107    VecRGu8{width:usize, height:usize, data:Option<Vec<u8>>, unpack_row_length:Option<usize>, updated: TextureUpdated},
108    VecRf32{width:usize, height:usize, data:Option<Vec<f32>>, updated: TextureUpdated},
109    DepthD32{size:TextureSize, initial: bool},
110    RenderBGRAu8{size:TextureSize, initial: bool},
111    RenderRGBAf16{size:TextureSize, initial: bool},
112    RenderRGBAf32{size:TextureSize, initial: bool},
113    
114    SharedBGRAu8{width:usize, height:usize, id:crate::cx_stdin::PresentableImageId, initial: bool},
115    #[cfg(any(target_os = "android", target_os = "linux"))]
116    VideoRGB,
117}
118
119impl std::fmt::Debug for TextureFormat{
120    fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error>{
121        match self{
122            TextureFormat::Unknown=>write!(f, "TextureFormat::Unknown"),
123            TextureFormat::VecBGRAu8_32{width, height,..}=>write!(f, "TextureFormat::VecBGRAu8_32(width:{width},height:{height})"),
124            TextureFormat::VecMipBGRAu8_32{width, height,..}=>write!(f, "TextureFormat::VecMipBGRAu8_32(width:{width},height:{height})"),
125            TextureFormat::VecRGBAf32{width, height,..}=>write!(f, "TextureFormat::VecRGBAf32(width:{width},height:{height})"),
126            TextureFormat::VecRu8{width, height,..}=>write!(f, "TextureFormat::VecRu8(width:{width},height:{height})"),
127            TextureFormat::VecRGu8{width, height,..}=>write!(f, "TextureFormat::VecRGu8(width:{width},height:{height})"),
128            TextureFormat::VecRf32{width, height,..}=>write!(f, "TextureFormat::VecRf32(width:{width},height:{height})"),
129            TextureFormat::DepthD32{size,..}=>write!(f, "TextureFormat::DepthD32(size:{:?})", size),
130            TextureFormat::RenderBGRAu8{size,..}=>write!(f, "TextureFormat::RenderBGRAu8(size:{:?})", size),
131            TextureFormat::RenderRGBAf16{size,..}=>write!(f, "TextureFormat::RenderRGBAf16(size:{:?})", size),
132            TextureFormat::RenderRGBAf32{size,..}=>write!(f, "TextureFormat::RenderRGBAf32(size:{:?})", size),
133            TextureFormat::SharedBGRAu8{width,height,..}=>write!(f, "TextureFormat::SharedBGRAu8(width:{width},height:{height})"),
134            #[cfg(any(target_os = "android", target_os = "linux"))]
135            TextureFormat::VideoRGB=>write!(f, "TextureFormat::VideoRGB"),
136        }
137    }
138}
139
140#[derive(Debug, Default, Clone)] 
141pub struct TextureAnimation {
142    pub width: usize,
143    pub height: usize,
144    pub num_frames: usize
145}
146
147#[derive(Clone, Debug, PartialEq)]
148pub(crate) struct TextureAlloc{
149    pub category: TextureCategory,
150    pub pixel: TexturePixel,
151    pub width: usize,
152    pub height: usize,
153}
154
155#[allow(unused)]    
156#[derive(Clone, Debug)]
157pub enum TextureCategory{
158    Vec,
159    Render,
160    DepthBuffer,
161    Shared,
162    Video,
163}
164
165impl PartialEq for TextureCategory{
166    fn eq(&self, other: &TextureCategory) -> bool{
167        match self{
168            Self::Vec{..} => if let Self::Vec{..} = other{true} else {false},
169            Self::Render{..} => if let Self::Render{..} = other{true} else {false},
170            Self::Shared{..} => if let Self::Shared{..} = other{true} else {false},
171            Self::DepthBuffer{..} => if let Self::DepthBuffer{..} = other{true} else {false},           
172            Self::Video{..} => if let Self::Video{..} = other{true} else {false},           
173        }
174    }
175}
176
177#[derive(Clone, Copy, Debug)]
178pub enum TextureUpdated {
179    Empty,
180    Partial(RectUsize),
181    Full,
182}
183
184impl TextureUpdated {
185    pub fn is_empty(&self) -> bool {
186        match self {
187            TextureUpdated::Empty => true,
188            _ => false,
189        }
190    }
191
192    pub fn update(self, dirty_rect: Option<RectUsize>) -> Self {
193        let new = match dirty_rect {
194            Some(dirty_rect) => match self {
195                TextureUpdated::Empty => TextureUpdated::Partial(dirty_rect),
196                TextureUpdated::Partial(rect) => 
197                TextureUpdated::Partial(rect.union(dirty_rect)),
198                TextureUpdated::Full => TextureUpdated::Full,
199            },
200            None => TextureUpdated::Full,
201        };
202        if let TextureUpdated::Partial(p) = new{
203            if p.size == SizeUsize::new(0,0){
204                return TextureUpdated::Empty
205            }
206        }
207        new
208    }
209}
210
211#[allow(unused)]    
212#[derive(Clone, Debug, PartialEq)]
213pub(crate) enum TexturePixel{
214    BGRAu8,
215    RGBAf16,
216    RGBAf32,
217    Ru8,
218    RGu8,
219    Rf32,
220    D32,
221    #[cfg(any(target_os = "android", target_os = "linux"))]
222    VideoRGB
223}
224
225impl CxTexture{
226    pub(crate) fn updated(&self) -> TextureUpdated {
227        match self.format {
228            TextureFormat::VecBGRAu8_32 { updated, .. } => updated,
229            TextureFormat::VecMipBGRAu8_32{ updated, .. } => updated,
230            TextureFormat::VecRGBAf32 { updated, .. } => updated,
231            TextureFormat::VecRu8 { updated, .. } => updated,
232            TextureFormat::VecRGu8 { updated, .. } => updated,
233            TextureFormat::VecRf32 { updated, .. } => updated,
234            _ => panic!(),
235        }
236    }
237    
238    #[allow(unused)]
239    pub(crate) fn initial(&mut self) -> bool {
240        match self.format {
241            TextureFormat::DepthD32{ initial, .. } => initial,
242            TextureFormat::RenderBGRAu8{ initial, .. } => initial,
243            TextureFormat::RenderRGBAf16{ initial, .. } => initial,
244            TextureFormat::RenderRGBAf32{ initial, .. } => initial,
245            TextureFormat::SharedBGRAu8{ initial, .. } => initial,
246            _ => panic!()
247        }
248    }
249
250    pub(crate) fn set_updated(&mut self, updated: TextureUpdated) {
251        *match &mut self.format {
252            TextureFormat::VecBGRAu8_32 { updated, .. } => updated,
253            TextureFormat::VecMipBGRAu8_32{ updated, .. } => updated,
254            TextureFormat::VecRGBAf32 { updated, .. } => updated,
255            TextureFormat::VecRu8 { updated, .. } => updated,
256            TextureFormat::VecRGu8 { updated, .. } => updated,
257            TextureFormat::VecRf32 { updated, .. } => updated,
258            _ => panic!(),
259        } = updated;
260    }
261
262    pub fn set_initial(&mut self, initial: bool) {
263        *match &mut self.format {
264            TextureFormat::DepthD32{ initial, .. } => initial,
265            TextureFormat::RenderBGRAu8{ initial, .. } => initial,
266            TextureFormat::RenderRGBAf16{ initial, .. } => initial,
267            TextureFormat::RenderRGBAf32{ initial, .. } => initial,
268            TextureFormat::SharedBGRAu8{ initial, .. } => initial,
269            _ => panic!()
270        } = initial;
271    }
272
273    pub(crate) fn take_updated(&mut self) -> TextureUpdated {
274        let updated = self.updated();
275        self.set_updated(TextureUpdated::Empty);
276        updated
277    }
278    #[allow(unused)]
279    pub(crate) fn take_initial(&mut self) -> bool {
280        let initial = self.initial();
281        self.set_initial(false);
282        initial
283    }
284        
285    pub(crate) fn alloc_vec(&mut self)->bool{
286        if let Some(alloc) = self.format.as_vec_alloc(){
287            if self.alloc.is_none() || self.alloc.as_ref().unwrap() != &alloc{
288                self.alloc = Some(alloc);
289                return true;
290            }
291        }
292        false
293    }
294    
295    #[allow(unused)]
296    pub(crate) fn alloc_shared(&mut self)->bool{
297        if let Some(alloc) = self.format.as_shared_alloc(){
298            if self.alloc.is_none() || self.alloc.as_ref().unwrap() != &alloc{
299                self.alloc = Some(alloc);
300                return true;
301            }
302        }
303        false
304    }
305    
306    pub(crate) fn alloc_render(&mut self, width:usize, height: usize)->bool{
307        if let Some(alloc) = self.format.as_render_alloc(width, height){
308            if self.alloc.is_none() || self.alloc.as_ref().unwrap() != &alloc{
309                self.alloc = Some(alloc);
310                return true;
311            }
312        }
313        false
314    }
315    
316    pub(crate) fn alloc_depth(&mut self, width:usize, height: usize)->bool{
317        if let Some(alloc) = self.format.as_depth_alloc(width, height){
318            if self.alloc.is_none() || self.alloc.as_ref().unwrap() != &alloc{
319                self.alloc = Some(alloc);
320                return true;
321            }
322        }
323        false
324    }
325
326    #[cfg(any(target_os = "android", target_os = "linux"))]
327    #[allow(unused)]
328    pub(crate) fn alloc_video(&mut self)->bool{
329        if let Some(alloc) = self.format.as_video_alloc(){
330            if self.alloc.is_none() || self.alloc.as_ref().unwrap() != &alloc{
331                self.alloc = Some(alloc);
332                return true;
333            }
334        }
335        false
336    }
337}
338
339impl TextureFormat{
340    pub fn is_shared(&self)->bool{
341         match self{
342             Self::SharedBGRAu8{..}=>true,
343             _=>false
344         }
345    }
346    pub fn is_vec(&self)->bool{
347        match self{
348            Self::VecBGRAu8_32{..}=>true,
349            Self::VecMipBGRAu8_32{..}=>true,
350            Self::VecRGBAf32{..}=>true,
351            Self::VecRu8{..}=>true,
352            Self::VecRGu8{..}=>true,
353            Self::VecRf32{..}=>true,
354            _=>false
355        }
356    }
357    
358    pub fn is_render(&self)->bool{
359        match self{
360            Self::RenderBGRAu8{..}=>true,
361            Self::RenderRGBAf16{..}=>true,
362            Self::RenderRGBAf32{..}=>true,
363            _=>false
364        }
365    }
366        
367    pub fn is_depth(&self)->bool{
368        match self{
369            Self::DepthD32{..}=>true,
370            _=>false
371        }
372    }
373
374    pub fn is_video(&self) -> bool {
375        #[cfg(any(target_os = "android", target_os = "linux"))]
376        if let Self::VideoRGB = self {
377            return true;
378        }
379        false
380    }
381    
382    pub fn vec_width_height(&self)->Option<(usize,usize)>{
383        match self{
384            Self::VecBGRAu8_32{width, height, .. }=>Some((*width,*height)),
385            Self::VecMipBGRAu8_32{width, height, ..}=>Some((*width,*height)),
386            Self::VecRGBAf32{width, height, ..}=>Some((*width,*height)),
387            Self::VecRu8{width, height, ..}=>Some((*width,*height)),
388            Self::VecRGu8{width, height, ..}=>Some((*width,*height)),
389            Self::VecRf32{width, height,..}=>Some((*width,*height)),
390            _=>None
391        }
392    }
393    
394    pub(crate) fn as_vec_alloc(&self)->Option<TextureAlloc>{
395        match self{
396            Self::VecBGRAu8_32{width,height,..}=>Some(TextureAlloc{
397                width:*width,
398                height:*height,
399                pixel:TexturePixel::BGRAu8,
400                category: TextureCategory::Vec,
401            }),
402            Self::VecMipBGRAu8_32{width,height,..}=>Some(TextureAlloc{
403                width:*width,
404                height:*height,
405                pixel:TexturePixel::BGRAu8,
406                category: TextureCategory::Vec,
407            }),
408            Self::VecRGBAf32{width,height,..}=>Some(TextureAlloc{
409                width:*width,
410                height:*height,
411                pixel:TexturePixel::RGBAf32,
412                category: TextureCategory::Vec,
413            }),
414            Self::VecRu8{width,height,..}=>Some(TextureAlloc{
415                width:*width,
416                height:*height,
417                pixel:TexturePixel::Ru8,
418                category: TextureCategory::Vec,
419            }),
420            Self::VecRGu8{width,height,..}=>Some(TextureAlloc{
421                width:*width,
422                height:*height,
423                pixel:TexturePixel::RGu8,
424                category: TextureCategory::Vec,
425            }),
426            Self::VecRf32{width,height,..}=>Some(TextureAlloc{
427                width:*width,
428                height:*height,
429                pixel:TexturePixel::Rf32,
430                category: TextureCategory::Vec,
431            }),
432            _=>None
433        }
434    }
435    #[allow(unused)]    
436    pub(crate) fn as_render_alloc(&self, width:usize, height:usize)->Option<TextureAlloc>{
437        match self{
438            Self::RenderBGRAu8{size,..}=>{
439                let (width,height) = size.width_height(width, height);
440                Some(TextureAlloc{
441                    width,
442                    height,
443                    pixel:TexturePixel::BGRAu8,
444                    category: TextureCategory::Render,
445                })
446            }
447            Self::RenderRGBAf16{size,..}=>{
448                let (width,height) = size.width_height(width, height);
449                Some(TextureAlloc{
450                    width,
451                    height,
452                    pixel:TexturePixel::RGBAf16,
453                    category: TextureCategory::Render,
454                })
455            }
456            Self::RenderRGBAf32{size,..}=>{
457                let (width,height) = size.width_height(width, height);
458                Some(TextureAlloc{
459                    width,
460                    height,
461                    pixel:TexturePixel::RGBAf32,
462                    category: TextureCategory::Render,
463                })
464            }
465            _=>None
466        }
467    }
468        
469    pub(crate) fn as_depth_alloc(&self, width:usize, height:usize)->Option<TextureAlloc>{
470        match self{
471            Self::DepthD32{size,..}=>{
472                let (width,height) = size.width_height(width, height);
473                Some(TextureAlloc{
474                    width,
475                    height,
476                    pixel:TexturePixel::D32,
477                    category: TextureCategory::DepthBuffer,
478                })
479            },
480            _=>None
481        }
482    }
483
484    #[cfg(any(target_os = "android", target_os = "linux"))]
485    #[allow(unused)]
486    pub(crate) fn as_video_alloc(&self)->Option<TextureAlloc>{
487        match self{
488            Self::VideoRGB => {
489                Some(TextureAlloc{
490                    width: 0,
491                    height: 0,
492                    pixel:TexturePixel::VideoRGB,
493                    category: TextureCategory::Video,
494                })
495            },
496            _ => None
497        }
498    }
499    
500    #[allow(unused)]
501    pub(crate) fn as_shared_alloc(&self)->Option<TextureAlloc>{
502        match self{
503            Self::SharedBGRAu8{width, height, ..}=>{
504                Some(TextureAlloc{
505                    width:*width,
506                    height:*height,
507                    pixel:TexturePixel::BGRAu8,
508                    category: TextureCategory::Shared,
509                })
510            }
511            _=>None
512        }
513    }
514
515    #[allow(unused)]
516    fn is_compatible_with(&self, other: &Self) -> bool {
517        #[cfg(any(target_os = "android", target_os = "linux"))]
518        {
519            return !(self.is_video() ^ other.is_video());
520        }
521        true
522    }
523}
524
525impl Default for TextureFormat {
526    fn default() -> Self {
527        TextureFormat::Unknown
528    }
529}
530
531impl Texture {
532    pub fn new(cx: &mut Cx) -> Self {
533        cx.null_texture()
534    }
535
536    pub fn new_with_format(cx: &mut Cx, format: TextureFormat) -> Self {
537        let texture = cx.textures.alloc(format);
538        texture
539    }
540    
541    pub fn set_animation(&self, cx: &mut Cx, animation: Option<TextureAnimation>) {
542        cx.textures[self.texture_id()].animation = animation;
543    }
544        
545    pub fn animation<'a>(&self,cx: &'a mut Cx) -> &'a Option<TextureAnimation> {
546        &cx.textures[self.texture_id()].animation
547    }
548        
549    pub fn get_format<'a>(&self, cx: &'a mut Cx) -> &'a mut TextureFormat {
550        &mut cx.textures[self.texture_id()].format
551    }
552
553    pub fn take_vec_u32(&self, cx: &mut Cx) -> Vec<u32> {
554        let cx_texture = &mut cx.textures[self.texture_id()];
555        let data = match &mut cx_texture.format {
556            TextureFormat::VecBGRAu8_32 { data, .. } => data,
557            _ => panic!("incorrect texture format for u32 image data"),
558        };
559        data.take().expect("image data already taken")
560    }
561    
562    pub fn swap_vec_u32(&self, cx: &mut Cx, vec: &mut Vec<u32>) {
563        let cx_texture = &mut cx.textures[self.texture_id()];
564        let (data, updated) = match &mut cx_texture.format {
565            TextureFormat::VecBGRAu8_32 { data, updated, .. } => (data, updated),
566            _ => panic!("incorrect texture format for u32 image data"),
567        };
568        if data.is_none(){
569            *data = Some(vec![]);
570        }
571        if let Some(data) = data{
572            std::mem::swap(data, vec)
573        }
574        *updated = updated.update(None);
575    }
576
577    pub fn put_back_vec_u32(&self, cx: &mut Cx, new_data: Vec<u32>, dirty_rect: Option<RectUsize>) {
578        let cx_texture = &mut cx.textures[self.texture_id()];
579        let (data, updated) = match &mut cx_texture.format {
580            TextureFormat::VecBGRAu8_32 { data, updated, .. } => (data, updated),
581            _ => panic!("incorrect texture format for u32 image data"),
582        };
583        //assert!(data.is_none(), "image data not taken or already put back");
584        *data = Some(new_data);
585        *updated = updated.update(dirty_rect);
586    }
587
588    pub fn take_vec_u8(&self, cx: &mut Cx) -> Vec<u8> {
589        let cx_texture = &mut cx.textures[self.texture_id()];
590        let data = match &mut cx_texture.format {
591            TextureFormat::VecRu8 { data, .. } => data,
592            TextureFormat::VecRGu8 { data, .. } => data,
593            _ => panic!("incorrect texture format for u32 image data"),
594        };
595        data.take().expect("image data already taken")
596    }
597
598    pub fn put_back_vec_u8(&self, cx: &mut Cx, new_data: Vec<u8>, dirty_rect: Option<RectUsize>) {
599        let cx_texture = &mut cx.textures[self.texture_id()];
600        let (data, updated) = match &mut cx_texture.format {
601            TextureFormat::VecRu8 { data, updated, .. } => (data, updated),
602            TextureFormat::VecRGu8 { data,updated, .. } => (data, updated),
603            _ => panic!("incorrect texture format for u8 image data"),
604        };
605        assert!(data.is_none(), "image data not taken or already put back");
606        *data = Some(new_data);
607        *updated = updated.update(dirty_rect);
608    }
609            
610    pub fn take_vec_f32(&self, cx: &mut Cx) -> Vec<f32> {
611        let cx_texture = &mut cx.textures[self.texture_id()];
612        let data = match &mut cx_texture.format{
613            TextureFormat::VecRf32 { data, .. } => data,
614            TextureFormat::VecRGBAf32{data, .. } => data,
615            _ => panic!("Not the correct texture desc for f32 image data"),
616        };
617        data.take().expect("image data already taken")
618    }
619
620    pub fn put_back_vec_f32(&self, cx: &mut Cx, new_data: Vec<f32>, dirty_rect: Option<RectUsize>) {
621        let cx_texture = &mut cx.textures[self.texture_id()];
622        let (data, updated) = match &mut cx_texture.format {
623            TextureFormat::VecRf32 { data, updated, .. } => (data, updated),
624            TextureFormat::VecRGBAf32 { data, updated, .. } => (data, updated),
625            _ => panic!("incorrect texture format for f32 image data"),
626        };
627        assert!(data.is_none(), "image data not taken or already put back");
628        *data = Some(new_data);
629        *updated = updated.update(dirty_rect);
630    }
631}
632
633#[derive(Default)]
634pub struct CxTexture {
635    pub (crate) format: TextureFormat,
636    pub (crate) alloc: Option<TextureAlloc>,
637    pub (crate) animation: Option<TextureAnimation>,
638    pub os: CxOsTexture,
639    pub previous_platform_resource: Option<CxOsTexture>,
640}