From 8943dc4765147293025613f3b1242f1f05501f56 Mon Sep 17 00:00:00 2001 From: Zakarum Date: Sun, 1 Dec 2019 18:15:40 +0300 Subject: [PATCH 1/2] Dynamic meshes --- core/src/casts.rs | 26 +- factory/src/factory.rs | 20 +- factory/src/upload.rs | 8 +- memory/src/mapping/mod.rs | 5 +- memory/src/mapping/range.rs | 15 +- memory/src/mapping/write.rs | 17 +- mesh/src/{mesh.rs => builder.rs} | 458 +++++++++++-------------- mesh/src/dynamic.rs | 564 +++++++++++++++++++++++++++++++ mesh/src/lib.rs | 79 ++++- mesh/src/static.rs | 159 +++++++++ 10 files changed, 1042 insertions(+), 309 deletions(-) rename mesh/src/{mesh.rs => builder.rs} (51%) create mode 100644 mesh/src/dynamic.rs create mode 100644 mesh/src/static.rs diff --git a/core/src/casts.rs b/core/src/casts.rs index 11952b8a..0ff79ef3 100644 --- a/core/src/casts.rs +++ b/core/src/casts.rs @@ -1,12 +1,12 @@ //! Contains functions for casting -use std::{any::TypeId, borrow::Cow}; +use std::{any::TypeId, borrow::Cow, mem::{size_of, align_of}}; /// Cast vec of some arbitrary type into vec of bytes. /// Can lead to UB if allocator changes. Use with caution. /// TODO: Replace with something safer. pub fn cast_vec(mut vec: Vec) -> Vec { - let len = std::mem::size_of::() * vec.len(); - let cap = std::mem::size_of::() * vec.capacity(); + let len = size_of::() * vec.len(); + let cap = size_of::() * vec.capacity(); let ptr = vec.as_mut_ptr(); std::mem::forget(vec); unsafe { Vec::from_raw_parts(ptr as _, len, cap) } @@ -14,11 +14,29 @@ pub fn cast_vec(mut vec: Vec) -> Vec { /// Cast slice of some arbitrary type into slice of bytes. pub fn cast_slice(slice: &[T]) -> &[u8] { - let len = std::mem::size_of::() * slice.len(); + let len = size_of::() * slice.len(); let ptr = slice.as_ptr(); unsafe { std::slice::from_raw_parts(ptr as _, len) } } +/// Cast slice of some arbitrary type into slice of bytes. +pub unsafe fn cast_arbitrary_slice(slice: &[T]) -> &[U] { + let bytes = size_of::() * slice.len(); + let u_s = bytes / size_of::(); + let ptr = slice.as_ptr(); + assert_eq!(ptr as usize % align_of::(), 0); + std::slice::from_raw_parts(ptr as _, u_s) +} + +/// Cast slice of some arbitrary type into slice of bytes. +pub unsafe fn cast_arbitrary_slice_mut(slice: &mut [T]) -> &mut [U] { + let bytes = size_of::() * slice.len(); + let u_s = bytes / size_of::(); + let ptr = slice.as_ptr(); + assert_eq!(ptr as usize % align_of::(), 0); + std::slice::from_raw_parts_mut(ptr as _, u_s) +} + /// Cast `cow` of some arbitrary type into `cow` of bytes. /// Can lead to UB if allocator changes. Use with caution. /// TODO: Replace with something safer. diff --git a/factory/src/factory.rs b/factory/src/factory.rs index b5233bdc..49eeb610 100644 --- a/factory/src/factory.rs +++ b/factory/src/factory.rs @@ -28,7 +28,7 @@ use { HasRawWindowHandle, }, smallvec::SmallVec, - std::{borrow::BorrowMut, cmp::max, mem::ManuallyDrop}, + std::{borrow::BorrowMut, cmp::max, mem::{ManuallyDrop, size_of_val}}, thread_profiler::profile_scope, }; @@ -442,7 +442,7 @@ where /// Update content of the buffer bound to host visible memory. /// This function (unlike [`upload_buffer`]) update content immediatelly. /// - /// Buffers allocated from host-invisible memory types cannot be + /// Buffers allocated not from host-invisible memory types cannot be /// updated via this function. /// /// Updated content will be automatically made visible to device operations @@ -450,7 +450,7 @@ where /// /// # Panics /// - /// Panics if buffer size is less than `offset` + size of `content`. + /// Panics if buffer size is less than `offset + size_of_val(content)`. /// /// # Safety /// @@ -468,7 +468,7 @@ where { let content = std::slice::from_raw_parts( content.as_ptr() as *const u8, - content.len() * std::mem::size_of::(), + size_of_val(content), ); let mut mapped = buffer.map(&self.device, offset..offset + content.len() as u64)?; @@ -512,7 +512,7 @@ where { assert!(buffer.info().usage.contains(buffer::Usage::TRANSFER_DST)); - let content_size = content.len() as u64 * std::mem::size_of::() as u64; + let content_size = size_of_val(content) as u64; let mut staging = self .create_buffer( BufferInfo { @@ -527,7 +527,11 @@ where .map_err(UploadError::Map)?; self.uploader - .upload_buffer(&self.device, buffer, offset, staging, last, next) + .upload_buffer(&self.device, buffer, staging, last, next, Some(rendy_core::hal::command::BufferCopy { + src: 0, + dst: offset, + size: content_size, + })) .map_err(UploadError::Upload) } @@ -548,15 +552,15 @@ where pub unsafe fn upload_from_staging_buffer( &self, buffer: &Buffer, - offset: u64, staging: Escape>, last: Option, next: BufferState, + ranges: impl IntoIterator, ) -> Result<(), OutOfMemory> { assert!(buffer.info().usage.contains(buffer::Usage::TRANSFER_DST)); assert!(staging.info().usage.contains(buffer::Usage::TRANSFER_SRC)); self.uploader - .upload_buffer(&self.device, buffer, offset, staging, last, next) + .upload_buffer(&self.device, buffer, staging, last, next, ranges) } /// Update image layers content with provided data. diff --git a/factory/src/upload.rs b/factory/src/upload.rs index 7ff15f70..b793af40 100644 --- a/factory/src/upload.rs +++ b/factory/src/upload.rs @@ -170,10 +170,10 @@ where &self, device: &Device, buffer: &Buffer, - offset: u64, staging: Escape>, last: Option, next: BufferState, + ranges: impl IntoIterator, ) -> Result<(), OutOfMemory> { let mut family_uploads = self.family_uploads[next.queue.family.index] .as_ref() @@ -198,11 +198,7 @@ where encoder.copy_buffer( staging.raw(), buffer.raw(), - Some(rendy_core::hal::command::BufferCopy { - src: 0, - dst: offset, - size: staging.size(), - }), + ranges, ); next_upload.staging_buffers.push(staging); diff --git a/memory/src/mapping/mod.rs b/memory/src/mapping/mod.rs index c8071bcb..b834b77e 100644 --- a/memory/src/mapping/mod.rs +++ b/memory/src/mapping/mod.rs @@ -4,7 +4,7 @@ pub(crate) mod write; use { crate::{memory::Memory, util::fits_usize}, gfx_hal::{device::Device as _, Backend}, - std::{ops::Range, ptr::NonNull}, + std::{ops::Range, ptr::NonNull, mem::MaybeUninit}, }; pub(crate) use self::range::{ @@ -118,12 +118,11 @@ where /// # Safety /// /// * Caller must ensure that device won't write to the memory region until the borrowing ends. - /// * `T` Must be plain-old-data type compatible with data in mapped region. pub unsafe fn read<'b, T>( &'b mut self, device: &B::Device, range: Range, - ) -> Result<&'b [T], gfx_hal::device::MapError> + ) -> Result<&'b [MaybeUninit], gfx_hal::device::MapError> where 'a: 'b, T: Copy, diff --git a/memory/src/mapping/range.rs b/memory/src/mapping/range.rs index f840cc75..2d25ee19 100644 --- a/memory/src/mapping/range.rs +++ b/memory/src/mapping/range.rs @@ -1,7 +1,7 @@ use { crate::util::fits_usize, std::{ - mem::{align_of, size_of}, + mem::{align_of, size_of, MaybeUninit}, ops::Range, ptr::NonNull, slice::{from_raw_parts, from_raw_parts_mut}, @@ -62,8 +62,7 @@ pub(crate) fn mapped_sub_range( /// User must ensure that: /// * this function won't create aliasing slices. /// * returned slice doesn't outlive mapping. -/// * `T` Must be plain-old-data type compatible with data in mapped region. -pub(crate) unsafe fn mapped_slice_mut<'a, T>(ptr: NonNull, size: usize) -> &'a mut [T] { +pub(crate) unsafe fn mapped_slice_mut<'a, T>(ptr: NonNull, size: usize) -> &'a mut [MaybeUninit] { assert_eq!( size % size_of::(), 0, @@ -76,15 +75,13 @@ pub(crate) unsafe fn mapped_slice_mut<'a, T>(ptr: NonNull, size: usize) -> & "Range offset must be multiple of element alignment" ); assert!(usize::max_value() - size >= ptr.as_ptr() as usize); - from_raw_parts_mut(ptr.as_ptr() as *mut T, size) + from_raw_parts_mut(ptr.as_ptr() as *mut MaybeUninit, size) } /// # Safety /// -/// User must ensure that: -/// * returned slice doesn't outlive mapping. -/// * `T` Must be plain-old-data type compatible with data in mapped region. -pub(crate) unsafe fn mapped_slice<'a, T>(ptr: NonNull, size: usize) -> &'a [T] { +/// User must ensure that returned slice doesn't outlive mapping. +pub(crate) unsafe fn mapped_slice<'a, T>(ptr: NonNull, size: usize) -> &'a [MaybeUninit] { assert_eq!( size % size_of::(), 0, @@ -97,5 +94,5 @@ pub(crate) unsafe fn mapped_slice<'a, T>(ptr: NonNull, size: usize) -> &'a [ "Range offset must be multiple of element alignment" ); assert!(usize::max_value() - size >= ptr.as_ptr() as usize); - from_raw_parts(ptr.as_ptr() as *const T, size) + from_raw_parts(ptr.as_ptr() as *const MaybeUninit, size) } diff --git a/memory/src/mapping/write.rs b/memory/src/mapping/write.rs index d067a612..c80fdd3d 100644 --- a/memory/src/mapping/write.rs +++ b/memory/src/mapping/write.rs @@ -1,4 +1,4 @@ -use std::ptr::copy_nonoverlapping; +use std::{mem::MaybeUninit, ptr::copy_nonoverlapping}; /// Trait for memory region suitable for host writes. pub trait Write { @@ -7,7 +7,7 @@ pub trait Write { /// # Safety /// /// * Returned slice should not be read. - unsafe fn slice(&mut self) -> &mut [T]; + unsafe fn slice(&mut self) -> &mut [MaybeUninit]; /// Write data into mapped memory sub-region. /// @@ -18,14 +18,13 @@ pub trait Write { unsafe { let slice = self.slice(); assert!(data.len() <= slice.len()); - copy_nonoverlapping(data.as_ptr(), slice.as_mut_ptr(), data.len()); + copy_nonoverlapping(data.as_ptr(), slice.as_mut_ptr() as *mut T, data.len()); } } } -#[derive(Debug)] pub(super) struct WriteFlush<'a, T, F: FnOnce() + 'a> { - pub(super) slice: &'a mut [T], + pub(super) slice: &'a mut [MaybeUninit], pub(super) flush: Option, } @@ -49,15 +48,13 @@ where /// # Safety /// /// [See doc comment for trait method](trait.Write#method.slice) - unsafe fn slice(&mut self) -> &mut [T] { + unsafe fn slice(&mut self) -> &mut [MaybeUninit] { self.slice } } -#[warn(dead_code)] -#[derive(Debug)] pub(super) struct WriteCoherent<'a, T> { - pub(super) slice: &'a mut [T], + pub(super) slice: &'a mut [MaybeUninit], } impl<'a, T> Write for WriteCoherent<'a, T> @@ -67,7 +64,7 @@ where /// # Safety /// /// [See doc comment for trait method](trait.Write#method.slice) - unsafe fn slice(&mut self) -> &mut [T] { + unsafe fn slice(&mut self) -> &mut [MaybeUninit] { self.slice } } diff --git a/mesh/src/mesh.rs b/mesh/src/builder.rs similarity index 51% rename from mesh/src/mesh.rs rename to mesh/src/builder.rs index 9285c713..edc7e871 100644 --- a/mesh/src/mesh.rs +++ b/mesh/src/builder.rs @@ -1,31 +1,17 @@ -//! -//! Manage vertex and index buffers of single objects with ease. -//! use crate::{ - command::{EncoderCommon, Graphics, QueueId, RenderPassEncoder, Supports}, - core::cast_cow, + index_stride, + command::QueueId, + core::{cast_arbitrary_slice, cast_vec, cast_cow, hal::{Backend, IndexType}}, factory::{BufferState, Factory, UploadError}, memory::{Data, Upload, Write}, - resource::{Buffer, BufferInfo, Escape}, + resource::BufferInfo, AsVertex, VertexFormat, + r#static::{IndexBuffer, Mesh, VertexBufferLayout}, align_by, + dynamic::{DynamicMesh, DynamicVertices, DynamicIndices}, }; use rendy_core::hal::adapter::PhysicalDevice; -use std::{borrow::Cow, mem::size_of}; - -/// Vertex buffer with it's format -#[derive(Debug)] -pub struct VertexBufferLayout { - offset: u64, - format: VertexFormat, -} - -/// Index buffer with it's type -#[derive(Debug)] -pub struct IndexBuffer { - buffer: Escape>, - index_type: rendy_core::hal::IndexType, -} +use std::{any::TypeId, borrow::Cow, mem::{align_of, MaybeUninit}}; /// Abstracts over two types of indices and their absence. #[derive(Debug)] @@ -91,25 +77,129 @@ pub struct MeshBuilder<'a> { #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] struct RawVertices<'a> { #[cfg_attr(feature = "serde", serde(with = "serde_bytes", borrow))] - vertices: Cow<'a, [u8]>, + bytes: Cow<'a, [u8]>, format: VertexFormat, + align: usize, + ty: TypeId, +} + +#[derive(Clone, Copy)] #[repr(C, align(1))] struct Aligned1(u8); +#[derive(Clone, Copy)] #[repr(C, align(2))] struct Aligned2(u8); +#[derive(Clone, Copy)] #[repr(C, align(4))] struct Aligned4(u8); +#[derive(Clone, Copy)] #[repr(C, align(8))] struct Aligned8(u8); +#[derive(Clone, Copy)] #[repr(C, align(16))] struct Aligned16(u8); +#[derive(Clone, Copy)] #[repr(C, align(32))] struct Aligned32(u8); +#[derive(Clone, Copy)] #[repr(C, align(64))] struct Aligned64(u8); +#[derive(Clone, Copy)] #[repr(C, align(128))] struct Aligned128(u8); +#[derive(Clone, Copy)] #[repr(C, align(256))] struct Aligned256(u8); +#[derive(Clone, Copy)] #[repr(C, align(512))] struct Aligned512(u8); + +impl RawVertices<'_> { + fn into_owned(self) -> RawVertices<'static> { + let bytes = match self.bytes { + Cow::Borrowed(bytes) => { + unsafe { match self.align { + 1 => cast_vec(Vec::::from(cast_arbitrary_slice(bytes))), + 2 => cast_vec(Vec::::from(cast_arbitrary_slice(bytes))), + 4 => cast_vec(Vec::::from(cast_arbitrary_slice(bytes))), + 8 => cast_vec(Vec::::from(cast_arbitrary_slice(bytes))), + 16 => cast_vec(Vec::::from(cast_arbitrary_slice(bytes))), + 32 => cast_vec(Vec::::from(cast_arbitrary_slice(bytes))), + 64 => cast_vec(Vec::::from(cast_arbitrary_slice(bytes))), + 128 => cast_vec(Vec::::from(cast_arbitrary_slice(bytes))), + 256 => cast_vec(Vec::::from(cast_arbitrary_slice(bytes))), + 512 => cast_vec(Vec::::from(cast_arbitrary_slice(bytes))), + _ => panic!("Too aligned"), + } } + }, + Cow::Owned(owned) => owned, + }; + + RawVertices { + bytes: Cow::Owned(bytes), + format: self.format, + align: self.align, + ty: self.ty, + } + } + + fn into_dynamic(self) -> DynamicVertices { + let bytes = match self.bytes { + Cow::Borrowed(bytes) => { + unsafe { match self.align { + 1 => cast_vec(Vec::::from(cast_arbitrary_slice(bytes))), + 2 => cast_vec(Vec::::from(cast_arbitrary_slice(bytes))), + 4 => cast_vec(Vec::::from(cast_arbitrary_slice(bytes))), + 8 => cast_vec(Vec::::from(cast_arbitrary_slice(bytes))), + 16 => cast_vec(Vec::::from(cast_arbitrary_slice(bytes))), + 32 => cast_vec(Vec::::from(cast_arbitrary_slice(bytes))), + 64 => cast_vec(Vec::::from(cast_arbitrary_slice(bytes))), + 128 => cast_vec(Vec::::from(cast_arbitrary_slice(bytes))), + 256 => cast_vec(Vec::::from(cast_arbitrary_slice(bytes))), + 512 => cast_vec(Vec::::from(cast_arbitrary_slice(bytes))), + _ => panic!("Too aligned"), + } } + }, + Cow::Owned(owned) => owned, + }; + + DynamicVertices { + bytes, + dirty: Vec::new(), + ty: self.ty, + offset: 0, + size: 0, + format: self.format, + } + } } #[derive(Clone, Debug)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] struct RawIndices<'a> { #[cfg_attr(feature = "serde", serde(with = "serde_bytes", borrow))] - indices: Cow<'a, [u8]>, - index_type: rendy_core::hal::IndexType, + bytes: Cow<'a, [u8]>, + ty: IndexType, } -fn index_stride(index_type: rendy_core::hal::IndexType) -> usize { - match index_type { - rendy_core::hal::IndexType::U16 => size_of::(), - rendy_core::hal::IndexType::U32 => size_of::(), +impl RawIndices<'_> { + fn into_owned(self) -> RawIndices<'static> { + let bytes = match self.bytes { + Cow::Borrowed(bytes) => { + unsafe { match self.ty { + IndexType::U16 => cast_vec(Vec::::from(cast_arbitrary_slice(bytes))), + IndexType::U32 => cast_vec(Vec::::from(cast_arbitrary_slice(bytes))), + } } + }, + Cow::Owned(owned) => owned, + }; + + RawIndices { + bytes: Cow::Owned(bytes), + ty: self.ty, + } + } + + fn into_dynamic(self) -> DynamicIndices { + let bytes = match self.bytes { + Cow::Borrowed(bytes) => { + unsafe { match self.ty { + IndexType::U16 => cast_vec(Vec::::from(cast_arbitrary_slice(bytes))), + IndexType::U32 => cast_vec(Vec::::from(cast_arbitrary_slice(bytes))), + } } + }, + Cow::Owned(owned) => owned, + }; + + DynamicIndices { + bytes, + dirty: Vec::new(), + ty: self.ty, + } } } + impl<'a> MeshBuilder<'a> { /// Create empty builder. pub fn new() -> Self { @@ -127,15 +217,9 @@ impl<'a> MeshBuilder<'a> { vertices: self .vertices .into_iter() - .map(|v| RawVertices { - vertices: Cow::Owned(v.vertices.into_owned()), - format: v.format, - }) + .map(RawVertices::into_owned) .collect(), - indices: self.indices.map(|i| RawIndices { - indices: Cow::Owned(i.indices.into_owned()), - index_type: i.index_type, - }), + indices: self.indices.map(RawIndices::into_owned), prim: self.prim, } } @@ -157,12 +241,12 @@ impl<'a> MeshBuilder<'a> { self.indices = match indices.into() { Indices::None => None, Indices::U16(i) => Some(RawIndices { - indices: cast_cow(i), - index_type: rendy_core::hal::IndexType::U16, + bytes: cast_cow(i), + ty: IndexType::U16, }), Indices::U32(i) => Some(RawIndices { - indices: cast_cow(i), - index_type: rendy_core::hal::IndexType::U32, + bytes: cast_cow(i), + ty: IndexType::U32, }), }; self @@ -185,8 +269,10 @@ impl<'a> MeshBuilder<'a> { D: Into>, { self.vertices.push(RawVertices { - vertices: cast_cow(vertices.into()), + bytes: cast_cow(vertices.into()), format: V::vertex(), + align: align_of::(), + ty: TypeId::of::(), }); self } @@ -216,20 +302,20 @@ impl<'a> MeshBuilder<'a> { /// Note that contents of index buffer is not validated. pub fn build(&self, queue: QueueId, factory: &Factory) -> Result, UploadError> where - B: rendy_core::hal::Backend, + B: Backend, { let align = factory.physical().limits().non_coherent_atom_size; let mut len = self .vertices .iter() - .map(|v| v.vertices.len() as u32 / v.format.stride) + .map(|v| v.bytes.len() / v.format.stride as usize) .min() .unwrap_or(0); let buffer_size = self .vertices .iter() - .map(|v| (v.format.stride * len) as usize) + .map(|v| v.format.stride as usize * len) .sum(); let aligned_size = align_by(align, buffer_size) as u64; @@ -258,22 +344,32 @@ impl<'a> MeshBuilder<'a> { let mut mapped = staging .map(factory, 0..aligned_size) .map_err(UploadError::Map)?; - let mut writer = - unsafe { mapped.write(factory, 0..aligned_size) }.map_err(UploadError::Map)?; - let staging_slice = unsafe { writer.slice() }; + let mut writer = unsafe { + // New staging buffer cannot be accessed by device. + mapped.write(factory, 0..aligned_size) + }.map_err(UploadError::Map)?; + + let staging_slice: &mut [MaybeUninit] = unsafe { + // Slize is never read. + writer.slice() + }; let mut offset = 0usize; let mut vertex_layouts: Vec<_> = self .vertices .iter() - .map(|RawVertices { vertices, format }| { - let size = (format.stride * len) as usize; - staging_slice[offset..offset + size].copy_from_slice(&vertices[0..size]); - let this_offset = offset as u64; + .map(|v| { + let size = v.format.stride as usize * len; + unsafe { + debug_assert!(v.bytes.len() >= size); // "Ensured by `len` calculation + // `staging_slice` size is sum of all `size`s in this loop + alignment. + std::ptr::copy_nonoverlapping(v.bytes.as_ptr(), staging_slice.as_mut_ptr().add(offset) as *mut u8, size); + } + let this_offset = offset; offset += size; VertexBufferLayout { offset: this_offset, - format: format.clone(), + format: v.format.clone(), } }) .collect(); @@ -282,19 +378,37 @@ impl<'a> MeshBuilder<'a> { drop(writer); drop(mapped); + unsafe { + factory + .upload_from_staging_buffer( + &mut buffer, + staging, + None, + BufferState::new(queue) + .with_access(rendy_core::hal::buffer::Access::VERTEX_BUFFER_READ) + .with_stage(rendy_core::hal::pso::PipelineStage::VERTEX_INPUT), + Some(rendy_core::hal::command::BufferCopy { + src: 0, + dst: 0, + size: buffer_size as u64, + }) + ) + .map_err(UploadError::Upload)?; + } + vertex_layouts.sort_unstable_by(|a, b| a.format.cmp(&b.format)); let index_buffer = match self.indices { None => None, Some(RawIndices { - ref indices, - index_type, + ref bytes, + ty, }) => { - len = (indices.len() / index_stride(index_type)) as u32; + len = bytes.len() / index_stride(ty); let mut buffer = factory .create_buffer( BufferInfo { - size: indices.len() as _, + size: bytes.len() as _, usage: rendy_core::hal::buffer::Usage::INDEX | rendy_core::hal::buffer::Usage::TRANSFER_DST, }, @@ -306,7 +420,7 @@ impl<'a> MeshBuilder<'a> { factory.upload_buffer( &mut buffer, 0, - &indices, + &bytes, None, BufferState::new(queue) .with_access(rendy_core::hal::buffer::Access::INDEX_BUFFER_READ) @@ -314,231 +428,41 @@ impl<'a> MeshBuilder<'a> { )?; } - Some(IndexBuffer { buffer, index_type }) + Some(IndexBuffer { buffer, ty }) } }; - unsafe { - factory - .upload_from_staging_buffer( - &mut buffer, - 0, - staging, - None, - BufferState::new(queue) - .with_access(rendy_core::hal::buffer::Access::VERTEX_BUFFER_READ) - .with_stage(rendy_core::hal::pso::PipelineStage::VERTEX_INPUT), - ) - .map_err(UploadError::Upload)?; - } - Ok(Mesh { vertex_layouts, index_buffer, vertex_buffer: buffer, prim: self.prim, - len, + len: len as u32, }) } -} - -fn align_by(align: usize, value: usize) -> usize { - ((value + align - 1) / align) * align -} - -/// Single mesh is a collection of buffer ranges that provides available attributes. -/// Usually exactly one mesh is used per draw call. -#[derive(Debug)] -pub struct Mesh { - vertex_buffer: Escape>, - vertex_layouts: Vec, - index_buffer: Option>, - prim: rendy_core::hal::pso::Primitive, - len: u32, -} - -impl Mesh -where - B: rendy_core::hal::Backend, -{ - /// Build new mesh with `MeshBuilder` - pub fn builder<'a>() -> MeshBuilder<'a> { - MeshBuilder::new() - } - - /// rendy_core::hal::pso::Primitive type of the `Mesh` - pub fn primitive(&self) -> rendy_core::hal::pso::Primitive { - self.prim - } - /// Returns the number of vertices that will be drawn - /// in the mesh. For a mesh with no index buffer, - /// this is the same as the number of vertices, or for - /// a mesh with indices, this is the same as the number - /// of indices. - pub fn len(&self) -> u32 { - self.len - } - - fn get_vertex_iter<'a>( - &'a self, - formats: &[VertexFormat], - ) -> Result, Incompatible> { - debug_assert!(is_slice_sorted(formats), "Formats: {:#?}", formats); - debug_assert!(is_slice_sorted_by_key(&self.vertex_layouts, |l| &l.format)); - - let mut vertex = smallvec::SmallVec::<[_; 16]>::new(); - - let mut next = 0; - for format in formats { - if let Some(index) = find_compatible_buffer(&self.vertex_layouts[next..], format) { - next += index; - vertex.push(self.vertex_layouts[next].offset); - } else { - // Can't bind - return Err(Incompatible { - not_found: format.clone(), - in_formats: self - .vertex_layouts - .iter() - .map(|l| l.format.clone()) - .collect(), - }); - } - } - - let buffer = self.vertex_buffer.raw(); - Ok(vertex.into_iter().map(move |offset| (buffer, offset))) - } - - /// Bind buffers to specified attribute locations. - pub fn bind( - &self, - first_binding: u32, - formats: &[VertexFormat], - encoder: &mut EncoderCommon<'_, B, C>, - ) -> Result + /// Builds and returns the new dynamic mesh. + /// + /// A mesh expects all vertex buffers to have the same number of elements. + /// If those are not equal, the length of smallest vertex buffer is selected + /// + /// Note that contents of index buffer is not validated. + /// + /// In addition dynamic mesh can be modified and new vertices added. + /// Set of vertex attributes or presense of index buffer cannot be changed. + /// To apply modifications to underlying GPU buffers `DynamicMesh::update` must be called. + pub fn build_dynamic(self, queue: QueueId, factory: &Factory) -> Result, UploadError> where - C: Supports, + B: Backend, { - let vertex_iter = self.get_vertex_iter(formats)?; - match self.index_buffer.as_ref() { - Some(index_buffer) => unsafe { - encoder.bind_index_buffer(index_buffer.buffer.raw(), 0, index_buffer.index_type); - encoder.bind_vertex_buffers(first_binding, vertex_iter); - }, - None => unsafe { - encoder.bind_vertex_buffers(first_binding, vertex_iter); - }, - } - - Ok(self.len) - } + let mesh = self.build(queue, factory)?; - /// Bind buffers to specified attribute locations and issue draw calls with given instance range. - pub fn bind_and_draw( - &self, - first_binding: u32, - formats: &[VertexFormat], - instance_range: std::ops::Range, - encoder: &mut RenderPassEncoder<'_, B>, - ) -> Result { - let vertex_iter = self.get_vertex_iter(formats)?; - unsafe { - match self.index_buffer.as_ref() { - Some(index_buffer) => { - encoder.bind_index_buffer( - index_buffer.buffer.raw(), - 0, - index_buffer.index_type, - ); - encoder.bind_vertex_buffers(first_binding, vertex_iter); - encoder.draw_indexed(0..self.len, 0, instance_range); - } - None => { - encoder.bind_vertex_buffers(first_binding, vertex_iter); - encoder.draw(0..self.len, instance_range); - } - } - } - - Ok(self.len) - } -} - -/// Error type returned by `Mesh::bind` in case of mesh's vertex buffers are incompatible with requested vertex formats. -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct Incompatible { - /// Format that was queried but was not found - pub not_found: VertexFormat, - /// List of formats that were available at query time - pub in_formats: Vec, -} - -impl std::fmt::Display for Incompatible { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!( - f, - "Vertex format {:?} is not compatible with any of {:?}.", - self.not_found, self.in_formats - ) - } -} -impl std::error::Error for Incompatible {} - -/// Helper function to find buffer with compatible format. -fn find_compatible_buffer( - vertex_layouts: &[VertexBufferLayout], - format: &VertexFormat, -) -> Option { - debug_assert!(is_slice_sorted(&*format.attributes)); - for (i, layout) in vertex_layouts.iter().enumerate() { - debug_assert!(is_slice_sorted(&*layout.format.attributes)); - if is_compatible(&layout.format, format) { - return Some(i); - } - } - None -} - -/// Check is vertex format `left` is compatible with `right`. -/// `left` must have same `stride` and contain all attributes from `right`. -fn is_compatible(left: &VertexFormat, right: &VertexFormat) -> bool { - if left.stride != right.stride { - return false; - } - - // Don't start searching from index 0 because attributes are sorted - let mut skip = 0; - right.attributes.iter().all(|r| { - left.attributes[skip..] - .iter() - .position(|l| l == r) - .map_or(false, |p| { - skip += p; - true - }) - }) -} - -/// Chech if slice o f ordered values is sorted. -fn is_slice_sorted(slice: &[T]) -> bool { - is_slice_sorted_by_key(slice, |i| i) -} - -/// Check if slice is sorted using ordered key and key extractor -fn is_slice_sorted_by_key<'a, T, K: Ord>(slice: &'a [T], f: impl Fn(&'a T) -> K) -> bool { - if let Some((first, slice)) = slice.split_first() { - let mut cmp = f(first); - for item in slice { - let item = f(item); - if cmp > item { - return false; - } - cmp = item; - } + Ok(DynamicMesh { + mesh, + vertices: self.vertices.into_iter().map(RawVertices::into_dynamic).collect(), + indices: self.indices.map(RawIndices::into_dynamic), + }) } - true } impl<'a, A> From> for MeshBuilder<'a> diff --git a/mesh/src/dynamic.rs b/mesh/src/dynamic.rs new file mode 100644 index 00000000..4658b446 --- /dev/null +++ b/mesh/src/dynamic.rs @@ -0,0 +1,564 @@ + +use crate::{ + builder::MeshBuilder, + index_stride, align_by, + command::QueueId, + core::{cast_arbitrary_slice_mut, hal::{Backend, IndexType}}, + factory::{BufferState, Factory, UploadError}, + memory::{Data, Upload, Write}, + resource::BufferInfo, + AsVertex, VertexFormat, + r#static::{Mesh, IndexBuffer}, +}; +use rendy_core::hal::adapter::PhysicalDevice; +use std::{any::TypeId, mem::{size_of, align_of, MaybeUninit}, ops::{Deref, Range}}; + +pub(crate) struct DynamicVertices { + pub(crate) bytes: Vec, + pub(crate) dirty: Vec>, + pub(crate) ty: TypeId, + pub(crate) offset: usize, + pub(crate) size: usize, + pub(crate) format: VertexFormat, +} + +pub(crate)struct DynamicIndices { + pub(crate) bytes: Vec, + pub(crate) dirty: Vec>, + pub(crate) ty: IndexType, +} + +/// Single mesh is a collection of buffer ranges that provides available attributes. +/// Usually exactly one mesh is used per draw call. +/// +/// Dynamic mesh also allows modifying vertices and indices between frames. +pub struct DynamicMesh { + pub(crate) mesh: Mesh, + pub(crate) vertices: Vec, + pub(crate) indices: Option, +} + +#[derive(Clone, Copy, Debug)] +pub enum VerticesModifyError { + OutOfRange { + count: usize, + requested: usize, + }, + TypeMismatch { + expected: TypeId, + found: TypeId, + }, +} + +#[derive(Clone, Copy, Debug)] +pub enum IndicesModifyError { + NoIndexBuffer, + TypeMismatch { + expected: IndexType, + found: IndexType, + }, +} + +impl Deref for DynamicMesh +where + B: Backend, +{ + type Target = Mesh; + + fn deref(&self) -> &Mesh { + &self.mesh + } +} + +impl AsRef> for DynamicMesh +where + B: Backend, +{ + fn as_ref(&self) -> &Mesh { + &self.mesh + } +} + +impl std::borrow::Borrow> for DynamicMesh +where + B: Backend, +{ + fn borrow(&self) -> &Mesh { + &self.mesh + } +} + +impl DynamicMesh +where + B: Backend, +{ + /// Build new mesh with `MeshBuilder` + pub fn builder<'a>() -> MeshBuilder<'a> { + MeshBuilder::new() + } + + /// Acquire slice of vertices from vertex buffer at `index`. + /// All vertices from specifed range will be marked dirty and flushed on `DynamicMesh::update`. + /// If requested range is larger than vertex array then it will be resized, filling new values with `fill` before returning. + /// + /// # Panics + /// + /// This function will panic if wrong vertex type is requested. + pub fn modify_vertices(&mut self, index: usize, range: Range, fill: V) -> Result<&mut [V], VerticesModifyError> + where + V: AsVertex, + { + let len = self.vertices.len(); + let vertices = self.vertices.get_mut(index).ok_or_else(|| + VerticesModifyError::OutOfRange { + count: len, + requested: index, + } + )?; + + if vertices.ty != TypeId::of::() { + return Err(VerticesModifyError::TypeMismatch { + expected: TypeId::of::(), + found: vertices.ty, + }); + } + + let bytes_range = range.start as usize * size_of::() .. range.end as usize * size_of::(); + vertices.dirty.push(bytes_range.clone()); + + if vertices.bytes.len() < bytes_range.end { + let additional_bytes = bytes_range.end - vertices.bytes.len(); + assert_eq!(additional_bytes % size_of::(), 0); + vertices.bytes.reserve_exact(additional_bytes); + unsafe { + let ptr = vertices.bytes.as_mut_ptr().add(vertices.bytes.len()); + assert_eq!(ptr as usize % align_of::(), 0, "Vector contains `V`s and so must be aligned"); + let ptr = ptr as *mut V; + let additional = additional_bytes / size_of::(); + for ptr in std::iter::successors(Some(ptr), |p| Some(p.add(1))).take(additional) { + std::ptr::write(ptr, fill); + } + vertices.bytes.set_len(bytes_range.end); + } + } + + Ok(unsafe { + cast_arbitrary_slice_mut(&mut vertices.bytes[bytes_range]) + }) + } + + /// Acquire slice of indices from index buffer at `index`. + /// All indices from specifed range will be marked dirty and flushed on `DynamicMesh::update`. + /// If requested range is larger than index array then it will be resized, filling new values with `0` before returning. + /// + /// # Panics + /// + /// This function will panic if wrong index type is requested. + pub fn modify_indices_16(&mut self, range: Range) -> Result<&mut [u16], IndicesModifyError> { + let indices = self.indices.as_mut().ok_or(IndicesModifyError::NoIndexBuffer)?; + + if indices.ty != IndexType::U16 { + return Err(IndicesModifyError::TypeMismatch { + expected: IndexType::U16, + found: indices.ty, + }); + } + + let bytes_range = range.start as usize * size_of::() .. range.end as usize * size_of::(); + + if indices.bytes.len() < bytes_range.end { + indices.bytes.resize(bytes_range.end, 0); + } + + indices.dirty.push(bytes_range.clone()); + + if indices.bytes.len() < bytes_range.end { + let additional_bytes = bytes_range.end - indices.bytes.len(); + assert_eq!(additional_bytes % size_of::(), 0); + indices.bytes.reserve_exact(additional_bytes); + unsafe { + let ptr = indices.bytes.as_mut_ptr().add(indices.bytes.len()); + std::ptr::write_bytes(ptr, 0, additional_bytes); + indices.bytes.set_len(bytes_range.end); + } + } + + Ok(unsafe { + cast_arbitrary_slice_mut(&mut indices.bytes[bytes_range]) + }) + } + + /// Acquire slice of indices from index buffer at `index`. + /// All indices from specifed range will be marked dirty and flushed on `DynamicMesh::update`. + /// If requested range is larger than index array then it will be resized, filling new values with `0` before returning. + /// + /// # Panics + /// + /// This function will panic if wrong index type is requested. + pub fn modify_indices_32(&mut self, range: Range) -> Result<&mut [u32], IndicesModifyError> { + let indices = self.indices.as_mut().ok_or(IndicesModifyError::NoIndexBuffer)?; + + if indices.ty != IndexType::U32 { + return Err(IndicesModifyError::TypeMismatch { + expected: IndexType::U32, + found: indices.ty, + }); + } + + let bytes_range = range.start as usize * size_of::() .. range.end as usize * size_of::(); + + if indices.bytes.len() < bytes_range.end { + indices.bytes.resize(bytes_range.end, 0); + } + + indices.dirty.push(bytes_range.clone()); + + if indices.bytes.len() < bytes_range.end { + let additional_bytes = bytes_range.end - indices.bytes.len(); + assert_eq!(additional_bytes % size_of::(), 0); + indices.bytes.reserve_exact(additional_bytes); + unsafe { + let ptr = indices.bytes.as_mut_ptr().add(indices.bytes.len()); + std::ptr::write_bytes(ptr, 0, additional_bytes); + indices.bytes.set_len(bytes_range.end); + } + } + + Ok(unsafe { + cast_arbitrary_slice_mut(&mut indices.bytes[bytes_range]) + }) + } + + pub fn update(&mut self, queue: QueueId, factory: &Factory) -> Result<(), UploadError> { + let len = self.vertices + .iter() + .map(|v| v.bytes.len() / v.format.stride as usize) + .min() + .unwrap_or(0); + + if self.vertices.iter().all(|v| v.size >= len * v.format.stride as usize) { + self.update_vertex_buffer(queue, factory)?; + } else { + self.build_vertex_buffer(queue, factory)?; + } + + if let Some(indices) = &mut self.indices { + match &self.mesh.index_buffer { + Some(index_buffer) if index_buffer.buffer.size() >= indices.bytes.len() as u64 => { + self.update_index_buffer(queue, factory)?; + }, + _ => { + self.build_index_buffer(queue, factory)?; + } + } + } + + Ok(()) + } + + fn build_vertex_buffer(&mut self, queue: QueueId, factory: &Factory) -> Result<(), UploadError> { + let align = factory.physical().limits().non_coherent_atom_size; + let len = self.vertices + .iter() + .map(|v| v.bytes.len() / v.format.stride as usize) + .min() + .unwrap_or(0); + + let buffer_size = self.vertices + .iter() + .map(|v| v.format.stride as usize * len) + .sum(); + + let staging_size = align_by(align, buffer_size) as u64; + + let mut staging = factory + .create_buffer( + BufferInfo { + size: staging_size, + usage: rendy_core::hal::buffer::Usage::TRANSFER_SRC, + }, + Upload, + ) + .map_err(UploadError::Create)?; + + let mut buffer = factory + .create_buffer( + BufferInfo { + size: buffer_size as _, + usage: rendy_core::hal::buffer::Usage::VERTEX + | rendy_core::hal::buffer::Usage::TRANSFER_DST, + }, + Data, + ) + .map_err(UploadError::Create)?; + + let mut mapped = staging + .map(factory, 0..staging_size) + .map_err(UploadError::Map)?; + let mut writer = + unsafe { mapped.write(factory, 0..staging_size) }.map_err(UploadError::Map)?; + let staging_slice: &mut [MaybeUninit] = unsafe { writer.slice() }; + + let mut offset = 0usize; + for v in &mut self.vertices { + let size = v.format.stride as usize * len; + unsafe { + debug_assert!(v.bytes.len() >= size); // "Ensured by `len` calculation + // `staging_slice` size is sum of all `size`s in this loop + alignment. + std::ptr::copy_nonoverlapping(v.bytes.as_ptr(), staging_slice.as_mut_ptr().add(offset) as *mut u8, size); + } + v.offset = offset; + v.size = size; + offset += size; + } + + drop(staging_slice); + drop(writer); + drop(mapped); + + unsafe { + factory + .upload_from_staging_buffer( + &mut buffer, + staging, + None, + BufferState::new(queue) + .with_access(rendy_core::hal::buffer::Access::VERTEX_BUFFER_READ) + .with_stage(rendy_core::hal::pso::PipelineStage::VERTEX_INPUT), + Some(rendy_core::hal::command::BufferCopy { + src: 0, + dst: 0, + size: buffer_size as u64, + }) + ) + .map_err(UploadError::Upload)?; + } + + assert_eq!(self.mesh.vertex_layouts.len(), self.vertices.len()); + for (l, v) in self.mesh.vertex_layouts.iter_mut().zip(&self.vertices) { + assert_eq!(l.format, v.format); + l.offset = v.offset; + } + self.mesh.vertex_buffer = buffer; + if self.indices.is_none() { + self.mesh.len = len as u32; + } + + Ok(()) + } + + fn update_vertex_buffer(&mut self, queue: QueueId, factory: &Factory) -> Result<(), UploadError> { + let dirty_bytes_total = self.vertices.iter_mut().flat_map(|v| { + v.dirty.sort_by_key(|d| d.start); + + let mut j = 0; + for i in 1 .. v.dirty.len() { + if v.dirty[j].end >= v.dirty[i].start { + // merge + v.dirty[j].end = std::cmp::max(v.dirty[j].end, v.dirty[i].end); + } else { + // next + j += 1; + } + } + + v.dirty.truncate(j); + v.dirty.iter().map(|d| d.end - d.start) + }).sum(); + + if dirty_bytes_total == 0 { + return Ok(()); + } + + let align = factory.physical().limits().non_coherent_atom_size; + let staging_size = align_by(align, dirty_bytes_total) as u64; + + let mut staging = factory + .create_buffer( + BufferInfo { + size: staging_size, + usage: rendy_core::hal::buffer::Usage::TRANSFER_SRC, + }, + Upload, + ) + .map_err(UploadError::Create)?; + + let mut mapped = staging + .map(factory, 0..staging_size) + .map_err(UploadError::Map)?; + let mut writer = + unsafe { mapped.write(factory, 0..staging_size) }.map_err(UploadError::Map)?; + let staging_slice: &mut [MaybeUninit] = unsafe { writer.slice() }; + + let mut offset = 0usize; + for v in &self.vertices { + for d in &v.dirty { + let size = d.end - d.start; + unsafe { + debug_assert!(v.bytes.len() >= d.end); // "Ensured by `len` calculation + // `staging_slice` size is sum of all `size`s in this loop + alignment. + std::ptr::copy_nonoverlapping(v.bytes.as_ptr().add(d.start), staging_slice.as_mut_ptr().add(offset) as *mut u8, size); + } + offset += size; + } + } + + drop(staging_slice); + drop(writer); + drop(mapped); + + let state = BufferState::new(queue) + .with_access(rendy_core::hal::buffer::Access::VERTEX_BUFFER_READ) + .with_stage(rendy_core::hal::pso::PipelineStage::VERTEX_INPUT); + + let mut offset = 0usize; + unsafe { + factory + .upload_from_staging_buffer( + &mut self.mesh.vertex_buffer, + staging, + Some(state), + state, + self.vertices.iter().flat_map(|v| v.dirty.iter()).map(|d| { + let size = d.end - d.start; + offset += size; + rendy_core::hal::command::BufferCopy { + src: (offset - size) as u64, + dst: d.start as u64, + size: size as u64, + } + }), + ) + .map_err(UploadError::Upload)?; + } + + for v in &mut self.vertices { + v.dirty.clear(); + } + + Ok(()) + } + + fn build_index_buffer(&mut self, queue: QueueId, factory: &Factory) -> Result<(), UploadError> { + if let Some(indices) = &mut self.indices { + let len = (indices.bytes.len() / index_stride(indices.ty)) as u32; + let mut buffer = factory + .create_buffer( + BufferInfo { + size: indices.bytes.len() as _, + usage: rendy_core::hal::buffer::Usage::INDEX + | rendy_core::hal::buffer::Usage::TRANSFER_DST, + }, + Data, + ) + .map_err(UploadError::Create)?; + unsafe { + // New buffer can't be touched by device yet. + factory.upload_buffer( + &mut buffer, + 0, + &indices.bytes, + None, + BufferState::new(queue) + .with_access(rendy_core::hal::buffer::Access::INDEX_BUFFER_READ) + .with_stage(rendy_core::hal::pso::PipelineStage::VERTEX_INPUT), + )?; + } + + self.mesh.index_buffer = Some(IndexBuffer { buffer, ty: indices.ty }); + self.mesh.len = len; + } + + Ok(()) + } + + fn update_index_buffer(&mut self, queue: QueueId, factory: &Factory) -> Result<(), UploadError> { + let indices = self.indices.as_mut().unwrap(); + let index_buffer = self.mesh.index_buffer.as_mut().unwrap(); + + indices.dirty.sort_by_key(|d| d.start); + + let mut j = 0; + for i in 1 .. indices.dirty.len() { + if indices.dirty[j].end >= indices.dirty[i].start { + // merge + indices.dirty[j].end = std::cmp::max(indices.dirty[j].end, indices.dirty[i].end); + } else { + // next + j += 1; + } + } + + indices.dirty.truncate(j); + let dirty_bytes_total = indices.dirty.iter().map(|d| d.end - d.start).sum(); + + if dirty_bytes_total == 0 { + return Ok(()); + } + + let align = factory.physical().limits().non_coherent_atom_size; + let staging_size = align_by(align, dirty_bytes_total) as u64; + + let mut staging = factory + .create_buffer( + BufferInfo { + size: staging_size, + usage: rendy_core::hal::buffer::Usage::TRANSFER_SRC, + }, + Upload, + ) + .map_err(UploadError::Create)?; + + let mut mapped = staging + .map(factory, 0..staging_size) + .map_err(UploadError::Map)?; + let mut writer = + unsafe { mapped.write(factory, 0..staging_size) }.map_err(UploadError::Map)?; + let staging_slice: &mut [MaybeUninit] = unsafe { writer.slice() }; + + let mut offset = 0usize; + for d in &indices.dirty { + let size = d.end - d.start; + unsafe { + debug_assert!(indices.bytes.len() >= d.end); // "Ensured by `len` calculation + // `staging_slice` size is sum of all `size`s in this loop + alignment. + std::ptr::copy_nonoverlapping(indices.bytes.as_ptr().add(d.start), staging_slice.as_mut_ptr().add(offset) as *mut u8, size); + } + offset += size; + } + + drop(staging_slice); + drop(writer); + drop(mapped); + + let state = BufferState::new(queue) + .with_access(rendy_core::hal::buffer::Access::INDEX_BUFFER_READ) + .with_stage(rendy_core::hal::pso::PipelineStage::VERTEX_INPUT); + + let mut offset = 0usize; + unsafe { + factory + .upload_from_staging_buffer( + &mut index_buffer.buffer, + staging, + Some(state), + state, + indices.dirty.iter().map(|d| { + let size = d.end - d.start; + offset += size; + rendy_core::hal::command::BufferCopy { + src: (offset - size) as u64, + dst: d.start as u64, + size: size as u64, + } + }), + ) + .map_err(UploadError::Upload)?; + } + + indices.dirty.clear(); + Ok(()) + } +} + + diff --git a/mesh/src/lib.rs b/mesh/src/lib.rs index 0faa6555..5c5b0a30 100644 --- a/mesh/src/lib.rs +++ b/mesh/src/lib.rs @@ -23,7 +23,82 @@ use rendy_memory as memory; use rendy_resource as resource; mod format; -mod mesh; +mod builder; +mod r#static; +mod dynamic; -pub use crate::{format::*, mesh::*}; pub use rendy_core::types::vertex::*; +pub use crate::format::*; +pub use crate::{builder::*, r#static::*, dynamic::*}; + +fn index_stride(ty: rendy_core::hal::IndexType) -> usize { + match ty { + rendy_core::hal::IndexType::U16 => std::mem::size_of::(), + rendy_core::hal::IndexType::U32 => std::mem::size_of::(), + } +} + +fn align_by(align: usize, value: usize) -> usize { + ((value + align - 1) / align) * align +} + +/// Error type returned by `Mesh::bind` in case of mesh's vertex buffers are incompatible with requested vertex formats. +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct Incompatible { + /// Format that was queried but was not found + pub not_found: VertexFormat, + /// List of formats that were available at query time + pub in_formats: Vec, +} + +impl std::fmt::Display for Incompatible { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, + "Vertex format {:?} is not compatible with any of {:?}.", + self.not_found, self.in_formats + ) + } +} +impl std::error::Error for Incompatible {} + +/// Check is vertex format `left` is compatible with `right`. +/// `left` must have same `stride` and contain all attributes from `right`. +fn is_compatible(left: &VertexFormat, right: &VertexFormat) -> bool { + if left.stride != right.stride { + return false; + } + + // Don't start searching from index 0 because attributes are sorted + let mut skip = 0; + right.attributes.iter().all(|r| { + left.attributes[skip..] + .iter() + .position(|l| l == r) + .map_or(false, |p| { + skip += p; + true + }) + }) +} + +/// Chech if slice o f ordered values is sorted. +fn is_slice_sorted(slice: &[T]) -> bool { + is_slice_sorted_by_key(slice, |i| i) +} + +/// Check if slice is sorted using ordered key and key extractor +fn is_slice_sorted_by_key<'a, T, K: Ord>(slice: &'a [T], f: impl Fn(&'a T) -> K) -> bool { + if let Some((first, slice)) = slice.split_first() { + let mut cmp = f(first); + for item in slice { + let item = f(item); + if cmp > item { + return false; + } + cmp = item; + } + } + true +} + diff --git a/mesh/src/static.rs b/mesh/src/static.rs new file mode 100644 index 00000000..d259beaa --- /dev/null +++ b/mesh/src/static.rs @@ -0,0 +1,159 @@ + +use crate::{ + Incompatible, is_slice_sorted, is_slice_sorted_by_key, is_compatible, + command::{EncoderCommon, Graphics, RenderPassEncoder, Supports}, + core::hal::Backend, + resource::{Buffer, Escape}, + VertexFormat, + builder::MeshBuilder, +}; + +/// Helper function to find buffer with compatible format. +fn find_compatible_buffer( + vertex_layouts: &[VertexBufferLayout], + format: &VertexFormat, +) -> Option { + debug_assert!(is_slice_sorted(&*format.attributes)); + for (i, layout) in vertex_layouts.iter().enumerate() { + debug_assert!(is_slice_sorted(&*layout.format.attributes)); + if is_compatible(&layout.format, format) { + return Some(i); + } + } + None +} + +/// Vertex buffer with it's format +#[derive(Debug)] +pub(crate) struct VertexBufferLayout { + pub(crate) offset: usize, + pub(crate) format: VertexFormat, +} + +/// Index buffer with it's type +#[derive(Debug)] +pub(crate) struct IndexBuffer { + pub(crate) buffer: Escape>, + pub(crate) ty: rendy_core::hal::IndexType, +} + + +/// Single mesh is a collection of buffer ranges that provides available attributes. +/// Usually exactly one mesh is used per draw call. +#[derive(Debug)] +pub struct Mesh { + pub(crate) vertex_buffer: Escape>, + pub(crate) vertex_layouts: Vec, + pub(crate) index_buffer: Option>, + pub(crate) prim: rendy_core::hal::pso::Primitive, + pub(crate) len: u32, +} + +impl Mesh +where + B: Backend, +{ + /// Build new mesh with `MeshBuilder` + pub fn builder<'a>() -> MeshBuilder<'a> { + MeshBuilder::new() + } + + /// Primitive type of the `Mesh` + pub fn primitive(&self) -> rendy_core::hal::pso::Primitive { + self.prim + } + + /// Returns the number of vertices that will be drawn + /// in the mesh. For a mesh with no index buffer, + /// this is the same as the number of vertices, or for + /// a mesh with indices, this is the same as the number + /// of indices. + pub fn len(&self) -> u32 { + self.len + } + + fn get_vertex_iter<'a>( + &'a self, + formats: &[VertexFormat], + ) -> Result, Incompatible> { + debug_assert!(is_slice_sorted(formats), "Formats: {:#?}", formats); + debug_assert!(is_slice_sorted_by_key(&self.vertex_layouts, |l| &l.format)); + + let mut vertex = smallvec::SmallVec::<[_; 16]>::new(); + + let mut next = 0; + for format in formats { + if let Some(index) = find_compatible_buffer(&self.vertex_layouts[next..], format) { + next += index; + vertex.push(self.vertex_layouts[next].offset); + } else { + // Can't bind + return Err(Incompatible { + not_found: format.clone(), + in_formats: self + .vertex_layouts + .iter() + .map(|l| l.format.clone()) + .collect(), + }); + } + } + + let buffer = self.vertex_buffer.raw(); + Ok(vertex.into_iter().map(move |offset| (buffer, offset as u64))) + } + + /// Bind buffers to specified attribute locations. + pub fn bind( + &self, + first_binding: u32, + formats: &[VertexFormat], + encoder: &mut EncoderCommon<'_, B, C>, + ) -> Result + where + C: Supports, + { + let vertex_iter = self.get_vertex_iter(formats)?; + match self.index_buffer.as_ref() { + Some(index_buffer) => unsafe { + encoder.bind_index_buffer(index_buffer.buffer.raw(), 0, index_buffer.ty); + encoder.bind_vertex_buffers(first_binding, vertex_iter); + }, + None => unsafe { + encoder.bind_vertex_buffers(first_binding, vertex_iter); + }, + } + + Ok(self.len) + } + + /// Bind buffers to specified attribute locations and issue draw calls with given instance range. + pub fn bind_and_draw( + &self, + first_binding: u32, + formats: &[VertexFormat], + instance_range: std::ops::Range, + encoder: &mut RenderPassEncoder<'_, B>, + ) -> Result { + let vertex_iter = self.get_vertex_iter(formats)?; + unsafe { + match self.index_buffer.as_ref() { + Some(index_buffer) => { + encoder.bind_index_buffer( + index_buffer.buffer.raw(), + 0, + index_buffer.ty, + ); + encoder.bind_vertex_buffers(first_binding, vertex_iter); + encoder.draw_indexed(0..self.len, 0, instance_range); + } + None => { + encoder.bind_vertex_buffers(first_binding, vertex_iter); + encoder.draw(0..self.len, instance_range); + } + } + } + + Ok(self.len) + } +} From a05d1723b27022135ce33f6287cd2f86e57c4f8a Mon Sep 17 00:00:00 2001 From: Zakarum Date: Sun, 1 Dec 2019 18:21:48 +0300 Subject: [PATCH 2/2] Formatting --- core/src/casts.rs | 6 +- factory/src/factory.rs | 29 +++-- factory/src/upload.rs | 6 +- memory/src/mapping/mod.rs | 2 +- memory/src/mapping/range.rs | 5 +- mesh/src/builder.rs | 120 +++++++++++++------- mesh/src/dynamic.rs | 216 +++++++++++++++++++++++------------- mesh/src/lib.rs | 9 +- mesh/src/static.rs | 18 ++- 9 files changed, 257 insertions(+), 154 deletions(-) diff --git a/core/src/casts.rs b/core/src/casts.rs index 0ff79ef3..1e0b28fe 100644 --- a/core/src/casts.rs +++ b/core/src/casts.rs @@ -1,5 +1,9 @@ //! Contains functions for casting -use std::{any::TypeId, borrow::Cow, mem::{size_of, align_of}}; +use std::{ + any::TypeId, + borrow::Cow, + mem::{align_of, size_of}, +}; /// Cast vec of some arbitrary type into vec of bytes. /// Can lead to UB if allocator changes. Use with caution. diff --git a/factory/src/factory.rs b/factory/src/factory.rs index 49eeb610..6ede6a77 100644 --- a/factory/src/factory.rs +++ b/factory/src/factory.rs @@ -28,7 +28,11 @@ use { HasRawWindowHandle, }, smallvec::SmallVec, - std::{borrow::BorrowMut, cmp::max, mem::{ManuallyDrop, size_of_val}}, + std::{ + borrow::BorrowMut, + cmp::max, + mem::{size_of_val, ManuallyDrop}, + }, thread_profiler::profile_scope, }; @@ -466,10 +470,8 @@ where where T: 'static + Copy, { - let content = std::slice::from_raw_parts( - content.as_ptr() as *const u8, - size_of_val(content), - ); + let content = + std::slice::from_raw_parts(content.as_ptr() as *const u8, size_of_val(content)); let mut mapped = buffer.map(&self.device, offset..offset + content.len() as u64)?; mapped @@ -527,11 +529,18 @@ where .map_err(UploadError::Map)?; self.uploader - .upload_buffer(&self.device, buffer, staging, last, next, Some(rendy_core::hal::command::BufferCopy { - src: 0, - dst: offset, - size: content_size, - })) + .upload_buffer( + &self.device, + buffer, + staging, + last, + next, + Some(rendy_core::hal::command::BufferCopy { + src: 0, + dst: offset, + size: content_size, + }), + ) .map_err(UploadError::Upload) } diff --git a/factory/src/upload.rs b/factory/src/upload.rs index b793af40..573a06ab 100644 --- a/factory/src/upload.rs +++ b/factory/src/upload.rs @@ -195,11 +195,7 @@ where let next_upload = family_uploads.next_upload(device, next.queue.index)?; let mut encoder = next_upload.command_buffer.encoder(); - encoder.copy_buffer( - staging.raw(), - buffer.raw(), - ranges, - ); + encoder.copy_buffer(staging.raw(), buffer.raw(), ranges); next_upload.staging_buffers.push(staging); diff --git a/memory/src/mapping/mod.rs b/memory/src/mapping/mod.rs index b834b77e..1ff2bead 100644 --- a/memory/src/mapping/mod.rs +++ b/memory/src/mapping/mod.rs @@ -4,7 +4,7 @@ pub(crate) mod write; use { crate::{memory::Memory, util::fits_usize}, gfx_hal::{device::Device as _, Backend}, - std::{ops::Range, ptr::NonNull, mem::MaybeUninit}, + std::{mem::MaybeUninit, ops::Range, ptr::NonNull}, }; pub(crate) use self::range::{ diff --git a/memory/src/mapping/range.rs b/memory/src/mapping/range.rs index 2d25ee19..f888375b 100644 --- a/memory/src/mapping/range.rs +++ b/memory/src/mapping/range.rs @@ -62,7 +62,10 @@ pub(crate) fn mapped_sub_range( /// User must ensure that: /// * this function won't create aliasing slices. /// * returned slice doesn't outlive mapping. -pub(crate) unsafe fn mapped_slice_mut<'a, T>(ptr: NonNull, size: usize) -> &'a mut [MaybeUninit] { +pub(crate) unsafe fn mapped_slice_mut<'a, T>( + ptr: NonNull, + size: usize, +) -> &'a mut [MaybeUninit] { assert_eq!( size % size_of::(), 0, diff --git a/mesh/src/builder.rs b/mesh/src/builder.rs index edc7e871..2ec606d9 100644 --- a/mesh/src/builder.rs +++ b/mesh/src/builder.rs @@ -1,17 +1,24 @@ - use crate::{ - index_stride, + align_by, command::QueueId, - core::{cast_arbitrary_slice, cast_vec, cast_cow, hal::{Backend, IndexType}}, + core::{ + cast_arbitrary_slice, cast_cow, cast_vec, + hal::{Backend, IndexType}, + }, + dynamic::{DynamicIndices, DynamicMesh, DynamicVertices}, factory::{BufferState, Factory, UploadError}, + index_stride, memory::{Data, Upload, Write}, + r#static::{IndexBuffer, Mesh, VertexBufferLayout}, resource::BufferInfo, AsVertex, VertexFormat, - r#static::{IndexBuffer, Mesh, VertexBufferLayout}, align_by, - dynamic::{DynamicMesh, DynamicVertices, DynamicIndices}, }; use rendy_core::hal::adapter::PhysicalDevice; -use std::{any::TypeId, borrow::Cow, mem::{align_of, MaybeUninit}}; +use std::{ + any::TypeId, + borrow::Cow, + mem::{align_of, MaybeUninit}, +}; /// Abstracts over two types of indices and their absence. #[derive(Debug)] @@ -83,22 +90,42 @@ struct RawVertices<'a> { ty: TypeId, } -#[derive(Clone, Copy)] #[repr(C, align(1))] struct Aligned1(u8); -#[derive(Clone, Copy)] #[repr(C, align(2))] struct Aligned2(u8); -#[derive(Clone, Copy)] #[repr(C, align(4))] struct Aligned4(u8); -#[derive(Clone, Copy)] #[repr(C, align(8))] struct Aligned8(u8); -#[derive(Clone, Copy)] #[repr(C, align(16))] struct Aligned16(u8); -#[derive(Clone, Copy)] #[repr(C, align(32))] struct Aligned32(u8); -#[derive(Clone, Copy)] #[repr(C, align(64))] struct Aligned64(u8); -#[derive(Clone, Copy)] #[repr(C, align(128))] struct Aligned128(u8); -#[derive(Clone, Copy)] #[repr(C, align(256))] struct Aligned256(u8); -#[derive(Clone, Copy)] #[repr(C, align(512))] struct Aligned512(u8); +#[derive(Clone, Copy)] +#[repr(C, align(1))] +struct Aligned1(u8); +#[derive(Clone, Copy)] +#[repr(C, align(2))] +struct Aligned2(u8); +#[derive(Clone, Copy)] +#[repr(C, align(4))] +struct Aligned4(u8); +#[derive(Clone, Copy)] +#[repr(C, align(8))] +struct Aligned8(u8); +#[derive(Clone, Copy)] +#[repr(C, align(16))] +struct Aligned16(u8); +#[derive(Clone, Copy)] +#[repr(C, align(32))] +struct Aligned32(u8); +#[derive(Clone, Copy)] +#[repr(C, align(64))] +struct Aligned64(u8); +#[derive(Clone, Copy)] +#[repr(C, align(128))] +struct Aligned128(u8); +#[derive(Clone, Copy)] +#[repr(C, align(256))] +struct Aligned256(u8); +#[derive(Clone, Copy)] +#[repr(C, align(512))] +struct Aligned512(u8); impl RawVertices<'_> { fn into_owned(self) -> RawVertices<'static> { let bytes = match self.bytes { - Cow::Borrowed(bytes) => { - unsafe { match self.align { + Cow::Borrowed(bytes) => unsafe { + match self.align { 1 => cast_vec(Vec::::from(cast_arbitrary_slice(bytes))), 2 => cast_vec(Vec::::from(cast_arbitrary_slice(bytes))), 4 => cast_vec(Vec::::from(cast_arbitrary_slice(bytes))), @@ -110,7 +137,7 @@ impl RawVertices<'_> { 256 => cast_vec(Vec::::from(cast_arbitrary_slice(bytes))), 512 => cast_vec(Vec::::from(cast_arbitrary_slice(bytes))), _ => panic!("Too aligned"), - } } + } }, Cow::Owned(owned) => owned, }; @@ -125,8 +152,8 @@ impl RawVertices<'_> { fn into_dynamic(self) -> DynamicVertices { let bytes = match self.bytes { - Cow::Borrowed(bytes) => { - unsafe { match self.align { + Cow::Borrowed(bytes) => unsafe { + match self.align { 1 => cast_vec(Vec::::from(cast_arbitrary_slice(bytes))), 2 => cast_vec(Vec::::from(cast_arbitrary_slice(bytes))), 4 => cast_vec(Vec::::from(cast_arbitrary_slice(bytes))), @@ -138,7 +165,7 @@ impl RawVertices<'_> { 256 => cast_vec(Vec::::from(cast_arbitrary_slice(bytes))), 512 => cast_vec(Vec::::from(cast_arbitrary_slice(bytes))), _ => panic!("Too aligned"), - } } + } }, Cow::Owned(owned) => owned, }; @@ -165,11 +192,11 @@ struct RawIndices<'a> { impl RawIndices<'_> { fn into_owned(self) -> RawIndices<'static> { let bytes = match self.bytes { - Cow::Borrowed(bytes) => { - unsafe { match self.ty { + Cow::Borrowed(bytes) => unsafe { + match self.ty { IndexType::U16 => cast_vec(Vec::::from(cast_arbitrary_slice(bytes))), IndexType::U32 => cast_vec(Vec::::from(cast_arbitrary_slice(bytes))), - } } + } }, Cow::Owned(owned) => owned, }; @@ -182,11 +209,11 @@ impl RawIndices<'_> { fn into_dynamic(self) -> DynamicIndices { let bytes = match self.bytes { - Cow::Borrowed(bytes) => { - unsafe { match self.ty { + Cow::Borrowed(bytes) => unsafe { + match self.ty { IndexType::U16 => cast_vec(Vec::::from(cast_arbitrary_slice(bytes))), IndexType::U32 => cast_vec(Vec::::from(cast_arbitrary_slice(bytes))), - } } + } }, Cow::Owned(owned) => owned, }; @@ -199,7 +226,6 @@ impl RawIndices<'_> { } } - impl<'a> MeshBuilder<'a> { /// Create empty builder. pub fn new() -> Self { @@ -347,7 +373,8 @@ impl<'a> MeshBuilder<'a> { let mut writer = unsafe { // New staging buffer cannot be accessed by device. mapped.write(factory, 0..aligned_size) - }.map_err(UploadError::Map)?; + } + .map_err(UploadError::Map)?; let staging_slice: &mut [MaybeUninit] = unsafe { // Slize is never read. @@ -362,8 +389,12 @@ impl<'a> MeshBuilder<'a> { let size = v.format.stride as usize * len; unsafe { debug_assert!(v.bytes.len() >= size); // "Ensured by `len` calculation - // `staging_slice` size is sum of all `size`s in this loop + alignment. - std::ptr::copy_nonoverlapping(v.bytes.as_ptr(), staging_slice.as_mut_ptr().add(offset) as *mut u8, size); + // `staging_slice` size is sum of all `size`s in this loop + alignment. + std::ptr::copy_nonoverlapping( + v.bytes.as_ptr(), + staging_slice.as_mut_ptr().add(offset) as *mut u8, + size, + ); } let this_offset = offset; offset += size; @@ -391,7 +422,7 @@ impl<'a> MeshBuilder<'a> { src: 0, dst: 0, size: buffer_size as u64, - }) + }), ) .map_err(UploadError::Upload)?; } @@ -400,10 +431,7 @@ impl<'a> MeshBuilder<'a> { let index_buffer = match self.indices { None => None, - Some(RawIndices { - ref bytes, - ty, - }) => { + Some(RawIndices { ref bytes, ty }) => { len = bytes.len() / index_stride(ty); let mut buffer = factory .create_buffer( @@ -442,16 +470,20 @@ impl<'a> MeshBuilder<'a> { } /// Builds and returns the new dynamic mesh. - /// + /// /// A mesh expects all vertex buffers to have the same number of elements. /// If those are not equal, the length of smallest vertex buffer is selected - /// + /// /// Note that contents of index buffer is not validated. - /// + /// /// In addition dynamic mesh can be modified and new vertices added. /// Set of vertex attributes or presense of index buffer cannot be changed. /// To apply modifications to underlying GPU buffers `DynamicMesh::update` must be called. - pub fn build_dynamic(self, queue: QueueId, factory: &Factory) -> Result, UploadError> + pub fn build_dynamic( + self, + queue: QueueId, + factory: &Factory, + ) -> Result, UploadError> where B: Backend, { @@ -459,7 +491,11 @@ impl<'a> MeshBuilder<'a> { Ok(DynamicMesh { mesh, - vertices: self.vertices.into_iter().map(RawVertices::into_dynamic).collect(), + vertices: self + .vertices + .into_iter() + .map(RawVertices::into_dynamic) + .collect(), indices: self.indices.map(RawIndices::into_dynamic), }) } diff --git a/mesh/src/dynamic.rs b/mesh/src/dynamic.rs index 4658b446..cde352d8 100644 --- a/mesh/src/dynamic.rs +++ b/mesh/src/dynamic.rs @@ -1,17 +1,24 @@ - use crate::{ + align_by, builder::MeshBuilder, - index_stride, align_by, command::QueueId, - core::{cast_arbitrary_slice_mut, hal::{Backend, IndexType}}, + core::{ + cast_arbitrary_slice_mut, + hal::{Backend, IndexType}, + }, factory::{BufferState, Factory, UploadError}, + index_stride, memory::{Data, Upload, Write}, + r#static::{IndexBuffer, Mesh}, resource::BufferInfo, AsVertex, VertexFormat, - r#static::{Mesh, IndexBuffer}, }; use rendy_core::hal::adapter::PhysicalDevice; -use std::{any::TypeId, mem::{size_of, align_of, MaybeUninit}, ops::{Deref, Range}}; +use std::{ + any::TypeId, + mem::{align_of, size_of, MaybeUninit}, + ops::{Deref, Range}, +}; pub(crate) struct DynamicVertices { pub(crate) bytes: Vec, @@ -22,7 +29,7 @@ pub(crate) struct DynamicVertices { pub(crate) format: VertexFormat, } -pub(crate)struct DynamicIndices { +pub(crate) struct DynamicIndices { pub(crate) bytes: Vec, pub(crate) dirty: Vec>, pub(crate) ty: IndexType, @@ -40,14 +47,8 @@ pub struct DynamicMesh { #[derive(Clone, Copy, Debug)] pub enum VerticesModifyError { - OutOfRange { - count: usize, - requested: usize, - }, - TypeMismatch { - expected: TypeId, - found: TypeId, - }, + OutOfRange { count: usize, requested: usize }, + TypeMismatch { expected: TypeId, found: TypeId }, } #[derive(Clone, Copy, Debug)] @@ -100,21 +101,27 @@ where /// Acquire slice of vertices from vertex buffer at `index`. /// All vertices from specifed range will be marked dirty and flushed on `DynamicMesh::update`. /// If requested range is larger than vertex array then it will be resized, filling new values with `fill` before returning. - /// + /// /// # Panics /// /// This function will panic if wrong vertex type is requested. - pub fn modify_vertices(&mut self, index: usize, range: Range, fill: V) -> Result<&mut [V], VerticesModifyError> + pub fn modify_vertices( + &mut self, + index: usize, + range: Range, + fill: V, + ) -> Result<&mut [V], VerticesModifyError> where V: AsVertex, { let len = self.vertices.len(); - let vertices = self.vertices.get_mut(index).ok_or_else(|| - VerticesModifyError::OutOfRange { - count: len, - requested: index, - } - )?; + let vertices = + self.vertices + .get_mut(index) + .ok_or_else(|| VerticesModifyError::OutOfRange { + count: len, + requested: index, + })?; if vertices.ty != TypeId::of::() { return Err(VerticesModifyError::TypeMismatch { @@ -123,7 +130,8 @@ where }); } - let bytes_range = range.start as usize * size_of::() .. range.end as usize * size_of::(); + let bytes_range = + range.start as usize * size_of::()..range.end as usize * size_of::(); vertices.dirty.push(bytes_range.clone()); if vertices.bytes.len() < bytes_range.end { @@ -132,7 +140,11 @@ where vertices.bytes.reserve_exact(additional_bytes); unsafe { let ptr = vertices.bytes.as_mut_ptr().add(vertices.bytes.len()); - assert_eq!(ptr as usize % align_of::(), 0, "Vector contains `V`s and so must be aligned"); + assert_eq!( + ptr as usize % align_of::(), + 0, + "Vector contains `V`s and so must be aligned" + ); let ptr = ptr as *mut V; let additional = additional_bytes / size_of::(); for ptr in std::iter::successors(Some(ptr), |p| Some(p.add(1))).take(additional) { @@ -142,20 +154,24 @@ where } } - Ok(unsafe { - cast_arbitrary_slice_mut(&mut vertices.bytes[bytes_range]) - }) + Ok(unsafe { cast_arbitrary_slice_mut(&mut vertices.bytes[bytes_range]) }) } /// Acquire slice of indices from index buffer at `index`. /// All indices from specifed range will be marked dirty and flushed on `DynamicMesh::update`. /// If requested range is larger than index array then it will be resized, filling new values with `0` before returning. - /// + /// /// # Panics /// /// This function will panic if wrong index type is requested. - pub fn modify_indices_16(&mut self, range: Range) -> Result<&mut [u16], IndicesModifyError> { - let indices = self.indices.as_mut().ok_or(IndicesModifyError::NoIndexBuffer)?; + pub fn modify_indices_16( + &mut self, + range: Range, + ) -> Result<&mut [u16], IndicesModifyError> { + let indices = self + .indices + .as_mut() + .ok_or(IndicesModifyError::NoIndexBuffer)?; if indices.ty != IndexType::U16 { return Err(IndicesModifyError::TypeMismatch { @@ -164,7 +180,8 @@ where }); } - let bytes_range = range.start as usize * size_of::() .. range.end as usize * size_of::(); + let bytes_range = + range.start as usize * size_of::()..range.end as usize * size_of::(); if indices.bytes.len() < bytes_range.end { indices.bytes.resize(bytes_range.end, 0); @@ -183,20 +200,24 @@ where } } - Ok(unsafe { - cast_arbitrary_slice_mut(&mut indices.bytes[bytes_range]) - }) + Ok(unsafe { cast_arbitrary_slice_mut(&mut indices.bytes[bytes_range]) }) } /// Acquire slice of indices from index buffer at `index`. /// All indices from specifed range will be marked dirty and flushed on `DynamicMesh::update`. /// If requested range is larger than index array then it will be resized, filling new values with `0` before returning. - /// + /// /// # Panics /// /// This function will panic if wrong index type is requested. - pub fn modify_indices_32(&mut self, range: Range) -> Result<&mut [u32], IndicesModifyError> { - let indices = self.indices.as_mut().ok_or(IndicesModifyError::NoIndexBuffer)?; + pub fn modify_indices_32( + &mut self, + range: Range, + ) -> Result<&mut [u32], IndicesModifyError> { + let indices = self + .indices + .as_mut() + .ok_or(IndicesModifyError::NoIndexBuffer)?; if indices.ty != IndexType::U32 { return Err(IndicesModifyError::TypeMismatch { @@ -205,7 +226,8 @@ where }); } - let bytes_range = range.start as usize * size_of::() .. range.end as usize * size_of::(); + let bytes_range = + range.start as usize * size_of::()..range.end as usize * size_of::(); if indices.bytes.len() < bytes_range.end { indices.bytes.resize(bytes_range.end, 0); @@ -224,19 +246,22 @@ where } } - Ok(unsafe { - cast_arbitrary_slice_mut(&mut indices.bytes[bytes_range]) - }) + Ok(unsafe { cast_arbitrary_slice_mut(&mut indices.bytes[bytes_range]) }) } pub fn update(&mut self, queue: QueueId, factory: &Factory) -> Result<(), UploadError> { - let len = self.vertices + let len = self + .vertices .iter() .map(|v| v.bytes.len() / v.format.stride as usize) .min() .unwrap_or(0); - if self.vertices.iter().all(|v| v.size >= len * v.format.stride as usize) { + if self + .vertices + .iter() + .all(|v| v.size >= len * v.format.stride as usize) + { self.update_vertex_buffer(queue, factory)?; } else { self.build_vertex_buffer(queue, factory)?; @@ -245,8 +270,8 @@ where if let Some(indices) = &mut self.indices { match &self.mesh.index_buffer { Some(index_buffer) if index_buffer.buffer.size() >= indices.bytes.len() as u64 => { - self.update_index_buffer(queue, factory)?; - }, + self.update_index_buffer(queue, factory)?; + } _ => { self.build_index_buffer(queue, factory)?; } @@ -256,15 +281,21 @@ where Ok(()) } - fn build_vertex_buffer(&mut self, queue: QueueId, factory: &Factory) -> Result<(), UploadError> { + fn build_vertex_buffer( + &mut self, + queue: QueueId, + factory: &Factory, + ) -> Result<(), UploadError> { let align = factory.physical().limits().non_coherent_atom_size; - let len = self.vertices + let len = self + .vertices .iter() .map(|v| v.bytes.len() / v.format.stride as usize) .min() .unwrap_or(0); - let buffer_size = self.vertices + let buffer_size = self + .vertices .iter() .map(|v| v.format.stride as usize * len) .sum(); @@ -304,8 +335,12 @@ where let size = v.format.stride as usize * len; unsafe { debug_assert!(v.bytes.len() >= size); // "Ensured by `len` calculation - // `staging_slice` size is sum of all `size`s in this loop + alignment. - std::ptr::copy_nonoverlapping(v.bytes.as_ptr(), staging_slice.as_mut_ptr().add(offset) as *mut u8, size); + // `staging_slice` size is sum of all `size`s in this loop + alignment. + std::ptr::copy_nonoverlapping( + v.bytes.as_ptr(), + staging_slice.as_mut_ptr().add(offset) as *mut u8, + size, + ); } v.offset = offset; v.size = size; @@ -329,7 +364,7 @@ where src: 0, dst: 0, size: buffer_size as u64, - }) + }), ) .map_err(UploadError::Upload)?; } @@ -347,24 +382,32 @@ where Ok(()) } - fn update_vertex_buffer(&mut self, queue: QueueId, factory: &Factory) -> Result<(), UploadError> { - let dirty_bytes_total = self.vertices.iter_mut().flat_map(|v| { - v.dirty.sort_by_key(|d| d.start); - - let mut j = 0; - for i in 1 .. v.dirty.len() { - if v.dirty[j].end >= v.dirty[i].start { - // merge - v.dirty[j].end = std::cmp::max(v.dirty[j].end, v.dirty[i].end); - } else { - // next - j += 1; + fn update_vertex_buffer( + &mut self, + queue: QueueId, + factory: &Factory, + ) -> Result<(), UploadError> { + let dirty_bytes_total = self + .vertices + .iter_mut() + .flat_map(|v| { + v.dirty.sort_by_key(|d| d.start); + + let mut j = 0; + for i in 1..v.dirty.len() { + if v.dirty[j].end >= v.dirty[i].start { + // merge + v.dirty[j].end = std::cmp::max(v.dirty[j].end, v.dirty[i].end); + } else { + // next + j += 1; + } } - } - v.dirty.truncate(j); - v.dirty.iter().map(|d| d.end - d.start) - }).sum(); + v.dirty.truncate(j); + v.dirty.iter().map(|d| d.end - d.start) + }) + .sum(); if dirty_bytes_total == 0 { return Ok(()); @@ -396,8 +439,12 @@ where let size = d.end - d.start; unsafe { debug_assert!(v.bytes.len() >= d.end); // "Ensured by `len` calculation - // `staging_slice` size is sum of all `size`s in this loop + alignment. - std::ptr::copy_nonoverlapping(v.bytes.as_ptr().add(d.start), staging_slice.as_mut_ptr().add(offset) as *mut u8, size); + // `staging_slice` size is sum of all `size`s in this loop + alignment. + std::ptr::copy_nonoverlapping( + v.bytes.as_ptr().add(d.start), + staging_slice.as_mut_ptr().add(offset) as *mut u8, + size, + ); } offset += size; } @@ -439,7 +486,11 @@ where Ok(()) } - fn build_index_buffer(&mut self, queue: QueueId, factory: &Factory) -> Result<(), UploadError> { + fn build_index_buffer( + &mut self, + queue: QueueId, + factory: &Factory, + ) -> Result<(), UploadError> { if let Some(indices) = &mut self.indices { let len = (indices.bytes.len() / index_stride(indices.ty)) as u32; let mut buffer = factory @@ -465,21 +516,28 @@ where )?; } - self.mesh.index_buffer = Some(IndexBuffer { buffer, ty: indices.ty }); + self.mesh.index_buffer = Some(IndexBuffer { + buffer, + ty: indices.ty, + }); self.mesh.len = len; } Ok(()) } - fn update_index_buffer(&mut self, queue: QueueId, factory: &Factory) -> Result<(), UploadError> { + fn update_index_buffer( + &mut self, + queue: QueueId, + factory: &Factory, + ) -> Result<(), UploadError> { let indices = self.indices.as_mut().unwrap(); let index_buffer = self.mesh.index_buffer.as_mut().unwrap(); indices.dirty.sort_by_key(|d| d.start); let mut j = 0; - for i in 1 .. indices.dirty.len() { + for i in 1..indices.dirty.len() { if indices.dirty[j].end >= indices.dirty[i].start { // merge indices.dirty[j].end = std::cmp::max(indices.dirty[j].end, indices.dirty[i].end); @@ -491,7 +549,7 @@ where indices.dirty.truncate(j); let dirty_bytes_total = indices.dirty.iter().map(|d| d.end - d.start).sum(); - + if dirty_bytes_total == 0 { return Ok(()); } @@ -521,8 +579,12 @@ where let size = d.end - d.start; unsafe { debug_assert!(indices.bytes.len() >= d.end); // "Ensured by `len` calculation - // `staging_slice` size is sum of all `size`s in this loop + alignment. - std::ptr::copy_nonoverlapping(indices.bytes.as_ptr().add(d.start), staging_slice.as_mut_ptr().add(offset) as *mut u8, size); + // `staging_slice` size is sum of all `size`s in this loop + alignment. + std::ptr::copy_nonoverlapping( + indices.bytes.as_ptr().add(d.start), + staging_slice.as_mut_ptr().add(offset) as *mut u8, + size, + ); } offset += size; } @@ -560,5 +622,3 @@ where Ok(()) } } - - diff --git a/mesh/src/lib.rs b/mesh/src/lib.rs index 5c5b0a30..ab67ce13 100644 --- a/mesh/src/lib.rs +++ b/mesh/src/lib.rs @@ -22,14 +22,14 @@ use rendy_factory as factory; use rendy_memory as memory; use rendy_resource as resource; -mod format; mod builder; -mod r#static; mod dynamic; +mod format; +mod r#static; -pub use rendy_core::types::vertex::*; pub use crate::format::*; -pub use crate::{builder::*, r#static::*, dynamic::*}; +pub use crate::{builder::*, dynamic::*, r#static::*}; +pub use rendy_core::types::vertex::*; fn index_stride(ty: rendy_core::hal::IndexType) -> usize { match ty { @@ -101,4 +101,3 @@ fn is_slice_sorted_by_key<'a, T, K: Ord>(slice: &'a [T], f: impl Fn(&'a T) -> K) } true } - diff --git a/mesh/src/static.rs b/mesh/src/static.rs index d259beaa..3ac7c090 100644 --- a/mesh/src/static.rs +++ b/mesh/src/static.rs @@ -1,11 +1,10 @@ - use crate::{ - Incompatible, is_slice_sorted, is_slice_sorted_by_key, is_compatible, + builder::MeshBuilder, command::{EncoderCommon, Graphics, RenderPassEncoder, Supports}, core::hal::Backend, + is_compatible, is_slice_sorted, is_slice_sorted_by_key, resource::{Buffer, Escape}, - VertexFormat, - builder::MeshBuilder, + Incompatible, VertexFormat, }; /// Helper function to find buffer with compatible format. @@ -37,7 +36,6 @@ pub(crate) struct IndexBuffer { pub(crate) ty: rendy_core::hal::IndexType, } - /// Single mesh is a collection of buffer ranges that provides available attributes. /// Usually exactly one mesh is used per draw call. #[derive(Debug)] @@ -100,7 +98,9 @@ where } let buffer = self.vertex_buffer.raw(); - Ok(vertex.into_iter().map(move |offset| (buffer, offset as u64))) + Ok(vertex + .into_iter() + .map(move |offset| (buffer, offset as u64))) } /// Bind buffers to specified attribute locations. @@ -139,11 +139,7 @@ where unsafe { match self.index_buffer.as_ref() { Some(index_buffer) => { - encoder.bind_index_buffer( - index_buffer.buffer.raw(), - 0, - index_buffer.ty, - ); + encoder.bind_index_buffer(index_buffer.buffer.raw(), 0, index_buffer.ty); encoder.bind_vertex_buffers(first_binding, vertex_iter); encoder.draw_indexed(0..self.len, 0, instance_range); }