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 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 is_video == item.item.format.is_video()
51 }, cx_texture);
52
53 if let Some(previous_item) = previous_item {
54 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 *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}