ursus_core/assets/storage/
material.rs1use crate::render::gfx::descriptor::{DescriptorAllocator, DescriptorSetDesc};
2use crate::render::gfx::types::{DescriptorSetId, ShaderStage};
3use crate::vulkan::resources::growable_buffer::GrowableBuffer;
4use ash::vk;
5use std::collections::HashMap;
6use ursus_materials::MaterialHandle;
7
8pub struct MaterialStore {
21 buffers: HashMap<usize, GrowableBuffer>,
22 bucket_bytes: HashMap<usize, Vec<u8>>,
23 index_of: HashMap<MaterialHandle, (usize, usize)>, descriptor_set: DescriptorSetId,
26 bound_stride: Option<usize>,
27
28 device: ash::Device,
29 physical_device: vk::PhysicalDevice,
30 instance: ash::Instance,
31}
32
33impl MaterialStore {
34 pub fn new(
35 device: ash::Device,
36 physical_device: vk::PhysicalDevice,
37 instance: ash::Instance,
38 descriptors: &mut DescriptorAllocator,
39 ) -> anyhow::Result<Self> {
40 let desc = DescriptorSetDesc::new().with_storage_buffer::<()>(0, ShaderStage::Fragment);
41 let descriptor_set = descriptors.create_set(desc)?;
42
43 Ok(Self {
44 buffers: HashMap::new(),
45 bucket_bytes: HashMap::new(),
46 index_of: HashMap::new(),
47 descriptor_set,
48 bound_stride: None,
49 device,
50 physical_device,
51 instance,
52 })
53 }
54
55 pub fn descriptor_set(&self) -> DescriptorSetId {
56 self.descriptor_set
57 }
58
59 pub fn upload(
65 &mut self,
66 handle: MaterialHandle,
67 bytes: &[u8],
68 descriptors: &DescriptorAllocator,
69 ) -> anyhow::Result<()> {
70 let stride = bytes.len();
71 let bucket = self.bucket_bytes.entry(stride).or_default();
72
73 let index = match self.index_of.get(&handle) {
74 Some(&(existing_stride, existing_index)) if existing_stride == stride => existing_index,
75 _ => {
76 let index = bucket.len() / stride.max(1);
77 bucket.resize(bucket.len() + stride, 0);
78 self.index_of.insert(handle, (stride, index));
79 index
80 }
81 };
82
83 let start = index * stride;
84 bucket[start..start + stride].copy_from_slice(bytes);
85
86 let buffer = match self.buffers.get_mut(&stride) {
87 Some(b) => b,
88 None => {
89 let new_buffer = GrowableBuffer::new(
90 &self.device,
91 self.physical_device,
92 &self.instance,
93 vk::BufferUsageFlags::STORAGE_BUFFER,
94 stride.max(64),
95 )?;
96 self.buffers.entry(stride).or_insert(new_buffer)
97 }
98 };
99
100 let reallocated = buffer.upload(bucket)?;
101
102 if self.bound_stride != Some(stride) || reallocated {
103 self.bind_descriptor(stride, descriptors)?;
104 }
105
106 Ok(())
107 }
108
109 fn bind_descriptor(&mut self, stride: usize, descriptors: &DescriptorAllocator) -> anyhow::Result<()> {
110 if self.bound_stride.is_some() && self.bound_stride != Some(stride) {
111 log::warn!(
112 "MaterialStore: binding stride {stride} over previously bound stride {:?} - \
113 only one stride can be visible to shaders via the current single MaterialBuffer binding",
114 self.bound_stride
115 );
116 }
117
118 let buffer = self.buffers.get(&stride).expect("stride buffer must exist before binding");
119 descriptors.bind_storage_buffer(
120 self.descriptor_set,
121 0,
122 buffer.buffer(),
123 buffer.capacity() as vk::DeviceSize,
124 )?;
125 self.bound_stride = Some(stride);
126 Ok(())
127 }
128
129 pub fn index_of(&self, handle: MaterialHandle) -> Option<(usize, usize)> {
130 self.index_of.get(&handle).copied()
131 }
132
133 pub fn buffer_for_stride(&self, stride: usize) -> Option<vk::Buffer> {
134 self.buffers.get(&stride).map(|b| b.buffer())
135 }
136
137 pub fn strides(&self) -> impl Iterator<Item = usize> + '_ {
138 self.buffers.keys().copied()
139 }
140}