ursus_core/assets/registry/
texture.rs1use crate::assets::upload::GpuUploadRequest;
2use crate::assets::upload_queue::UploadQueue;
3use crate::render::gfx::types::Format;
4use std::collections::HashMap;
5use std::hash::{Hash, Hasher};
6
7struct TextureHandleAllocator {
9 next: u32,
10}
11
12impl TextureHandleAllocator {
13 fn alloc(&mut self) -> TextureHandle {
14 let h = TextureHandle(self.next);
15 self.next += 1;
16 h
17 }
18}
19impl Default for TextureHandleAllocator {
20 fn default() -> Self {
21 Self { next: 1 } }
23}
24
25#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
26pub struct TextureHandle(pub u32);
27
28#[derive(Default)]
30pub struct TextureRegistry {
31 handles: TextureHandleAllocator,
32 dedup: TextureDedup,
33 upload_queue: UploadQueue,
34}
35
36impl TextureRegistry {
37 pub fn upload_rgba8(
38 &mut self,
39 pixels: Vec<u8>,
40 width: u32,
41 height: u32,
42 format: Format,
43 name: impl Into<String>,
44 ) -> TextureHandle {
45 match self.dedup.register(&pixels, width, height, format, &mut self.handles) {
46 TextureRegistration::Existing(handle) => handle,
47 TextureRegistration::New(handle) => {
48 self.upload_queue.push(GpuUploadRequest::Texture {
49 handle,
50 pixels,
51 width,
52 height,
53 format,
54 name: name.into(),
55 });
56 handle
57 }
58 }
59 }
60
61 pub(crate) fn flush_uploads(&mut self, tx: &std::sync::mpsc::Sender<GpuUploadRequest>) {
62 self.upload_queue.drain_to(tx);
63 }
64}
65
66const TEXTURE_HASH_SAMPLE_COUNT: usize = 64;
67
68#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
69struct TextureContentKey(u64, usize, u32, u32, Format);
70
71fn hash_texture(pixels: &[u8], width: u32, height: u32, format: Format) -> TextureContentKey {
72 let mut hasher = std::collections::hash_map::DefaultHasher::new();
73 let len = pixels.len();
74 if len <= TEXTURE_HASH_SAMPLE_COUNT * 2 {
75 pixels.hash(&mut hasher);
76 } else {
77 let step = len / TEXTURE_HASH_SAMPLE_COUNT;
78 let mut i = 0;
79 while i < len {
80 hasher.write_u8(pixels[i]);
81 i += step;
82 }
83 hasher.write(&pixels[..32.min(len)]);
84 hasher.write(&pixels[len - 32.min(len)..]);
85 }
86 TextureContentKey(hasher.finish(), len, width, height, format)
87}
88
89pub(crate) enum TextureRegistration {
90 Existing(TextureHandle),
91 New(TextureHandle),
92}
93
94#[derive(Default)]
96pub(crate) struct TextureDedup {
97 dedup: HashMap<TextureContentKey, TextureHandle>,
98}
99
100impl TextureDedup {
101 fn register(
102 &mut self,
103 pixels: &[u8],
104 width: u32,
105 height: u32,
106 format: Format,
107 handles: &mut TextureHandleAllocator,
108 ) -> TextureRegistration {
109 let key = hash_texture(pixels, width, height, format);
110 if let Some(&handle) = self.dedup.get(&key) {
111 return TextureRegistration::Existing(handle);
112 }
113 let handle = handles.alloc();
114 self.dedup.insert(key, handle);
115 TextureRegistration::New(handle)
116 }
117}