Skip to main content

fyrox_impl/renderer/cache/
uniform.rs

1// Copyright (c) 2019-present Dmitry Stepanov and Fyrox Engine contributors.
2//
3// Permission is hereby granted, free of charge, to any person obtaining a copy
4// of this software and associated documentation files (the "Software"), to deal
5// in the Software without restriction, including without limitation the rights
6// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7// copies of the Software, and to permit persons to whom the Software is
8// furnished to do so, subject to the following conditions:
9//
10// The above copyright notice and this permission notice shall be included in all
11// copies or substantial portions of the Software.
12//
13// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19// SOFTWARE.
20
21//! Uniform buffer cache could be considered as pool of uniform buffers of fixed size. See
22//! [`UniformBufferCache`] for more info.
23
24use crate::graphics::{
25    buffer::{BufferKind, BufferUsage, GpuBuffer, GpuBufferDescriptor},
26    error::FrameworkError,
27    framebuffer::{BufferDataUsage, ResourceBinding},
28    server::{GraphicsServer, SharedGraphicsServer},
29    uniform::{ByteStorage, DynamicUniformBuffer, UniformBuffer},
30};
31use fxhash::FxHashMap;
32use std::cell::RefCell;
33
34#[derive(Default)]
35struct UniformBufferSet {
36    buffers: Vec<GpuBuffer>,
37    free: usize,
38}
39
40impl UniformBufferSet {
41    fn mark_unused(&mut self) {
42        self.free = 0;
43    }
44
45    fn get_or_create(
46        &mut self,
47        size: usize,
48        server: &dyn GraphicsServer,
49    ) -> Result<GpuBuffer, FrameworkError> {
50        if self.free < self.buffers.len() {
51            let buffer = &self.buffers[self.free];
52            self.free += 1;
53            Ok(buffer.clone())
54        } else {
55            let buffer = server.create_buffer(GpuBufferDescriptor {
56                name: &format!("UniformBuffer{}", self.buffers.len()),
57                size,
58                kind: BufferKind::Uniform,
59                usage: BufferUsage::StreamCopy,
60            })?;
61            self.buffers.push(buffer);
62            self.free = self.buffers.len();
63            Ok(self.buffers.last().unwrap().clone())
64        }
65    }
66}
67
68/// Uniform buffer cache could be considered as pool of uniform buffers of fixed size, that can be
69/// used to fetch free buffer for drawing. Uniform buffers usually have quite limited size
70/// (guaranteed to be at least 16kb and on vast majority of GPUs the upper limit is 65kb) and they
71/// are intended to be used as a storage for relatively small set of data that can fit into L1 cache
72/// of a GPU for very fast access.
73pub struct UniformBufferCache {
74    server: SharedGraphicsServer,
75    cache: RefCell<FxHashMap<usize, UniformBufferSet>>,
76}
77
78impl UniformBufferCache {
79    pub fn new(server: SharedGraphicsServer) -> Self {
80        Self {
81            server,
82            cache: Default::default(),
83        }
84    }
85
86    /// Reserves one of the existing uniform buffers of the given size. If there's no such free buffer,
87    /// this method creates a new one and reserves it for further use.
88    pub fn get_or_create(&self, size: usize) -> Result<GpuBuffer, FrameworkError> {
89        let mut cache = self.cache.borrow_mut();
90        let set = cache.entry(size).or_default();
91        set.get_or_create(size, &*self.server)
92    }
93
94    /// Fetches a suitable (or creates new one) GPU uniform buffer for the given CPU uniform buffer
95    /// and writes the data to it, returns a reference to the buffer.
96    pub fn write<T>(&self, uniform_buffer: UniformBuffer<T>) -> Result<GpuBuffer, FrameworkError>
97    where
98        T: ByteStorage,
99    {
100        let data = uniform_buffer.finish();
101        let buffer = self.get_or_create(data.bytes_count())?;
102        buffer.write_data(data.bytes())?;
103        Ok(buffer)
104    }
105
106    /// Marks all reserved buffers as unused. Must be called at least once per frame to prevent
107    /// uncontrollable growth of the cache.
108    pub fn mark_all_unused(&mut self) {
109        for set in self.cache.borrow_mut().values_mut() {
110            set.mark_unused();
111        }
112    }
113
114    /// Returns the total amount of allocated uniforms buffers.
115    pub fn alive_count(&self) -> usize {
116        let mut count = 0;
117        for (_, set) in self.cache.borrow().iter() {
118            count += set.buffers.len();
119        }
120        count
121    }
122}
123
124struct Page {
125    dynamic: DynamicUniformBuffer,
126    is_submitted: bool,
127}
128
129/// The position of a uniform within a [`UniformMemoryAllocator`].
130#[derive(Clone, Copy, Debug)]
131pub struct UniformBlockLocation {
132    /// Index of the buffer in the buffer list of [`UniformMemoryAllocator`].
133    pub page: usize,
134    /// The position of the uniform within the buffer.
135    pub offset: usize,
136    /// The size of the uniform.
137    pub size: usize,
138}
139
140pub struct UniformMemoryAllocator {
141    gpu_buffers: Vec<GpuBuffer>,
142    block_alignment: usize,
143    max_uniform_buffer_size: usize,
144    pages: Vec<Page>,
145    blocks: Vec<UniformBlockLocation>,
146}
147
148impl UniformMemoryAllocator {
149    pub fn new(max_uniform_buffer_size: usize, block_alignment: usize) -> Self {
150        Self {
151            gpu_buffers: Default::default(),
152            block_alignment,
153            max_uniform_buffer_size,
154            pages: Default::default(),
155            blocks: Default::default(),
156        }
157    }
158
159    pub fn clear(&mut self) {
160        for page in self.pages.iter_mut() {
161            page.dynamic.clear();
162            page.is_submitted = false;
163        }
164        self.blocks.clear();
165    }
166
167    pub fn allocate<T>(&mut self, buffer: UniformBuffer<T>) -> UniformBlockLocation
168    where
169        T: ByteStorage,
170    {
171        let data = buffer.finish();
172        assert!(data.bytes_count() > 0);
173        assert!(data.bytes_count() < self.max_uniform_buffer_size);
174
175        let page_index = match self.pages.iter().position(|page| {
176            let write_position = page
177                .dynamic
178                .next_write_aligned_position(self.block_alignment);
179            self.max_uniform_buffer_size - write_position >= data.bytes_count()
180        }) {
181            Some(page_index) => page_index,
182            None => {
183                let page_index = self.pages.len();
184                self.pages.push(Page {
185                    dynamic: UniformBuffer::with_storage(Vec::with_capacity(
186                        self.max_uniform_buffer_size,
187                    )),
188                    is_submitted: false,
189                });
190                page_index
191            }
192        };
193
194        let page = &mut self.pages[page_index];
195        page.is_submitted = false;
196        let offset = page
197            .dynamic
198            .write_bytes_with_alignment(data.bytes(), self.block_alignment);
199
200        let block = UniformBlockLocation {
201            page: page_index,
202            offset,
203            size: data.bytes_count(),
204        };
205        self.blocks.push(block);
206        block
207    }
208
209    pub fn upload(&mut self, server: &dyn GraphicsServer) -> Result<(), FrameworkError> {
210        if self.gpu_buffers.len() < self.pages.len() {
211            for _ in 0..(self.pages.len() - self.gpu_buffers.len()) {
212                let buffer = server.create_buffer(GpuBufferDescriptor {
213                    name: &format!("UniformMemoryPage{}", self.gpu_buffers.len()),
214                    size: self.max_uniform_buffer_size,
215                    kind: BufferKind::Uniform,
216                    usage: BufferUsage::StreamCopy,
217                })?;
218                self.gpu_buffers.push(buffer);
219            }
220        }
221
222        for (page, gpu_buffer) in self.pages.iter_mut().zip(self.gpu_buffers.iter()) {
223            if !page.is_submitted {
224                let bytes = page.dynamic.storage().bytes();
225                assert!(bytes.len() <= self.max_uniform_buffer_size);
226                gpu_buffer.write_data(bytes)?;
227                page.is_submitted = true;
228            }
229        }
230
231        Ok(())
232    }
233
234    pub fn block_to_binding(
235        &self,
236        block: UniformBlockLocation,
237        binding_point: usize,
238    ) -> ResourceBinding {
239        ResourceBinding::Buffer {
240            buffer: self.gpu_buffers[block.page].clone(),
241            binding: binding_point,
242            data_usage: BufferDataUsage::UseSegment {
243                offset: block.offset,
244                size: block.size,
245            },
246        }
247    }
248}