use crate::renderer::framework::{
buffer::{Buffer, BufferKind, BufferUsage},
error::FrameworkError,
framebuffer::{BufferDataUsage, ResourceBinding},
server::GraphicsServer,
uniform::{ByteStorage, DynamicUniformBuffer, UniformBuffer},
};
use fxhash::FxHashMap;
use fyrox_graphics::framebuffer::BufferLocation;
use fyrox_graphics::server::SharedGraphicsServer;
use std::cell::RefCell;
#[derive(Default)]
struct UniformBufferSet {
buffers: Vec<Box<dyn Buffer>>,
free: usize,
}
impl UniformBufferSet {
fn mark_unused(&mut self) {
self.free = 0;
}
fn get_or_create(
&mut self,
size: usize,
server: &dyn GraphicsServer,
) -> Result<&dyn Buffer, FrameworkError> {
if self.free < self.buffers.len() {
let buffer = &self.buffers[self.free];
self.free += 1;
Ok(&**buffer)
} else {
let buffer =
server.create_buffer(size, BufferKind::Uniform, BufferUsage::StreamCopy)?;
self.buffers.push(buffer);
self.free = self.buffers.len();
Ok(&**self.buffers.last().unwrap())
}
}
}
pub struct UniformBufferCache {
server: SharedGraphicsServer,
cache: RefCell<FxHashMap<usize, UniformBufferSet>>,
}
impl UniformBufferCache {
pub fn new(server: SharedGraphicsServer) -> Self {
Self {
server,
cache: Default::default(),
}
}
pub fn get_or_create<'a>(&'a self, size: usize) -> Result<&'a dyn Buffer, FrameworkError> {
let mut cache = self.cache.borrow_mut();
let set = cache.entry(size).or_default();
let buffer = set.get_or_create(size, &*self.server)?;
Ok(unsafe { std::mem::transmute::<&'_ dyn Buffer, &'a dyn Buffer>(buffer) })
}
pub fn write<T>(&self, uniform_buffer: UniformBuffer<T>) -> Result<&dyn Buffer, FrameworkError>
where
T: ByteStorage,
{
let data = uniform_buffer.finish();
let buffer = self.get_or_create(data.bytes_count())?;
buffer.write_data(data.bytes())?;
Ok(buffer)
}
pub fn mark_all_unused(&mut self) {
for set in self.cache.borrow_mut().values_mut() {
set.mark_unused();
}
}
pub fn alive_count(&self) -> usize {
let mut count = 0;
for (_, set) in self.cache.borrow().iter() {
count += set.buffers.len();
}
count
}
}
struct Page {
dynamic: DynamicUniformBuffer,
is_submitted: bool,
}
#[derive(Clone, Copy, Debug)]
pub struct UniformBlockLocation {
pub page: usize,
pub offset: usize,
pub size: usize,
}
pub struct UniformMemoryAllocator {
gpu_buffers: Vec<Box<dyn Buffer>>,
block_alignment: usize,
max_uniform_buffer_size: usize,
pages: Vec<Page>,
blocks: Vec<UniformBlockLocation>,
}
impl UniformMemoryAllocator {
pub fn new(max_uniform_buffer_size: usize, block_alignment: usize) -> Self {
Self {
gpu_buffers: Default::default(),
block_alignment,
max_uniform_buffer_size,
pages: Default::default(),
blocks: Default::default(),
}
}
pub fn clear(&mut self) {
for page in self.pages.iter_mut() {
page.dynamic.clear();
page.is_submitted = false;
}
self.blocks.clear();
}
pub fn allocate<T>(&mut self, buffer: UniformBuffer<T>) -> UniformBlockLocation
where
T: ByteStorage,
{
let data = buffer.finish();
assert!(data.bytes_count() > 0);
assert!(data.bytes_count() < self.max_uniform_buffer_size);
let page_index = match self.pages.iter().position(|page| {
let write_position = page
.dynamic
.next_write_aligned_position(self.block_alignment);
self.max_uniform_buffer_size - write_position >= data.bytes_count()
}) {
Some(page_index) => page_index,
None => {
let page_index = self.pages.len();
self.pages.push(Page {
dynamic: UniformBuffer::with_storage(Vec::with_capacity(
self.max_uniform_buffer_size,
)),
is_submitted: false,
});
page_index
}
};
let page = &mut self.pages[page_index];
page.is_submitted = false;
let offset = page
.dynamic
.write_bytes_with_alignment(data.bytes(), self.block_alignment);
let block = UniformBlockLocation {
page: page_index,
offset,
size: data.bytes_count(),
};
self.blocks.push(block);
block
}
pub fn upload(&mut self, server: &dyn GraphicsServer) -> Result<(), FrameworkError> {
if self.gpu_buffers.len() < self.pages.len() {
for _ in 0..(self.pages.len() - self.gpu_buffers.len()) {
let buffer = server.create_buffer(
self.max_uniform_buffer_size,
BufferKind::Uniform,
BufferUsage::StreamCopy,
)?;
self.gpu_buffers.push(buffer);
}
}
for (page, gpu_buffer) in self.pages.iter_mut().zip(self.gpu_buffers.iter()) {
if !page.is_submitted {
let bytes = page.dynamic.storage().bytes();
assert!(bytes.len() <= self.max_uniform_buffer_size);
gpu_buffer.write_data(bytes)?;
page.is_submitted = true;
}
}
Ok(())
}
pub fn block_to_binding(
&self,
block: UniformBlockLocation,
binding_point: usize,
) -> ResourceBinding {
ResourceBinding::Buffer {
buffer: &*self.gpu_buffers[block.page],
binding: BufferLocation::Explicit {
binding: binding_point,
},
data_usage: BufferDataUsage::UseSegment {
offset: block.offset,
size: block.size,
},
}
}
}