From 40eeea224b8b93392aff3968a8eeb0d4dc55d6b6 Mon Sep 17 00:00:00 2001 From: zawz Date: Fri, 2 Aug 2024 09:10:28 +0200 Subject: [PATCH] 021: boxes --- shaders/shader.vert | 11 +- src/{instance => }/error.rs | 4 +- src/instance/buffer.rs | 89 +++++++++++-- src/instance/mod.rs | 105 +++++++-------- src/main.rs | 8 +- src/model/cube.rs | 39 ++++++ src/model/mod.rs | 232 ++++++++++++++++++++++++++++++++ src/model/model.rs | 255 ++++++++++++++++++++++++++++++++++++ 8 files changed, 663 insertions(+), 80 deletions(-) rename src/{instance => }/error.rs (85%) create mode 100644 src/model/cube.rs create mode 100644 src/model/mod.rs create mode 100644 src/model/model.rs diff --git a/shaders/shader.vert b/shaders/shader.vert index 11e7a6f..228c9e5 100644 --- a/shaders/shader.vert +++ b/shaders/shader.vert @@ -1,13 +1,12 @@ #version 450 -layout(location = 0) in vec4 position; -layout(location = 1) in float size; -layout(location = 2) in vec4 colour; +layout(location = 0) in vec3 position; +layout(location = 1) in vec3 position_offset; +layout(location = 2) in vec3 colour; layout(location = 0) out vec4 colourdata_for_the_fragmentshader; void main() { - gl_PointSize = size; - gl_Position = position; - colourdata_for_the_fragmentshader = colour; + gl_Position = vec4(position + position_offset, 1.0); + colourdata_for_the_fragmentshader = vec4(colour, 1.0); } diff --git a/src/instance/error.rs b/src/error.rs similarity index 85% rename from src/instance/error.rs rename to src/error.rs index 6d52945..8054d30 100644 --- a/src/instance/error.rs +++ b/src/error.rs @@ -1,5 +1,7 @@ use ash::vk; +use crate::instance::buffer::BufferError; + #[derive(Debug, thiserror::Error)] pub enum EngineError { #[error(transparent)] @@ -13,5 +15,5 @@ pub enum EngineError { #[error(transparent)] RWHError(#[from] winit::raw_window_handle::HandleError), #[error(transparent)] - AllocationError(#[from] gpu_allocator::AllocationError), + BufferError(#[from] BufferError), } diff --git a/src/instance/buffer.rs b/src/instance/buffer.rs index 2bb0bb9..636fa6f 100644 --- a/src/instance/buffer.rs +++ b/src/instance/buffer.rs @@ -6,8 +6,6 @@ use gpu_allocator::vulkan::Allocator as GpuAllocator; use gpu_allocator::vulkan::AllocatorCreateDesc; use gpu_allocator::MemoryLocation; -use super::EngineError; - pub struct BufferAllocator { pub allocator: GpuAllocator, } @@ -15,6 +13,21 @@ pub struct BufferAllocator { pub struct Buffer { pub allocation: Option, pub buffer: vk::Buffer, + pub size: u64, + pub flags: vk::BufferUsageFlags, + pub location: MemoryLocation, +} + +#[derive(Debug, thiserror::Error)] +pub enum BufferError { + #[error(transparent)] + AllocationError(#[from] gpu_allocator::AllocationError), + #[error("buffer memory is not visible to host")] + NonVisibleMemory, + #[error("data is larger than buffer")] + BufferOverflow, + #[error("buffer was unexpectedly unallocated")] + UnexpectedUnallocatedBuffer, } impl BufferAllocator { @@ -22,7 +35,7 @@ impl BufferAllocator { ash_instance: &ash::Instance, physical_device: vk::PhysicalDevice, device: &ash::Device, - ) -> Result { + ) -> Result { let allocator = GpuAllocator::new(&AllocatorCreateDesc { instance: ash_instance.clone(), device: device.clone(), @@ -40,7 +53,7 @@ impl BufferAllocator { size: u64, flags: vk::BufferUsageFlags, location: MemoryLocation, - ) -> Result { + ) -> Result { let vk_info = vk::BufferCreateInfo::default().size(size).usage(flags); let buffer = unsafe { device.create_buffer(&vk_info, None) }.unwrap(); @@ -63,13 +76,24 @@ impl BufferAllocator { Ok(Buffer { allocation: Some(allocation), buffer, + size, + flags, + location, }) } - pub fn free_buffer_allocation(&mut self, device: &ash::Device, buffer: &mut Buffer) { - let allocation = buffer.allocation.take(); - self.allocator.free(allocation.unwrap()).unwrap(); + pub fn free_buffer_allocation( + &mut self, + device: &ash::Device, + buffer: &mut Buffer, + ) -> Result<(), BufferError> { + let allocation = buffer + .allocation + .take() + .ok_or(BufferError::UnexpectedUnallocatedBuffer)?; + self.allocator.free(allocation)?; unsafe { device.destroy_buffer(buffer.buffer, None) }; + Ok(()) } } @@ -82,9 +106,54 @@ impl Buffer { } } - pub unsafe fn fill(&mut self, data: &[T]) { - let slice = self.mapped_slice_mut().unwrap(); + pub fn resize( + &mut self, + allocator: &mut BufferAllocator, + device: &ash::Device, + size: u64, + ) -> Result<(), BufferError> { + allocator.free_buffer_allocation(device, self)?; + let newbuffer = allocator.create_buffer(device, size, self.flags, self.location)?; + *self = newbuffer; + Ok(()) + } + + /// Same as resize() but only if it's bigger + pub fn grow( + &mut self, + allocator: &mut BufferAllocator, + device: &ash::Device, + size: u64, + ) -> Result<(), BufferError> { + if size > self.size { + self.resize(allocator, device, size) + } else { + Ok(()) + } + } + + /// Try to grow the buffer if necessary, then fill + pub fn grow_fill( + &mut self, + allocator: &mut BufferAllocator, + device: &ash::Device, + data: &[T], + ) -> Result<(), BufferError> { + let bytes_to_write = data.len() * std::mem::size_of::(); + self.grow(allocator, device, bytes_to_write as u64)?; + self.fill(data) + } + + pub fn fill(&mut self, data: &[T]) -> Result<(), BufferError> { + let bytes_to_write = data.len() * std::mem::size_of::(); + if bytes_to_write as u64 > self.size { + return Err(BufferError::BufferOverflow); + } + let slice = self + .mapped_slice_mut() + .ok_or(BufferError::NonVisibleMemory)?; let inner_buffer = slice.as_ptr() as *mut T; - inner_buffer.copy_from_nonoverlapping(data.as_ptr(), data.len()); + unsafe { inner_buffer.copy_from_nonoverlapping(data.as_ptr(), data.len()) }; + Ok(()) } } diff --git a/src/instance/mod.rs b/src/instance/mod.rs index 76236d1..0f9dc52 100644 --- a/src/instance/mod.rs +++ b/src/instance/mod.rs @@ -1,11 +1,10 @@ use std::ffi::{c_char, CString}; use ash::vk; -use buffer::{Buffer, BufferAllocator}; +use buffer::BufferAllocator; pub mod buffer; pub mod debug; -pub mod error; pub mod pipeline; pub mod pools; pub mod queue; @@ -13,7 +12,13 @@ pub mod surface; pub mod swapchain; pub mod window; -pub use error::EngineError; +use crate::{ + model::{ + cube::{self, Cube}, + Model, + }, + EngineError, +}; pub use debug::DebugInstance; pub use pipeline::Pipeline; @@ -46,7 +51,7 @@ pub struct EngineInstance { pub pools: Pools, pub commandbuffers: Vec, pub allocator: std::mem::ManuallyDrop, - pub buffers: Vec, + pub models: Vec, pub framecount: FrameCounter, } @@ -118,70 +123,40 @@ impl EngineInstance { let renderpass = Self::init_renderpass(&device, surface_format.format)?; swapchain.create_framebuffers(&device, renderpass)?; - let pbuflen1: u32 = 16; - let pbuflen2: u32 = 20; - let nvertex: u32 = 6; - let ninstance: u32 = 1; - log::debug!("Create allocator"); - let mut allocator = BufferAllocator::init(&ash_instance, physical_device, &device)?; - let mut buffer1 = allocator.create_buffer( - &device, - pbuflen1 as u64 * nvertex as u64, - vk::BufferUsageFlags::VERTEX_BUFFER, - gpu_allocator::MemoryLocation::CpuToGpu, - )?; - unsafe { - buffer1.fill(&[ - 0.5f32, 0.0f32, 0.0f32, 1.0f32, 0.0f32, 0.2f32, 0.0f32, 1.0f32, -0.5f32, 0.0f32, - 0.0f32, 1.0f32, -0.9f32, -0.9f32, 0.0f32, 1.0f32, 0.3f32, -0.8f32, 0.0f32, 1.0f32, - 0.0f32, -0.6f32, 0.0f32, 1.0f32, - ]) - }; - let mut buffer2 = allocator.create_buffer( - &device, - pbuflen2 as u64 * nvertex as u64, - vk::BufferUsageFlags::VERTEX_BUFFER, - gpu_allocator::MemoryLocation::CpuToGpu, - )?; - unsafe { - buffer2.fill(&[ - 1.0f32, 0.0f32, 1.0f32, 0.0f32, 1.0f32, 1.0f32, 0.0f32, 1.0f32, 0.0f32, 1.0f32, - 1.0f32, 0.0f32, 1.0f32, 0.0f32, 1.0f32, 1.0f32, 0.8f32, 0.7f32, 0.0f32, 1.0f32, - 1.0f32, 1.0f32, 0.0f32, 0.0f32, 1.0f32, 1.0f32, 0.0f32, 0.0f32, 1.0f32, 1.0f32, - ]) - }; + let mut allocator: BufferAllocator = + BufferAllocator::init(&ash_instance, physical_device, &device)?; let vertex_attrib_descs = [ vk::VertexInputAttributeDescription { binding: 0, location: 0, offset: 0, - format: vk::Format::R32G32B32A32_SFLOAT, + format: vk::Format::R32G32B32_SFLOAT, }, vk::VertexInputAttributeDescription { binding: 1, location: 1, offset: 0, - format: vk::Format::R32_SFLOAT, + format: vk::Format::R32G32B32_SFLOAT, }, vk::VertexInputAttributeDescription { binding: 1, location: 2, - offset: 4, - format: vk::Format::R32G32B32A32_SFLOAT, + offset: 12, + format: vk::Format::R32G32B32_SFLOAT, }, ]; let vertex_binding_descs = [ vk::VertexInputBindingDescription { binding: 0, - stride: pbuflen1, + stride: 12, input_rate: vk::VertexInputRate::VERTEX, }, vk::VertexInputBindingDescription { binding: 1, - stride: pbuflen2, - input_rate: vk::VertexInputRate::VERTEX, + stride: 24, + input_rate: vk::VertexInputRate::INSTANCE, }, ]; @@ -201,6 +176,23 @@ impl EngineInstance { log::debug!("Create command buffers"); let commandbuffers = pools.create_commandbuffers(&device, swapchain.framebuffers.len())?; + + let mut cube = cube::Cube::cube(); + cube.insert_visibly(cube::InstanceData { + position: [0.0, -0.25, 0.0], + colour: [1.0, 0.0, 0.0], + }); + cube.insert_visibly(cube::InstanceData { + position: [0.0, 0.0, 0.0], + colour: [0.6, 0.5, 0.0], + }); + cube.insert_visibly(cube::InstanceData { + position: [0.0, 0.25, 0.0], + colour: [0.0, 0.5, 0.0], + }); + cube.update_vertexbuffer(&mut allocator, &device)?; + cube.update_instancebuffer(&mut allocator, &device)?; + let models = vec![cube]; for (i, &commandbuffer) in commandbuffers.iter().enumerate() { Self::fill_commandbuffer( &device, @@ -209,10 +201,7 @@ impl EngineInstance { &swapchain, i, &pipeline, - buffer1.buffer, - buffer2.buffer, - nvertex, - ninstance, + &models, )?; } @@ -234,7 +223,7 @@ impl EngineInstance { pools, commandbuffers, allocator: std::mem::ManuallyDrop::new(allocator), - buffers: vec![buffer1, buffer2], + models, framecount: FrameCounter::new(), }) } @@ -247,8 +236,9 @@ impl EngineInstance { self.pools.cleanup(&self.device); self.pipeline.cleanup(&self.device); - for buffer in &mut self.buffers { - self.allocator.free_buffer_allocation(&self.device, buffer); + for m in &mut self.models { + m.cleanup_buffers(&mut self.allocator, &self.device) + .unwrap(); } std::mem::ManuallyDrop::drop(&mut self.allocator); @@ -423,24 +413,19 @@ impl EngineInstance { Ok(renderpass) } - fn fill_commandbuffer( + fn fill_commandbuffer( device: &ash::Device, commandbuffer: vk::CommandBuffer, renderpass: vk::RenderPass, swapchain: &Swapchain, framebuffer_index: usize, pipeline: &Pipeline, - // buffers: &[vk::Buffer], - buffer1: vk::Buffer, - buffer2: vk::Buffer, - vertex_count: u32, - instance_count: u32, + models: &[Model], ) -> Result<(), vk::Result> { let commandbuffer_begininfo = vk::CommandBufferBeginInfo::default(); unsafe { device.begin_command_buffer(commandbuffer, &commandbuffer_begininfo)?; } - let clearvalues = [vk::ClearValue { color: vk::ClearColorValue { float32: [0.0, 0.0, 0.08, 1.0], @@ -466,9 +451,9 @@ impl EngineInstance { vk::PipelineBindPoint::GRAPHICS, pipeline.pipeline, ); - device.cmd_bind_vertex_buffers(commandbuffer, 0, &[buffer1], &[0]); - device.cmd_bind_vertex_buffers(commandbuffer, 1, &[buffer2], &[0]); - device.cmd_draw(commandbuffer, vertex_count, instance_count, 0, 0); + for m in models { + m.draw(device, commandbuffer); + } device.cmd_end_render_pass(commandbuffer); device.end_command_buffer(commandbuffer)?; } diff --git a/src/main.rs b/src/main.rs index ad8b5a5..e41e903 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,14 +1,16 @@ +pub mod error; +pub mod frame_counter; pub mod instance; +pub mod model; pub mod render; -pub mod frame_counter; - +pub use error::EngineError; pub use instance::EngineInstance; use instance::{window::Resolution, Window}; use ash::vk; -pub const WINDOW_WIDTH: u32 = 800; +pub const WINDOW_WIDTH: u32 = 600; pub const WINDOW_HEIGHT: u32 = 600; pub const DEFAULT_RES: Resolution = Resolution { diff --git a/src/model/cube.rs b/src/model/cube.rs new file mode 100644 index 0000000..23462cd --- /dev/null +++ b/src/model/cube.rs @@ -0,0 +1,39 @@ +use super::Model; + +#[repr(C)] +pub struct InstanceData { + pub position: [f32; 3], + pub colour: [f32; 3], +} + +pub type Cube = Model<[f32; 3], InstanceData>; + +impl Cube { + pub fn cube() -> Self { + let lbf = [-0.1, 0.1, 0.0]; //lbf: left-bottom-front + let lbb = [-0.1, 0.1, 0.1]; + let ltf = [-0.1, -0.1, 0.0]; + let ltb = [-0.1, -0.1, 0.1]; + let rbf = [0.1, 0.1, 0.0]; + let rbb = [0.1, 0.1, 0.1]; + let rtf = [0.1, -0.1, 0.0]; + let rtb = [0.1, -0.1, 0.1]; + Model { + vertexdata: vec![ + lbf, lbb, rbb, lbf, rbb, rbf, //bottom + ltf, rtb, ltb, ltf, rtf, rtb, //top + lbf, rtf, ltf, lbf, rbf, rtf, //front + lbb, ltb, rtb, lbb, rtb, rbb, //back + lbf, ltf, lbb, lbb, ltf, ltb, //left + rbf, rbb, rtf, rbb, rtb, rtf, //right + ], + handle_to_index: std::collections::HashMap::new(), + handles: Vec::new(), + instances: Vec::new(), + first_invisible: 0, + next_handle: 0, + vertexbuffer: None, + instancebuffer: None, + } + } +} diff --git a/src/model/mod.rs b/src/model/mod.rs new file mode 100644 index 0000000..a21524e --- /dev/null +++ b/src/model/mod.rs @@ -0,0 +1,232 @@ +use ash::vk; + +pub mod cube; + +use crate::{ + instance::buffer::{Buffer, BufferAllocator, BufferError}, + EngineError, +}; + +#[derive(Debug, Clone)] +pub struct InvalidHandle; +impl std::fmt::Display for InvalidHandle { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f, "invalid handle") + } +} +impl std::error::Error for InvalidHandle { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + None + } +} + +pub struct Model { + vertexdata: Vec, + handle_to_index: std::collections::HashMap, + handles: Vec, + instances: Vec, + first_invisible: usize, + next_handle: usize, + vertexbuffer: Option, + instancebuffer: Option, +} + +impl Model { + pub fn get(&self, handle: usize) -> Option<&I> { + if let Some(&index) = self.handle_to_index.get(&handle) { + self.instances.get(index) + } else { + None + } + } + pub fn get_mut(&mut self, handle: usize) -> Option<&mut I> { + if let Some(&index) = self.handle_to_index.get(&handle) { + self.instances.get_mut(index) + } else { + None + } + } + + pub fn swap_by_handle(&mut self, handle1: usize, handle2: usize) -> Result<(), InvalidHandle> { + if handle1 == handle2 { + return Ok(()); + } + if let (Some(&index1), Some(&index2)) = ( + self.handle_to_index.get(&handle1), + self.handle_to_index.get(&handle2), + ) { + self.handles.swap(index1, index2); + self.instances.swap(index1, index2); + self.handle_to_index.insert(index1, handle2); + self.handle_to_index.insert(index2, handle1); + Ok(()) + } else { + Err(InvalidHandle) + } + } + fn swap_by_index(&mut self, index1: usize, index2: usize) { + if index1 == index2 { + return; + } + let handle1 = self.handles[index1]; + let handle2 = self.handles[index2]; + self.handles.swap(index1, index2); + self.instances.swap(index1, index2); + self.handle_to_index.insert(index1, handle2); + self.handle_to_index.insert(index2, handle1); + } + + pub fn is_visible(&self, handle: usize) -> Result { + if let Some(index) = self.handle_to_index.get(&handle) { + Ok(index < &self.first_invisible) + } else { + Err(InvalidHandle) + } + } + pub fn make_visible(&mut self, handle: usize) -> Result<(), InvalidHandle> { + //if already visible: do nothing + if let Some(&index) = self.handle_to_index.get(&handle) { + if index < self.first_invisible { + return Ok(()); + } + //else: move to position first_invisible and increase value of first_invisible + self.swap_by_index(index, self.first_invisible); + self.first_invisible += 1; + Ok(()) + } else { + Err(InvalidHandle) + } + } + pub fn make_invisible(&mut self, handle: usize) -> Result<(), InvalidHandle> { + //if already invisible: do nothing + if let Some(&index) = self.handle_to_index.get(&handle) { + if index >= self.first_invisible { + return Ok(()); + } + //else: move to position before first_invisible and decrease value of first_invisible + self.swap_by_index(index, self.first_invisible - 1); + self.first_invisible -= 1; + Ok(()) + } else { + Err(InvalidHandle) + } + } + + pub fn insert(&mut self, element: I) -> usize { + let handle = self.next_handle; + self.next_handle += 1; + let index = self.instances.len(); + self.instances.push(element); + self.handles.push(handle); + self.handle_to_index.insert(handle, index); + handle + } + pub fn insert_visibly(&mut self, element: I) -> usize { + let new_handle = self.insert(element); + self.make_visible(new_handle).ok(); //can't go wrong, see previous line + new_handle + } + pub fn remove(&mut self, handle: usize) -> Result { + if let Some(&index) = self.handle_to_index.get(&handle) { + if index < self.first_invisible { + self.swap_by_index(index, self.first_invisible - 1); + self.first_invisible -= 1; + } + self.swap_by_index(self.first_invisible, self.instances.len() - 1); + self.handles.pop(); + self.handle_to_index.remove(&handle); + //must be Some(), otherwise we couldn't have found an index + Ok(self.instances.pop().unwrap()) + } else { + Err(InvalidHandle) + } + } + + pub fn update_vertexbuffer( + &mut self, + allocator: &mut BufferAllocator, + device: &ash::Device, + ) -> Result<(), BufferError> { + if let Some(buffer) = &mut self.vertexbuffer { + buffer.grow_fill(allocator, device, &self.vertexdata)?; + Ok(()) + } else { + let bytes = (self.vertexdata.len() * std::mem::size_of::()) as u64; + let mut buffer = allocator.create_buffer( + device, + bytes, + vk::BufferUsageFlags::VERTEX_BUFFER, + gpu_allocator::MemoryLocation::CpuToGpu, + )?; + buffer.grow_fill(allocator, device, &self.vertexdata)?; + self.vertexbuffer = Some(buffer); + Ok(()) + } + } + + pub fn update_instancebuffer( + &mut self, + allocator: &mut BufferAllocator, + device: &ash::Device, + ) -> Result<(), EngineError> { + if let Some(buffer) = &mut self.instancebuffer { + buffer.grow_fill(allocator, device, &self.instances[0..self.first_invisible])?; + Ok(()) + } else { + let bytes = (self.first_invisible * std::mem::size_of::()) as u64; + let mut buffer = allocator.create_buffer( + device, + bytes, + vk::BufferUsageFlags::VERTEX_BUFFER, + gpu_allocator::MemoryLocation::CpuToGpu, + )?; + buffer.grow_fill(allocator, device, &self.instances[0..self.first_invisible])?; + self.instancebuffer = Some(buffer); + Ok(()) + } + } + + pub fn draw(&self, logical_device: &ash::Device, commandbuffer: vk::CommandBuffer) { + if let Some(vertexbuffer) = &self.vertexbuffer { + if let Some(instancebuffer) = &self.instancebuffer { + if self.first_invisible > 0 { + unsafe { + logical_device.cmd_bind_vertex_buffers( + commandbuffer, + 0, + &[vertexbuffer.buffer], + &[0], + ); + logical_device.cmd_bind_vertex_buffers( + commandbuffer, + 1, + &[instancebuffer.buffer], + &[0], + ); + logical_device.cmd_draw( + commandbuffer, + self.vertexdata.len() as u32, + self.first_invisible as u32, + 0, + 0, + ); + } + } + } + } + } + + pub fn cleanup_buffers( + &mut self, + allocator: &mut BufferAllocator, + device: &ash::Device, + ) -> Result<(), BufferError> { + if let Some(b) = &mut self.vertexbuffer { + allocator.free_buffer_allocation(device, b)?; + } + if let Some(b) = &mut self.instancebuffer { + allocator.free_buffer_allocation(device, b)?; + } + Ok(()) + } +} diff --git a/src/model/model.rs b/src/model/model.rs new file mode 100644 index 0000000..a5c256f --- /dev/null +++ b/src/model/model.rs @@ -0,0 +1,255 @@ +use ash::vk; + +use crate::{ + instance::buffer::{Buffer, BufferAllocator, BufferError}, + EngineError, +}; + +#[derive(Debug, Clone)] +pub struct InvalidHandle; +impl std::fmt::Display for InvalidHandle { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f, "invalid handle") + } +} +impl std::error::Error for InvalidHandle { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + None + } +} + +pub struct Model { + vertexdata: Vec, + handle_to_index: std::collections::HashMap, + handles: Vec, + instances: Vec, + first_invisible: usize, + next_handle: usize, + vertexbuffer: Option, + instancebuffer: Option, +} + +impl Model { + pub fn get(&self, handle: usize) -> Option<&I> { + if let Some(&index) = self.handle_to_index.get(&handle) { + self.instances.get(index) + } else { + None + } + } + pub fn get_mut(&mut self, handle: usize) -> Option<&mut I> { + if let Some(&index) = self.handle_to_index.get(&handle) { + self.instances.get_mut(index) + } else { + None + } + } + + pub fn swap_by_handle(&mut self, handle1: usize, handle2: usize) -> Result<(), InvalidHandle> { + if handle1 == handle2 { + return Ok(()); + } + if let (Some(&index1), Some(&index2)) = ( + self.handle_to_index.get(&handle1), + self.handle_to_index.get(&handle2), + ) { + self.handles.swap(index1, index2); + self.instances.swap(index1, index2); + self.handle_to_index.insert(index1, handle2); + self.handle_to_index.insert(index2, handle1); + Ok(()) + } else { + Err(InvalidHandle) + } + } + fn swap_by_index(&mut self, index1: usize, index2: usize) { + if index1 == index2 { + return; + } + let handle1 = self.handles[index1]; + let handle2 = self.handles[index2]; + self.handles.swap(index1, index2); + self.instances.swap(index1, index2); + self.handle_to_index.insert(index1, handle2); + self.handle_to_index.insert(index2, handle1); + } + + pub fn is_visible(&self, handle: usize) -> Result { + if let Some(index) = self.handle_to_index.get(&handle) { + Ok(index < &self.first_invisible) + } else { + Err(InvalidHandle) + } + } + pub fn make_visible(&mut self, handle: usize) -> Result<(), InvalidHandle> { + //if already visible: do nothing + if let Some(&index) = self.handle_to_index.get(&handle) { + if index < self.first_invisible { + return Ok(()); + } + //else: move to position first_invisible and increase value of first_invisible + self.swap_by_index(index, self.first_invisible); + self.first_invisible += 1; + Ok(()) + } else { + Err(InvalidHandle) + } + } + pub fn make_invisible(&mut self, handle: usize) -> Result<(), InvalidHandle> { + //if already invisible: do nothing + if let Some(&index) = self.handle_to_index.get(&handle) { + if index >= self.first_invisible { + return Ok(()); + } + //else: move to position before first_invisible and decrease value of first_invisible + self.swap_by_index(index, self.first_invisible - 1); + self.first_invisible -= 1; + Ok(()) + } else { + Err(InvalidHandle) + } + } + + pub fn insert(&mut self, element: I) -> usize { + let handle = self.next_handle; + self.next_handle += 1; + let index = self.instances.len(); + self.instances.push(element); + self.handles.push(handle); + self.handle_to_index.insert(handle, index); + handle + } + pub fn insert_visibly(&mut self, element: I) -> usize { + let new_handle = self.insert(element); + self.make_visible(new_handle).ok(); //can't go wrong, see previous line + new_handle + } + pub fn remove(&mut self, handle: usize) -> Result { + if let Some(&index) = self.handle_to_index.get(&handle) { + if index < self.first_invisible { + self.swap_by_index(index, self.first_invisible - 1); + self.first_invisible -= 1; + } + self.swap_by_index(self.first_invisible, self.instances.len() - 1); + self.handles.pop(); + self.handle_to_index.remove(&handle); + //must be Some(), otherwise we couldn't have found an index + Ok(self.instances.pop().unwrap()) + } else { + Err(InvalidHandle) + } + } + + pub fn update_vertexbuffer( + &mut self, + allocator: &mut BufferAllocator, + device: &ash::Device, + ) -> Result<(), BufferError> { + if let Some(buffer) = &mut self.vertexbuffer { + buffer.grow_fill(allocator, device, &self.vertexdata)?; + Ok(()) + } else { + let bytes = (self.vertexdata.len() * std::mem::size_of::()) as u64; + let mut buffer = allocator.create_buffer( + device, + bytes, + vk::BufferUsageFlags::VERTEX_BUFFER, + gpu_allocator::MemoryLocation::CpuToGpu, + )?; + buffer.grow_fill(allocator, device, &self.vertexdata)?; + self.vertexbuffer = Some(buffer); + Ok(()) + } + } + + pub fn update_instancebuffer( + &mut self, + allocator: &mut BufferAllocator, + device: &ash::Device, + ) -> Result<(), EngineError> { + if let Some(buffer) = &mut self.instancebuffer { + buffer.grow_fill(allocator, device, &self.instances[0..self.first_invisible])?; + Ok(()) + } else { + let bytes = (self.first_invisible * std::mem::size_of::()) as u64; + let mut buffer = allocator.create_buffer( + device, + bytes, + vk::BufferUsageFlags::VERTEX_BUFFER, + gpu_allocator::MemoryLocation::CpuToGpu, + )?; + buffer.grow_fill(allocator, device, &self.instances[0..self.first_invisible])?; + self.instancebuffer = Some(buffer); + Ok(()) + } + } + + pub fn draw(&self, logical_device: &ash::Device, commandbuffer: vk::CommandBuffer) { + if let Some(vertexbuffer) = &self.vertexbuffer { + if let Some(instancebuffer) = &self.instancebuffer { + if self.first_invisible > 0 { + unsafe { + logical_device.cmd_bind_vertex_buffers( + commandbuffer, + 0, + &[vertexbuffer.buffer], + &[0], + ); + logical_device.cmd_bind_vertex_buffers( + commandbuffer, + 1, + &[instancebuffer.buffer], + &[0], + ); + logical_device.cmd_draw( + commandbuffer, + self.vertexdata.len() as u32, + self.first_invisible as u32, + 0, + 0, + ); + } + } + } + } + } + + pub fn cleanup_buffers(&mut self, allocator: &mut BufferAllocator, device: &ash::Device) { + if let Some(b) = &mut self.vertexbuffer { + allocator.free_buffer_allocation(device, b); + } + if let Some(b) = &mut self.instancebuffer { + allocator.free_buffer_allocation(device, b); + } + } +} + +impl Model<[f32; 3], [f32; 6]> { + pub fn cube() -> Model<[f32; 3], [f32; 6]> { + let lbf = [-0.1, 0.1, 0.0]; //lbf: left-bottom-front + let lbb = [-0.1, 0.1, 0.1]; + let ltf = [-0.1, -0.1, 0.0]; + let ltb = [-0.1, -0.1, 0.1]; + let rbf = [0.1, 0.1, 0.0]; + let rbb = [0.1, 0.1, 0.1]; + let rtf = [0.1, -0.1, 0.0]; + let rtb = [0.1, -0.1, 0.1]; + Model { + vertexdata: vec![ + lbf, lbb, rbb, lbf, rbb, rbf, //bottom + ltf, rtb, ltb, ltf, rtf, rtb, //top + lbf, rtf, ltf, lbf, rbf, rtf, //front + lbb, ltb, rtb, lbb, rtb, rbb, //back + lbf, ltf, lbb, lbb, ltf, ltb, //left + rbf, rbb, rtf, rbb, rtb, rtf, //right + ], + handle_to_index: std::collections::HashMap::new(), + handles: Vec::new(), + instances: Vec::new(), + first_invisible: 0, + next_handle: 0, + vertexbuffer: None, + instancebuffer: None, + } + } +}