diff --git a/src/mesh.rs b/src/mesh.rs new file mode 100644 index 0000000..3f6b415 --- /dev/null +++ b/src/mesh.rs @@ -0,0 +1,391 @@ +use crate::staging::*; +use slab::Slab; +use smallvec::SmallVec; +use std::any::TypeId; +use std::collections::HashMap; +use std::sync::Arc; + +/// An error that can be returned when allocating a mesh. +pub enum PoolError { + TooBig, + NoMoreRoom, + InvalidIndex, + AttrTaken, + AttrUnregistered, + MismatchedId, + MismatchedLayout, +} + +/// An identifier for a mesh attribute. +#[repr(transparent)] +#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] +pub struct AttrId(usize); + +/// A description of an attribute's layout in memory. +#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] +pub struct AttrLayout { + /// The size (in bytes) of this attribute. + pub size: usize, +} + +/// Information about an [Attribute] registered in [AttrStore]. +#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] +pub struct AttrInfo { + pub layout: AttrLayout, + pub default_pool_size: usize, +} + +/// The data single mesh attribute. +pub struct AttrBuffer { + pub id: AttrId, + pub count: usize, + pub data: Vec, +} + +/// A compile-time attribute data type. +pub trait Attribute: Sized { + /// The memory layout of this data. + fn get_layout() -> AttrLayout { + AttrLayout { + size: std::mem::size_of::(), + } + } + + /// The default size for new pools of this attribute. + /// + /// Defaults to 1024 * 1024. (Around one million.) + fn get_default_pool_size() -> usize { + 1024 * 1024 + } +} + +/// A store of [AttrIds][AttrId]. +pub struct AttrStore { + attributes: Slab, + types: HashMap, +} + +impl AttrStore { + pub fn new() -> Arc { + Arc::new(Self { + attributes: Default::default(), + types: Default::default(), + }) + } + + /// Dynamically creates a new [AttrId]. + pub fn add(&mut self, info: AttrInfo) -> AttrId { + let index = self.attributes.insert(info); + AttrId(index) + } + + /// Gets the [AttrId] for a type implementing [Attribute]. + /// + /// Creates a new [AttrId] for unrecognized types, otherwise reuses an + /// existing [AttrId]. + pub fn get_type(&mut self) -> AttrId { + let type_id = TypeId::of::(); + if let Some(id) = self.types.get(&type_id) { + *id + } else { + let layout = T::get_layout(); + let default_pool_size = T::get_default_pool_size(); + let info = AttrInfo { + layout, + default_pool_size, + }; + let id = self.add(info); + self.types.insert(type_id, id); + id + } + } + + /// Gets the [AttrInfo] for an [AttrId]. + pub fn get_info(&self, id: &AttrId) -> Option<&AttrInfo> { + self.attributes.get(id.0) + } +} + +/// Attribute pool allocation location. +#[derive(Clone)] +pub struct AttrAllocKey { + /// The target attribute. + pub attr: AttrId, + + /// The index of the attribute pool. + pub pool: usize, + + /// The allocation within the attribute pool. + pub alloc: usize, +} + +/// Info about an array of attributes that has been allocated in an [AttrPool]. +#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] +pub struct AttrAlloc { + offset: usize, + count: usize, + offset_bytes: usize, + count_bytes: usize, +} + +/// An unused space range in an [AttrPool]. +pub struct FreeSpace { + offset: usize, + count: usize, +} + +/// A single GPU buffer containing linear arrays of attributes. +pub struct AttrPool { + id: AttrId, + pool_id: usize, + layout: AttrLayout, + size: usize, + allocs: Slab, + free_space: Vec, +} + +impl AttrPool { + pub fn new(id: AttrId, pool_id: usize, info: AttrInfo) -> Self { + let layout = info.layout; + let size = info.default_pool_size; + + Self { + id, + pool_id, + layout, + size, + free_space: vec![FreeSpace { + offset: 0, + count: size, + }], + allocs: Default::default(), + } + } + + /// Loads an [AttrBuffer]. + /// + /// Returns the new [AttrAllocKey]. + pub fn load(&mut self, buf: &AttrBuffer) -> Result { + let best_index = self.can_load(buf)?; + self.alloc_at(best_index, buf.count) + } + + /// Allocates an array of attributes. + /// + /// Returns the new [AttrAllocKey]. + pub fn alloc(&mut self, count: usize) -> Result { + let best_index = self.can_alloc(count)?; + self.alloc_at(best_index, count) + } + + /// Tests if an [AttrBuffer] can be loaded. + /// + /// Returns the result of [Self::best_fit]. + pub fn can_load(&self, buf: &AttrBuffer) -> Result { + if buf.id != self.id { + Err(PoolError::MismatchedId) + } else if buf.count * self.layout.size != buf.data.len() { + Err(PoolError::MismatchedLayout) + } else { + self.can_alloc(buf.count) + } + } + + /// Tests if an array of attributes can be allocated. + /// + /// Returns the result of [Self::best_fit]. + pub fn can_alloc(&self, count: usize) -> Result { + if count > self.size { + Err(PoolError::TooBig) + } else if let Some(best_index) = self.best_fit(count) { + Ok(best_index) + } else { + Err(PoolError::NoMoreRoom) + } + } + + /// Finds the index of the best-fit free space for an array of attributes. + /// + /// TODO: use a binary tree to find best-fit free space in logarithmic time + fn best_fit(&self, count: usize) -> Option { + let mut best_index = None; + let mut best_count = usize::MAX; + for (index, space) in self.free_space.iter().enumerate() { + if space.count >= count && space.count < best_count { + best_index = Some(index); + best_count = space.count; + } + } + + best_index + } + + /// Allocates an array of attribute at a specific free space index. + /// + /// Returns the new [AttrAllocKey]. + fn alloc_at( + &mut self, + index: usize, + count: usize, + ) -> Result { + let free_space = match self.free_space.get_mut(index) { + Some(index) => index, + None => return Err(PoolError::InvalidIndex), + }; + + let offset = free_space.offset; + + let alloc = AttrAlloc { + offset, + count, + offset_bytes: offset * self.layout.size, + count_bytes: count * self.layout.size, + }; + + let index = self.allocs.insert(alloc); + + let key = AttrAllocKey { + attr: self.id, + pool: self.pool_id, + alloc: index, + }; + + use std::cmp::Ordering; + match free_space.count.cmp(&count) { + Ordering::Less => { + return Err(PoolError::TooBig); + } + Ordering::Equal => { + self.free_space.remove(index); + } + Ordering::Greater => { + free_space.count -= count; + free_space.offset += count; + } + } + + Ok(key) + } + + /// Frees an allocation (by key) from the pool. + pub fn free(&mut self, key: usize) -> Result<(), PoolError> { + let alloc = self.allocs.try_remove(key).ok_or(PoolError::InvalidIndex)?; + + let free_space = FreeSpace { + offset: alloc.offset, + count: alloc.count, + }; + + // TODO merge free spaces + self.free_space.push(free_space); + + Ok(()) + } +} + +/// The number of attributes a mesh can have before they're moved to the heap. +pub const MAX_MESH_INLINE_ATTRIBUTES: usize = 16; + +/// A mesh and all of its attributes. +pub struct MeshBuffer { + pub attributes: SmallVec<[AttrBuffer; MAX_MESH_INLINE_ATTRIBUTES]>, +} + +/// An allocated mesh. +pub struct MeshAlloc { + pub attributes: SmallVec<[AttrAllocKey; MAX_MESH_INLINE_ATTRIBUTES]>, +} + +/// A handle to an allocated mesh. +#[repr(transparent)] +pub struct MeshHandle(usize); + +impl HasMeshHandle for MeshHandle { + fn get_mesh_handle(&self) -> &Self { + self + } +} + +/// A trait for structs containing [MeshHandles][MeshHandle] that are not +/// themselves handles. Used for iteration. +pub trait HasMeshHandle { + fn get_mesh_handle(&self) -> &MeshHandle; +} + +/// A mesh data pool. +pub struct MeshPool { + device: Arc, + staging: StagingPool, + attr_store: Arc, + allocs: Slab, + pools: HashMap>, +} + +impl MeshPool { + pub fn new(device: Arc, attr_store: Arc) -> Arc { + Arc::new(Self { + device: device.clone(), + staging: StagingPool::new(device, 1024 * 1024), + attr_store, + allocs: Default::default(), + pools: Default::default(), + }) + } + + /// Loads a [MeshBuffer]. + pub fn load(&mut self, buf: MeshBuffer) -> Result { + let mut attrs = HashMap::new(); + + for attr in buf.attributes.into_iter() { + if let Some(_) = attrs.insert(attr.id, attr) { + // TODO: support for loading duplicate attribute IDs + return Err(PoolError::AttrTaken); + } + } + + let mut attr_allocs = SmallVec::new(); + let mut copies = Vec::new(); + for (id, buf) in attrs.drain() { + let pools = match self.pools.get_mut(&id) { + Some(pools) => pools, + None => { + let info = match self.attr_store.get_info(&id) { + Some(info) => info, + None => return Err(PoolError::AttrUnregistered), + }; + + let pool = AttrPool::new(id, 0, *info); + self.pools.insert(id, vec![pool]); + self.pools.get_mut(&id).unwrap() + } + }; + + for pool in pools.iter_mut() { + match pool.load(&buf) { + Ok(alloc) => { + let copy = CopyBuffer { + target: alloc.clone(), + offset: 0, + data: buf.data, + }; + + attr_allocs.push(alloc); + copies.push(copy); + break; + } + Err(PoolError::NoMoreRoom) => {} + Err(e) => return Err(e), + } + } + } + + self.staging.queue_copies(copies); + + let alloc = MeshAlloc { + attributes: attr_allocs, + }; + let key = self.allocs.insert(alloc); + let handle = MeshHandle(key); + Ok(handle) + } +} diff --git a/src/mesh/attr.rs b/src/mesh/attr.rs deleted file mode 100644 index f2b9b95..0000000 --- a/src/mesh/attr.rs +++ /dev/null @@ -1,243 +0,0 @@ -//! Mesh storage pooling for a single attribute. -//! -//! Attribute pools have a fixed size, and once created cannot be expanded to -//! fit more data. - -use super::*; -use std::any::TypeId; -use std::collections::HashMap; - -/// An externally-defined identifier for a mesh attribute. -#[repr(transparent)] -#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] -pub struct AttrId(pub usize); - -/// A description of a attribute's layout in memory. -#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] -pub struct AttrLayout { - /// The size (in bytes) of this attribute. - pub size: usize, -} - -/// Information about an [Attribute] registered in [AttrStore]. -#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] -pub struct AttrInfo { - pub layout: AttrLayout, - pub default_pool_size: usize, -} - -/// A compile-time attribute data type. -pub trait Attribute: Sized { - /// The memory layout of this data. - fn get_layout() -> AttrLayout { - AttrLayout { - size: std::mem::size_of::(), - } - } - - /// The default size for new pools of this attribute. - /// - /// Defaults to 1024 * 1024. (Around one million.) - fn get_default_pool_size() -> usize { - 1024 * 1024 - } -} - -/// A store of [AttrIds][AttrId]. -pub struct AttrStore { - attributes: Slab, - types: HashMap, -} - -impl AttrStore { - pub fn new() -> Self { - Self { - attributes: Default::default(), - types: Default::default(), - } - } - - /// Dynamically creates a new [AttrId]. - pub fn add(&mut self, info: AttrInfo) -> AttrId { - let index = self.attributes.insert(info); - AttrId(index) - } - - /// Gets the [AttrId] for a type implementing [Attribute]. - /// - /// Creates a new [AttrId] for unrecognized types, otherwise reuses an - /// existing [AttrId]. - pub fn get_type(&mut self) -> AttrId { - let type_id = TypeId::of::(); - if let Some(id) = self.types.get(&type_id) { - *id - } else { - let layout = T::get_layout(); - let default_pool_size = T::get_default_pool_size(); - let info = AttrInfo { - layout, - default_pool_size, - }; - let id = self.add(info); - self.types.insert(type_id, id); - id - } - } - - /// Gets the [AttrInfo] for an [AttrId]. - pub fn get_info(&self, id: &AttrId) -> Option<&AttrInfo> { - self.attributes.get(id.0) - } -} - -/// An attribute buffer that has been allocated in an [AttrPool]. -#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] -pub struct AttrAlloc { - offset: usize, - count: usize, -} - -/// An unused space range in an [AttrPool]. -pub struct FreeSpace { - offset: usize, - count: usize, -} - -/// A single GPU buffer containing linear arrays of attributes. -pub struct AttrPool { - group: usize, - id: AttrId, - layout: AttrLayout, - size: usize, - allocs: Slab, - free_space: Vec, -} - -impl AttrPool { - pub fn new(group: usize, id: AttrId, info: AttrInfo) -> Result { - let layout = info.layout; - let size = info.default_pool_size; - - Ok(Self { - group, - id, - layout, - size, - free_space: vec![FreeSpace { - offset: 0, - count: size, - }], - allocs: Default::default(), - }) - } - - /// Tests if attributes can be allocated. - /// - /// Returns the result of [Self::best_fit]. - pub fn can_alloc(&self, count: usize) -> Result { - if count > self.size { - Err(PoolError::TooBig) - } else if let Some(best_index) = self.best_fit(count) { - Ok(best_index) - } else { - Err(PoolError::NoMoreRoom) - } - } - - /// Tests if an [AttrBuffer] can be loaded. - /// - /// Returns the result of [Self::best_fit]. - pub fn can_load(&self, buf: &AttrBuffer) -> Result { - if buf.id != self.id { - Err(PoolError::MismatchedId) - } else if buf.layout != self.layout { - Err(PoolError::MismatchedLayout) - } else { - self.can_alloc(buf.count) - } - } - - /// Finds the index of the best-fit free space for an array of attributes. - /// - /// TODO: use a binary tree to find best-fit free space in logarithmic time - pub fn best_fit(&self, count: usize) -> Option { - let mut best_index = None; - let mut best_count = usize::MAX; - for (index, space) in self.free_space.iter().enumerate() { - if space.count >= count && space.count < best_count { - best_index = Some(index); - best_count = space.count; - } - } - - best_index - } - - /// Allocates room for attributes at a specific free space index. - /// - /// Returns the new [AttrAlloc] and its key. - pub fn alloc_at( - &mut self, - index: usize, - count: usize, - ) -> Result<(AttrAlloc, usize), PoolError> { - let free_space = match self.free_space.get_mut(index) { - Some(index) => index, - None => return Err(PoolError::InvalidIndex), - }; - - let alloc = AttrAlloc { - offset: free_space.offset, - count, - }; - - let key = self.allocs.insert(alloc); - - use std::cmp::Ordering; - match free_space.count.cmp(&count) { - Ordering::Less => { - return Err(PoolError::TooBig); - } - Ordering::Equal => { - self.free_space.remove(index); - } - Ordering::Greater => { - free_space.count -= count; - free_space.offset += count; - } - } - - Ok((alloc, key)) - } - - /// Allocates room for attributes. - /// - /// Returns the new [AttrAlloc] and its key. - pub fn alloc(&mut self, count: usize) -> Result<(AttrAlloc, usize), PoolError> { - let best_index = self.can_alloc(count)?; - self.alloc_at(best_index, count) - } - - /// Loads an [AttrBuffer]. - /// - /// Returns the key for the allocation, as well as [CopyInfo] that can be - /// queued into a [StagingPool]. - pub fn load(&mut self, buf: &AttrBuffer) -> Result<(usize, CopyInfo), PoolError> { - let best_index = self.can_load(buf)?; - let (alloc, key) = self.alloc_at(best_index, buf.count)?; - - let copy = CopyInfo { - group: self.group, - target: self.id, - offset: alloc.offset * self.layout.size, - size: alloc.count * self.layout.size, - }; - - Ok((key, copy)) - } - - /// Frees an allocation (by key) from the pool. - pub fn free(&mut self, alloc: usize) -> Result<(), PoolError> { - todo!() - } -} diff --git a/src/mesh/group.rs b/src/mesh/group.rs deleted file mode 100644 index c5c112c..0000000 --- a/src/mesh/group.rs +++ /dev/null @@ -1,92 +0,0 @@ -//! Fixed-room pooling of mesh data. - -use super::*; -use std::sync::Arc; - -/// A mesh that has been allocated in a [MeshGroup]. -pub struct MeshAlloc { - pub attributes: SmallVec<[(usize, AttrId); MAX_MESH_INLINE_ATTRIBUTES]>, -} - -/// A set of GPU-side vertex attribute pools and index pools. -pub struct MeshGroup { - id: usize, - attr_store: Arc, - pools: HashMap, - meshes: Slab, -} - -impl MeshGroup { - pub fn new(id: usize, attr_store: Arc) -> Self { - Self { - id, - attr_store, - pools: Default::default(), - meshes: Default::default(), - } - } - - /// Checks to see if a mesh can be loaded within this group. - pub fn can_load(&self, buf: &MeshBuffer) -> Result<(), PoolError> { - for attr in buf.attributes.iter() { - match self.pools.get(&attr.id) { - None => { - match self.attr_store.get_info(&attr.id) { - Some(_) => {} // new pools of valid attrs can be made - None => return Err(PoolError::AttrUnregistered), - }; - } - Some(pool) => { - pool.can_load(attr)?; - } - } - } - - Ok(()) - } - - /// Tries to load a [MeshBuffer] into this pool. Returns a [MeshHandle]. - pub fn load(&mut self, buf: &MeshBuffer) -> Result<(MeshHandle, Vec), PoolError> { - self.can_load(&buf)?; - - let mut allocs = SmallVec::with_capacity(buf.attributes.len()); - let mut copies = Vec::new(); - - for attr in buf.attributes.iter() { - let id = &attr.id; - let pool = match self.pools.get_mut(id) { - Some(pool) => pool, - None => { - let info = match self.attr_store.get_info(id) { - Some(info) => *info, - None => return Err(PoolError::AttrUnregistered), - }; - - let new_pool = AttrPool::new(self.id, *id, info)?; - self.pools.insert(*id, new_pool); - self.pools.get_mut(id).unwrap() - } - }; - - let (alloc, copy) = pool.load(attr)?; - allocs.push((alloc, attr.id)); - copies.push(copy); - } - - let mesh = MeshAlloc { attributes: allocs }; - let sub = self.meshes.insert(mesh); - let group = self.id; - let handle = MeshHandle { group, sub }; - - Ok((handle, copies)) - } - - /// Gets a [MeshHandle's][MeshHandle] [MeshAlloc]. - pub fn get_alloc(&self, handle: &MeshHandle) -> Option<&MeshAlloc> { - if handle.group != self.id { - None - } else { - self.meshes.get(handle.sub) - } - } -} diff --git a/src/mesh/mod.rs b/src/mesh/mod.rs deleted file mode 100644 index 1e14009..0000000 --- a/src/mesh/mod.rs +++ /dev/null @@ -1,178 +0,0 @@ -//! Dynamic mesh data storage. -//! -//! Meshes are based on ECS-like archetypes. Each pool contains a set of mesh -//! "attributes," which can be either vertex attributes, indices of different -//! formats (u8, u16, u32), or in the future, fixed-size mesh chunklets too. -//! The mesh pool itself is agnostic to specific rendering implementation. It -//! has no implicit knowledge of what a vertex position, normal, or texture -//! coordinate is, or even what an index is. -//! -//! Multiple attributes can have the same layout. For example, a rudimentary -//! mesh format might use three 32-bit floating point values (`[f32; 3]`) for -//! both vertex position and vertex normals. In this case, positions and normals -//! would have different [AttrIds][AttrId] to distuingish them, and must each be -//! registered to the pool. Once an attribute is registered in a pool instance, -//! it cannot be unregistered, although the mesh pool may free GPU buffers for -//! unused attribute pools. -//! -//! Meshes are pooled by [groups][MeshGroup], so all mesh data in a group -//! shares the same memory. This allows the rendering pipeline to operate on as -//! much mesh data simultaneously as possible without rebinding buffers, -//! enabling some highly-efficient rendering techniques like bindless forward -//! rendering, bindless vertex skinning, and mesh shading. -//! -//! However, because a mesh groups' underlying buffers are so large, they cannot -//! be resized without copying all of the mesh data within to a new allocation, -//! putting a lot of pressure on the GPU's memory bus and causing massive lag -//! spikes. Instead, an entirely new group must be created to store more mesh -//! data. In practice, new groups will not be created often, again due to the -//! large size of their underlying buffers. -//! -//! When a mesh is loaded, the pool is searched for a group that has spare room -//! for all of the mesh's attributes. If one is found, the pool copies the mesh's -//! attribute data into the pool's internal staging buffer, which is later -//! copied by the GPU into the corresponding attribute pools in the selected -//! group. If no group has enough free space to store all of the attributes, a -//! new group is created. -//! -//! Staging buffers are fixed-size, so when a large amount of mesh data is loaded -//! at once and the pool can't fit it all into an available staging buffer, the -//! memory is instead copied to a CPU-side spillover buffer, and GPU transfer is -//! deferred to a future staging pass. Because of this, meshes are not guaranteed -//! to be available for drawing on the frame that they are loaded. -//! -//! TODO: mesh coherency -//! TODO: make spillover buffers GPU-transferrable on iGPUs - -use slab::Slab; -use smallvec::SmallVec; -use std::collections::HashMap; -use std::sync::Arc; - -pub mod attr; -pub mod group; -pub mod staging; - -use attr::*; -use group::*; -use staging::*; - -/// An error that can be returned when allocating a mesh. -pub enum PoolError { - TooBig, - NoMoreRoom, - InvalidIndex, - AttrTaken, - AttrUnregistered, - MismatchedId, - MismatchedLayout, -} - -/// The number of attributes a mesh can have before they're moved to the heap. -pub const MAX_MESH_INLINE_ATTRIBUTES: usize = 16; - -/// The data and layout of a single mesh attribute. -pub struct AttrBuffer { - pub id: AttrId, - pub layout: AttrLayout, - pub count: usize, - pub data: Vec, -} - -/// A mesh and all of its attributes. -/// -/// An attribute ID can be used multiple times in a mesh, corresponding to -/// multiple allocations within an [AttrPool]. -pub struct MeshBuffer { - pub attributes: SmallVec<[AttrBuffer; MAX_MESH_INLINE_ATTRIBUTES]>, -} - -/// A handle to an allocated mesh. -pub struct MeshHandle { - pub(crate) group: usize, - pub(crate) sub: usize, -} - -impl HasMeshHandle for MeshHandle { - fn get_mesh_handle(&self) -> &Self { - self - } -} - -/// A trait for structs containing [MeshHandles][MeshHandle] that are not -/// themselves handles. Used for iteration. -pub trait HasMeshHandle { - fn get_mesh_handle(&self) -> &MeshHandle; -} - -/// The top-level mesh data pool. -pub struct MeshPool { - staging: StagingPool, - attr_store: Arc, - groups: Vec, -} - -impl MeshPool { - pub fn new(attr_store: Arc) -> Self { - Self { - staging: StagingPool::new(1_000_000), - attr_store, - groups: Default::default(), - } - } - - pub fn load(&mut self, buf: &MeshBuffer) -> Result { - for group in self.groups.iter_mut() { - match group.load(buf) { - Ok((handle, copies)) => { - self.staging.queue_copies(copies); - return Ok(handle); - } - Err(PoolError::NoMoreRoom) => {} - Err(e) => return Err(e), - } - } - - let group_index = self.groups.len(); - let attr_store = self.attr_store.clone(); - self.groups.push(MeshGroup::new(group_index, attr_store)); - let group = self.groups.get_mut(group_index).unwrap(); - - let (handle, copies) = group.load(buf)?; - self.staging.queue_copies(copies); - Ok(handle) - } - - pub fn iter_meshes( - &self, - meshes: impl IntoIterator, - ) -> Vec<(&MeshGroup, Vec<(&MeshAlloc, T)>)> { - let group_num = self.groups.len(); - let mut by_group = Vec::with_capacity(group_num); - for index in 0..group_num { - let group = self.groups.get(index); - let meshes = Vec::new(); - by_group.push((group, meshes)); - } - - for mesh in meshes { - let handle = mesh.get_mesh_handle(); - let (group, meshes) = match by_group.get_mut(handle.group) { - Some((Some(group), meshes)) => (group, meshes), - _ => continue, - }; - - let alloc = match group.get_alloc(handle) { - Some(alloc) => alloc, - None => continue, // TODO err out on invalid handle? - }; - - meshes.push((alloc, mesh)); - } - - by_group - .into_iter() - .filter_map(|(group, meshes)| group.map(|group| (group, meshes))) - .collect() - } -} diff --git a/src/mesh/staging.rs b/src/mesh/staging.rs deleted file mode 100644 index 2679aec..0000000 --- a/src/mesh/staging.rs +++ /dev/null @@ -1,51 +0,0 @@ -//! Intermediate CPU-mappable, GPU-visible storage for transferral to an attribute pool. -//! -//! TODO: double-buffered staging - -use super::*; -use std::collections::VecDeque; - -pub struct StagingPool { - stage_size: usize, - current_budget: usize, - copies: Vec, - spillover: VecDeque, -} - -impl StagingPool { - pub fn new(stage_size: usize) -> Self { - Self { - stage_size, - current_budget: 0, - copies: Default::default(), - spillover: Default::default(), - } - } - - pub fn flush(&mut self) { - todo!() - } - - pub fn queue_copies(&mut self, copies: Vec) { - todo!() - } -} - -pub struct CopyInfo { - /// The index of the target attribute pool's group. - pub group: usize, - - /// The target attribute pool within the group. - pub target: AttrId, - - /// The destination offset *in bytes.* - pub offset: usize, - - /// The copy size *in bytes.* - pub size: usize, -} - -pub struct SpilloverBuffer { - pub info: CopyInfo, - pub data: Vec, -}