diff options
author | tcmal <me@aria.rip> | 2024-08-25 17:44:21 +0100 |
---|---|---|
committer | tcmal <me@aria.rip> | 2024-08-25 17:44:21 +0100 |
commit | 2f112ab34ac1458b038598f4d7ef6638df463dc6 (patch) | |
tree | c0100341b31276297fc9d2992797c5ca18cd2d99 /stockton-render | |
parent | 27760ec1ca7a93877b2b015a0a2e9db87de4204c (diff) |
feat(render): depth buffer and refactors
Diffstat (limited to 'stockton-render')
-rw-r--r-- | stockton-render/src/draw/context.rs | 577 | ||||
-rw-r--r-- | stockton-render/src/draw/mod.rs | 2 | ||||
-rw-r--r-- | stockton-render/src/draw/target.rs | 407 | ||||
-rw-r--r-- | stockton-render/src/draw/texture/chunk.rs | 20 | ||||
-rw-r--r-- | stockton-render/src/draw/texture/image.rs | 216 | ||||
-rw-r--r-- | stockton-render/src/draw/texture/loader.rs | 5 | ||||
-rw-r--r-- | stockton-render/src/draw/texture/mod.rs | 4 | ||||
-rw-r--r-- | stockton-render/src/error.rs | 3 |
8 files changed, 714 insertions, 520 deletions
diff --git a/stockton-render/src/draw/context.rs b/stockton-render/src/draw/context.rs index eacfc44..9da5789 100644 --- a/stockton-render/src/draw/context.rs +++ b/stockton-render/src/draw/context.rs @@ -17,20 +17,22 @@ //! In the end, this takes in a depth-sorted list of faces and a map file and renders them. //! You'll need something else to actually find/sort the faces though. + use std::{ mem::{ManuallyDrop, size_of}, ops::Deref, borrow::Borrow, convert::TryInto }; -use winit::window::Window; -use arrayvec::ArrayVec; +use arrayvec::ArrayVec; use hal::{ prelude::*, - queue::{Submission}, - window::SwapchainConfig + pool::CommandPoolCreateFlags }; +use log::debug; +use winit::window::Window; + use stockton_types::{Vector2, Vector3}; use stockton_levels::prelude::*; use stockton_levels::traits::faces::FaceType; @@ -40,6 +42,7 @@ use crate::{ error }; use super::{ + target::{TargetChain, SwapchainProperties}, camera::WorkingCamera, texture::TextureStore, buffer::{StagedBuffer, ModifiableBuffer} @@ -48,13 +51,6 @@ use super::{ /// Entry point name for shaders const ENTRY_NAME: &str = "main"; -/// Defines the colour range we use. -const COLOR_RANGE: hal::image::SubresourceRange = hal::image::SubresourceRange { - aspects: hal::format::Aspects::COLOR, - levels: 0..1, - layers: 0..1, -}; - /// Initial size of vertex buffer. TODO: Way of overriding this const INITIAL_VERT_SIZE: u64 = 3 * 3000; @@ -91,35 +87,8 @@ pub struct RenderingContext<'a> { /// Surface to draw to surface: ManuallyDrop<Surface>, - /// Swapchain we're targeting - swapchain: ManuallyDrop<Swapchain>, - - /// Viewport of surface - viewport: hal::pso::Viewport, - - /// The imageviews in our swapchain - imageviews: Vec<ImageView>, - - /// The framebuffers of imageviews in our swapchain - framebuffers: Vec<Framebuffer>, - - /// The frame we will draw to next - current_frame: usize, - - /// The number of frames in our swapchain, ie max pre-rendered frames possible - frames_in_flight: usize, - - // Sync objects - // TODO: Collect these together? - - /// Triggered when the image is ready to draw to - get_image: Vec<Semaphore>, - - /// Triggered when rendering is done - render_complete: Vec<Semaphore>, - - /// Triggered when the image is on screen - present_complete: Vec<Fence>, + /// Swapchain and stuff + target_chain: ManuallyDrop<TargetChain>, // Pipeline @@ -137,9 +106,6 @@ pub struct RenderingContext<'a> { /// The command pool used for our buffers cmd_pool: ManuallyDrop<CommandPool>, - /// The buffers used to draw to our frames - cmd_buffers: Vec<CommandBuffer>, - /// The queue group our buffers belong to queue_group: QueueGroup, @@ -193,8 +159,15 @@ impl<'a> RenderingContext<'a> { (gpu.device, gpu.queue_groups.pop().unwrap()) }; - // Swapchain - let (format, viewport, extent, swapchain, backbuffer) = RenderingContext::create_swapchain(&mut surface, &mut device, &adapter, None)?; + // Figure out what our swapchain will look like + let swapchain_properties = SwapchainProperties::find_best(&adapter, &surface).map_err(|_| error::CreationError::BadSurface)?; + + debug!("Detected following swapchain properties: {:?}", swapchain_properties); + + // Command pool + let mut cmd_pool = unsafe { + device.create_command_pool(queue_group.family, CommandPoolCreateFlags::RESET_INDIVIDUAL) + }.map_err(|_| error::CreationError::OutOfMemoryError)?; // Renderpass let renderpass = { @@ -205,31 +178,55 @@ impl<'a> RenderingContext<'a> { memory::Dependencies }; - let attachment = Attachment { - format: Some(format), + let img_attachment = Attachment { + format: Some(swapchain_properties.format), samples: 1, ops: AttachmentOps::new(AttachmentLoadOp::Clear, AttachmentStoreOp::Store), - stencil_ops: AttachmentOps::new(AttachmentLoadOp::DontCare, AttachmentStoreOp::DontCare), + stencil_ops: AttachmentOps::new(AttachmentLoadOp::Clear, AttachmentStoreOp::DontCare), layouts: Layout::Undefined..Layout::Present }; + let depth_attachment = Attachment { + format: Some(swapchain_properties.depth_format), + samples: 1, + ops: AttachmentOps::new(AttachmentLoadOp::Clear, AttachmentStoreOp::DontCare), + stencil_ops: AttachmentOps::new(AttachmentLoadOp::DontCare, AttachmentStoreOp::DontCare), + layouts: Layout::Undefined..Layout::DepthStencilAttachmentOptimal + }; + let subpass = SubpassDesc { colors: &[(0, Layout::ColorAttachmentOptimal)], - depth_stencil: None, + depth_stencil: Some(&(1, Layout::DepthStencilAttachmentOptimal)), inputs: &[], resolves: &[], preserves: &[] }; - let dependency = SubpassDependency { + let in_dependency = SubpassDependency { flags: Dependencies::empty(), passes: None..Some(0), - stages: PipelineStage::COLOR_ATTACHMENT_OUTPUT..PipelineStage::COLOR_ATTACHMENT_OUTPUT, + stages: PipelineStage::COLOR_ATTACHMENT_OUTPUT.. + (PipelineStage::COLOR_ATTACHMENT_OUTPUT | PipelineStage::EARLY_FRAGMENT_TESTS), accesses: Access::empty() - ..(Access::COLOR_ATTACHMENT_READ | Access::COLOR_ATTACHMENT_WRITE) + ..(Access::COLOR_ATTACHMENT_READ + | Access::COLOR_ATTACHMENT_WRITE + | Access::DEPTH_STENCIL_ATTACHMENT_READ + | Access::DEPTH_STENCIL_ATTACHMENT_WRITE) }; - unsafe { device.create_render_pass(&[attachment], &[subpass], &[dependency]) } + let out_dependency = SubpassDependency { + flags: Dependencies::empty(), + passes: Some(0)..None, + stages: PipelineStage::COLOR_ATTACHMENT_OUTPUT | PipelineStage::EARLY_FRAGMENT_TESTS.. + PipelineStage::COLOR_ATTACHMENT_OUTPUT, + accesses: (Access::COLOR_ATTACHMENT_READ + | Access::COLOR_ATTACHMENT_WRITE + | Access::DEPTH_STENCIL_ATTACHMENT_READ + | Access::DEPTH_STENCIL_ATTACHMENT_WRITE).. + Access::empty() + }; + + unsafe { device.create_render_pass(&[img_attachment, depth_attachment], &[subpass], &[in_dependency, out_dependency]) } .map_err(|_| error::CreationError::OutOfMemoryError)? }; @@ -239,6 +236,12 @@ impl<'a> RenderingContext<'a> { main_pass: &renderpass }; + // Camera + // TODO: Settings + let ratio = swapchain_properties.extent.width as f32 / swapchain_properties.extent.height as f32; + let camera = WorkingCamera::defaults(ratio); + + // Vertex and index buffers let (vert_buffer, index_buffer) = { use hal::buffer::Usage; @@ -249,70 +252,20 @@ impl<'a> RenderingContext<'a> { (vert, index) }; - // Command Pool, Buffers, imageviews, framebuffers & Sync objects - let frames_in_flight = backbuffer.len(); - let (mut cmd_pool, cmd_buffers, get_image, render_complete, present_complete, imageviews, framebuffers) = { - use hal::pool::CommandPoolCreateFlags; - use hal::command::Level; - - let mut cmd_pool = ManuallyDrop::new(unsafe { - device.create_command_pool(queue_group.family, CommandPoolCreateFlags::RESET_INDIVIDUAL) - }.map_err(|_| error::CreationError::OutOfMemoryError)?); - - let mut cmd_buffers = Vec::with_capacity(frames_in_flight); - let mut get_image = Vec::with_capacity(frames_in_flight); - let mut render_complete = Vec::with_capacity(frames_in_flight); - let mut present_complete = Vec::with_capacity(frames_in_flight); - let mut imageviews = Vec::with_capacity(frames_in_flight); - let mut framebuffers = Vec::with_capacity(frames_in_flight); - - for i in 0..frames_in_flight { - unsafe { - cmd_buffers.push(cmd_pool.allocate_one(Level::Primary)); // TODO: We can do this all at once outside the loop - } - - get_image.push(device.create_semaphore().map_err(|_| error::CreationError::SyncObjectError)?); - render_complete.push(device.create_semaphore().map_err(|_| error::CreationError::SyncObjectError)?); - present_complete.push(device.create_fence(true).map_err(|_| error::CreationError::SyncObjectError)?); - - unsafe { - use hal::image::ViewKind; - use hal::format::Swizzle; - - imageviews.push(device.create_image_view( - &backbuffer[i], - ViewKind::D2, - format, - Swizzle::NO, - COLOR_RANGE.clone(), - ).map_err(|e| error::CreationError::ImageViewError (e))?); - framebuffers.push(device.create_framebuffer( - &renderpass, - Some(&imageviews[i]), - extent - ).map_err(|_| error::CreationError::OutOfMemoryError)?); - } - } - - (cmd_pool, cmd_buffers, get_image, render_complete, present_complete, imageviews, framebuffers) - }; - // Texture store let texture_store = TextureStore::new(&mut device, &mut adapter, &mut queue_group.queues[0], &mut cmd_pool, file)?; - // Camera - // TODO: Settings - let ratio = extent.width as f32 / extent.height as f32; - let camera = WorkingCamera::defaults(ratio); - let mut descriptor_set_layouts: ArrayVec<[_; 2]> = ArrayVec::new(); descriptor_set_layouts.push(texture_store.descriptor_set_layout.deref()); // Graphics pipeline - let (pipeline_layout, pipeline) = Self::create_pipeline(&mut device, extent, &subpass, descriptor_set_layouts)?; + let (pipeline_layout, pipeline) = Self::create_pipeline(&mut device, swapchain_properties.extent, &subpass, descriptor_set_layouts)?; + + // Swapchain and associated resources + let target_chain = TargetChain::new(&mut device, &adapter, &mut surface, &renderpass, &mut cmd_pool, swapchain_properties, None).map_err(|e| error::CreationError::TargetChainCreationError (e) )?; Ok(RenderingContext { instance: ManuallyDrop::new(instance), @@ -321,21 +274,10 @@ impl<'a> RenderingContext<'a> { device: ManuallyDrop::new(device), adapter, queue_group, - swapchain: ManuallyDrop::new(swapchain), - viewport, - - imageviews, - framebuffers, renderpass: ManuallyDrop::new(renderpass), - current_frame: 0, - - get_image, - render_complete, - present_complete, - frames_in_flight, - cmd_pool, - cmd_buffers, + target_chain: ManuallyDrop::new(target_chain), + cmd_pool: ManuallyDrop::new(cmd_pool), pipeline_layout: ManuallyDrop::new(pipeline_layout), pipeline: ManuallyDrop::new(pipeline), @@ -352,20 +294,16 @@ impl<'a> RenderingContext<'a> { /// If this function fails the whole context is probably dead /// The context must not be used while this is being called pub unsafe fn handle_surface_change(&mut self) -> Result<(), error::CreationError> { - use core::ptr::read; - self.device.wait_idle().unwrap(); - // Swapchain itself - let old_swapchain = ManuallyDrop::into_inner(read(&self.swapchain)); - - let (format, viewport, extent, swapchain, backbuffer) = RenderingContext::create_swapchain(&mut self.surface, &mut self.device, &self.adapter, Some(old_swapchain))?; - - self.swapchain = ManuallyDrop::new(swapchain); - self.viewport = viewport; + let properties = SwapchainProperties::find_best(&self.adapter, &self.surface).map_err(|_| error::CreationError::BadSurface)?; // Camera settings (aspect ratio) - self.camera.update_aspect_ratio(extent.width as f32 / extent.height as f32); + self.camera.update_aspect_ratio( + properties.extent.width as f32 / + properties.extent.height as f32); + + use core::ptr::read; // Graphics pipeline self.device.destroy_graphics_pipeline(ManuallyDrop::into_inner(read(&self.pipeline))); @@ -373,7 +311,6 @@ impl<'a> RenderingContext<'a> { self.device .destroy_pipeline_layout(ManuallyDrop::into_inner(read(&self.pipeline_layout))); - let (pipeline_layout, pipeline) = { let mut descriptor_set_layouts: ArrayVec<[_; 2]> = ArrayVec::new(); descriptor_set_layouts.push(self.texture_store.descriptor_set_layout.deref()); @@ -383,126 +320,17 @@ impl<'a> RenderingContext<'a> { main_pass: &(*self.renderpass) }; - Self::create_pipeline(&mut self.device, extent, &subpass, descriptor_set_layouts)? + Self::create_pipeline(&mut self.device, properties.extent, &subpass, descriptor_set_layouts)? }; self.pipeline_layout = ManuallyDrop::new(pipeline_layout); self.pipeline = ManuallyDrop::new(pipeline); - // Imageviews, framebuffers - // Destroy old objects - for framebuffer in self.framebuffers.drain(..) { - self.device.destroy_framebuffer(framebuffer); - } - for image_view in self.imageviews.drain(..) { - self.device.destroy_image_view(image_view); - } - // Make new ones - for i in 0..backbuffer.len() { - use hal::image::ViewKind; - use hal::format::Swizzle; - - self.imageviews.push(self.device.create_image_view( - &backbuffer[i], - ViewKind::D2, - format, - Swizzle::NO, - COLOR_RANGE.clone(), - ).map_err(|e| error::CreationError::ImageViewError (e))?); - - self.framebuffers.push(self.device.create_framebuffer( - &self.renderpass, - Some(&self.imageviews[i]), - extent - ).map_err(|_| error::CreationError::OutOfMemoryError)?); - } + let old_swapchain = ManuallyDrop::into_inner(read(&self.target_chain)).deactivate_with_recyling(&mut self.device, &mut self.cmd_pool); + self.target_chain = ManuallyDrop::new(TargetChain::new(&mut self.device, &self.adapter, &mut self.surface, &self.renderpass, &mut self.cmd_pool, properties, Some(old_swapchain)) + .map_err(|e| error::CreationError::TargetChainCreationError (e) )?); Ok(()) - } - - fn create_swapchain(surface: &mut Surface, device: &mut Device, adapter: &Adapter, old_swapchain: Option<Swapchain>) -> Result<( - hal::format::Format, - hal::pso::Viewport, - hal::image::Extent, - Swapchain, - Vec<Image> - ), error::CreationError> { - use hal::{ - window::{PresentMode, CompositeAlphaMode}, - format::{Format, ChannelType}, - image::Usage, - pso::Viewport - }; - - // Figure out what the surface supports - let caps = surface.capabilities(&adapter.physical_device); - let formats = surface.supported_formats(&adapter.physical_device); - - // Find which settings we'll actually use based on preset preferences - let format = formats.map_or(Format::Rgba8Srgb, |formats| { - formats.iter() - .find(|format| format.base_format().1 == ChannelType::Srgb) - .map(|format| *format) - .unwrap_or(formats[0]) - }); - - let present_mode = { - [PresentMode::MAILBOX, PresentMode::FIFO, PresentMode::RELAXED, PresentMode::IMMEDIATE] - .iter() - .cloned() - .find(|pm| caps.present_modes.contains(*pm)) - .ok_or(error::CreationError::BadSurface)? - }; - let composite_alpha = { - [CompositeAlphaMode::OPAQUE, CompositeAlphaMode::INHERIT, CompositeAlphaMode::PREMULTIPLIED, CompositeAlphaMode::POSTMULTIPLIED] - .iter() - .cloned() - .find(|ca| caps.composite_alpha_modes.contains(*ca)) - .ok_or(error::CreationError::BadSurface)? - }; - - // Figure out properties for our swapchain - let extent = caps.extents.end(); // Size - - // Number of frames to pre-render - let image_count = if present_mode == PresentMode::MAILBOX { - ((*caps.image_count.end()) - 1).min((*caps.image_count.start()).max(3)) - } else { - ((*caps.image_count.end()) - 1).min((*caps.image_count.start()).max(2)) - }; - - let image_layers = 1; // Don't support 3D - let image_usage = if caps.usage.contains(Usage::COLOR_ATTACHMENT) { - Usage::COLOR_ATTACHMENT - } else { - Err(error::CreationError::BadSurface)? - }; - - // Swap config - let swap_config = SwapchainConfig { - present_mode, - composite_alpha_mode: composite_alpha, - format, - extent: *extent, - image_count, - image_layers, - image_usage, - }; - - // Viewport - let extent = extent.to_extent(); - let viewport = Viewport { - rect: extent.rect(), - depth: 0.0..1.0 - }; - - // Swapchain - let (swapchain, backbuffer) = unsafe { - device.create_swapchain(surface, swap_config, old_swapchain) - .map_err(|e| error::CreationError::SwapchainError (e))? - }; - - Ok((format, viewport, extent, swapchain, backbuffer)) } #[allow(clippy::type_complexity)] @@ -586,7 +414,10 @@ impl<'a> RenderingContext<'a> { // Depth stencil let depth_stencil = DepthStencilDesc { - depth: None, + depth: Some(DepthTest { + fun: Comparison::Less, + write: true + }), depth_bounds: false, stencil: None, }; @@ -660,167 +491,94 @@ impl<'a> RenderingContext<'a> { /// Draw all vertices in the buffer pub fn draw_vertices<M: MinBSPFeatures<VulkanSystem>>(&mut self, file: &M,faces: &Vec<u32>) -> Result<(), &'static str> { - let get_image = &self.get_image[self.current_frame]; - let render_complete = &self.render_complete[self.current_frame]; - - // Advance the frame _before_ we start using the `?` operator - self.current_frame = (self.current_frame + 1) % self.frames_in_flight; - - // Get the image - let (image_index, _) = unsafe { - self - .swapchain - .acquire_image(core::u64::MAX, Some(get_image), None) - .map_err(|_| "FrameError::AcquireError")? - }; - let image_index = image_index as usize; - - // Make sure whatever was last using this has finished - let present_complete = &self.present_complete[image_index]; - unsafe { - self.device - .wait_for_fence(present_complete, core::u64::MAX) - .map_err(|_| "FrameError::SyncObjectError")?; - self.device - .reset_fence(present_complete) - .map_err(|_| "FrameError::SyncObjectError")?; - }; - - // Record commands - unsafe { - use hal::buffer::{IndexBufferView, SubRange}; - use hal::command::{SubpassContents, CommandBufferFlags, ClearValue, ClearColor}; - use hal::pso::ShaderStageFlags; - - // Command buffer to use - let buffer = &mut self.cmd_buffers[image_index]; + // Prepare command buffer + let cmd_buffer = self.target_chain.prep_next_target( + &mut self.device, + self.vert_buffer.get_buffer(), + self.index_buffer.get_buffer(), + &self.renderpass, + &self.pipeline, + &self.pipeline_layout, + &mut self.camera + )?; + + // Iterate over faces, copying them in and drawing groups that use the same texture chunk all at once. + let mut current_chunk = file.get_face(0).texture_idx as usize / 8; + let mut chunk_start = 0; + + let mut curr_vert_idx: usize = 0; + let mut curr_idx_idx: usize = 0; + + for face in faces.into_iter().map(|idx| file.get_face(*idx)) { + if current_chunk != face.texture_idx as usize / 8 { + // Last index was last of group, so draw it all. + let mut descriptor_sets: ArrayVec<[_; 1]> = ArrayVec::new(); + descriptor_sets.push(self.texture_store.get_chunk_descriptor_set(current_chunk)); + unsafe { - // Colour to clear window to - let clear_values = [ClearValue { - color: ClearColor { - float32: [0.0, 0.0, 0.0, 1.0] + cmd_buffer.bind_graphics_descriptor_sets( + &self.pipeline_layout, + 0, + descriptor_sets, + &[] + ); + cmd_buffer.draw_indexed(chunk_start as u32 * 3..(curr_idx_idx as u32 * 3) + 1, 0, 0..1); } - }]; - - // Get references to our buffers - let (vbufs, ibuf) = { - let vbufref: &<back::Backend as hal::Backend>::Buffer = self.vert_buffer.get_buffer(); - - let vbufs: ArrayVec<[_; 1]> = [(vbufref, SubRange::WHOLE)].into(); - let ibuf = self.index_buffer.get_buffer(); + + // Next group of same-chunked faces starts here. + chunk_start = curr_idx_idx; + current_chunk = face.texture_idx as usize / 8; + } - (vbufs, ibuf) - }; + if face.face_type == FaceType::Polygon || face.face_type == FaceType::Mesh { + // 2 layers of indirection + let base = face.vertices_idx.start; - buffer.begin_primary(CommandBufferFlags::EMPTY); - { - // Main render pass / pipeline - buffer.begin_render_pass( - &self.renderpass, - &self.framebuffers[image_index], - self.viewport.rect, - clear_values.iter(), - SubpassContents::Inline - ); - buffer.bind_graphics_pipeline(&self.pipeline); + for idx in face.meshverts_idx.clone().step_by(3) { + let start_idx: u16 = curr_vert_idx.try_into().unwrap(); - // VP Matrix - let vp = self.camera.get_matrix().as_slice(); - let vp = std::mem::transmute::<&[f32], &[u32]>(vp); + for idx2 in idx..idx+3 { + let vert = &file.resolve_meshvert(idx2 as u32, base); + let uv = Vector2::new(vert.tex.u[0], vert.tex.v[0]); - buffer.push_graphics_constants( - &self.pipeline_layout, - ShaderStageFlags::VERTEX, - 0, - vp); - - // Bind buffers - buffer.bind_vertex_buffers(0, vbufs); - buffer.bind_index_buffer(IndexBufferView { - buffer: ibuf, - range: SubRange::WHOLE, - index_type: hal::IndexType::U16 - }); - - // Iterate over faces, copying them in and drawing groups that use the same texture chunk all at once. - let mut current_chunk = file.get_face(0).texture_idx as usize / 8; - let mut chunk_start = 0; - - let mut curr_vert_idx: usize = 0; - let mut curr_idx_idx: usize = 0; - - for face in faces.into_iter().map(|idx| file.get_face(*idx)) { - if current_chunk != face.texture_idx as usize / 8 { - // Last index was last of group, so draw it all. - let mut descriptor_sets: ArrayVec<[_; 1]> = ArrayVec::new(); - descriptor_sets.push(self.texture_store.get_chunk_descriptor_set(current_chunk)); - - buffer.bind_graphics_descriptor_sets( - &self.pipeline_layout, - 0, - descriptor_sets, - &[] - ); - - buffer.draw_indexed(chunk_start as u32 * 3..(curr_idx_idx as u32 * 3) + 1, 0, 0..1); + let uvp = UVPoint (vert.position, face.texture_idx.try_into().unwrap(), uv); + self.vert_buffer[curr_vert_idx] = uvp; - // Next group of same-chunked faces starts here. - chunk_start = curr_idx_idx; - current_chunk = face.texture_idx as usize / 8; + curr_vert_idx += 1; } - if face.face_type == FaceType::Polygon || face.face_type == FaceType::Mesh { - // 2 layers of indirection - let base = face.vertices_idx.start; - - for idx in face.meshverts_idx.clone().step_by(3) { - let start_idx: u16 = curr_vert_idx.try_into().unwrap(); - - for idx2 in idx..idx+3 { - let vert = &file.resolve_meshvert(idx2 as u32, base); - let uv = Vector2::new(vert.tex.u[0], vert.tex.v[0]); - - let uvp = UVPoint (vert.position, face.texture_idx.try_into().unwrap(), uv); - self.vert_buffer[curr_vert_idx] = uvp; - - curr_vert_idx += 1; - } - - self.index_buffer[curr_idx_idx] = (start_idx, start_idx + 1, start_idx + 2); + self.index_buffer[curr_idx_idx] = (start_idx, start_idx + 1, start_idx + 2); - curr_idx_idx += 1; - - if curr_vert_idx >= INITIAL_VERT_SIZE.try_into().unwrap() || curr_idx_idx >= INITIAL_INDEX_SIZE.try_into().unwrap() { - println!("out of vertex buffer space!"); - break; - } - } - } else { - // TODO: Other types of faces - } + curr_idx_idx += 1; if curr_vert_idx >= INITIAL_VERT_SIZE.try_into().unwrap() || curr_idx_idx >= INITIAL_INDEX_SIZE.try_into().unwrap() { println!("out of vertex buffer space!"); break; } } + } else { + // TODO: Other types of faces + } - // Draw the final group of chunks - let mut descriptor_sets: ArrayVec<[_; 1]> = ArrayVec::new(); - descriptor_sets.push(self.texture_store.get_chunk_descriptor_set(current_chunk)); - buffer.bind_graphics_descriptor_sets( - &self.pipeline_layout, - 0, - descriptor_sets, - &[] - ); - buffer.draw_indexed(chunk_start as u32 * 3..(curr_idx_idx as u32 * 3) + 1, 0, 0..1); - - buffer.end_render_pass(); + if curr_vert_idx >= INITIAL_VERT_SIZE.try_into().unwrap() || curr_idx_idx >= INITIAL_INDEX_SIZE.try_into().unwrap() { + println!("out of vertex buffer space!"); + break; } - buffer.finish(); - }; + } + + // Draw the final group of chunks + let mut descriptor_sets: ArrayVec<[_; 1]> = ArrayVec::new(); + descriptor_sets.push(self.texture_store.get_chunk_descriptor_set(current_chunk)); + unsafe { + cmd_buffer.bind_graphics_descriptor_sets( + &self.pipeline_layout, + 0, + descriptor_sets, + &[] + ); + cmd_buffer.draw_indexed(chunk_start as u32 * 3..(curr_idx_idx as u32 * 3) + 1, 0, 0..1); + } // Update our buffers before we actually start drawing self.vert_buffer.commit( @@ -835,27 +593,8 @@ impl<'a> RenderingContext<'a> { &mut self.cmd_pool ); - // Make submission object - let command_buffers = &self.cmd_buffers[image_index..=image_index]; - let wait_semaphores: ArrayVec<[_; 1]> = [(get_image, hal::pso::PipelineStage::COLOR_ATTACHMENT_OUTPUT)].into(); - let signal_semaphores: ArrayVec<[_; 1]> = [render_complete].into(); - - let present_wait_semaphores: ArrayVec<[_; 1]> = [render_complete].into(); - - let submission = Submission { - command_buffers, - wait_semaphores, - signal_semaphores, - }; - - // Submit it - let command_queue = &mut self.queue_group.queues[0]; - unsafe { - command_queue.submit(submission, Some(present_complete)); - self.swapchain - .present(command_queue, image_index as u32, present_wait_semaphores) - .map_err(|_| "FrameError::PresentError")? - }; + // Send commands off to GPU + self.target_chain.finish_and_submit_target(&mut self.queue_group.queues[0])?; Ok(()) } @@ -883,35 +622,19 @@ impl<'a> core::ops::Drop for RenderingContext<'a> { self.device.wait_idle().unwrap(); unsafe { - for fence in self.present_complete.drain(..) { - self.device.destroy_fence(fence) - } - for semaphore in self.render_complete.drain(..) { - self.device.destroy_semaphore(semaphore) - } - for semaphore in self.get_image.drain(..) { - self.device.destroy_semaphore(semaphore) - } - for framebuffer in self.framebuffers.drain(..) { - self.device.destroy_framebuffer(framebuffer); - } - for image_view in self.imageviews.drain(..) { - self.device.destroy_image_view(image_view); - } - use core::ptr::read; + ManuallyDrop::into_inner(read(&self.vert_buffer)).deactivate(&mut self.device); ManuallyDrop::into_inner(read(&self.index_buffer)).deactivate(&mut self.device); ManuallyDrop::into_inner(read(&self.texture_store)).deactivate(&mut self.device); + ManuallyDrop::into_inner(read(&self.target_chain)).deactivate(&mut self.device, &mut self.cmd_pool); + self.device.destroy_command_pool( ManuallyDrop::into_inner(read(&self.cmd_pool)), ); - self.device .destroy_render_pass(ManuallyDrop::into_inner(read(&self.renderpass))); - self.device - .destroy_swapchain(ManuallyDrop::into_inner(read(&self.swapchain))); self.device.destroy_graphics_pipeline(ManuallyDrop::into_inner(read(&self.pipeline))); diff --git a/stockton-render/src/draw/mod.rs b/stockton-render/src/draw/mod.rs index 2834aa4..af86b46 100644 --- a/stockton-render/src/draw/mod.rs +++ b/stockton-render/src/draw/mod.rs @@ -15,6 +15,8 @@ //! Given 3D points and some camera information, renders to the screen. +pub mod target; + #[macro_use] mod macros; mod context; diff --git a/stockton-render/src/draw/target.rs b/stockton-render/src/draw/target.rs new file mode 100644 index 0000000..3d0ecdf --- /dev/null +++ b/stockton-render/src/draw/target.rs @@ -0,0 +1,407 @@ +// Copyright (C) Oscar Shrimpton 2019 + +// This program is free software: you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the Free +// Software Foundation, either version 3 of the License, or (at your option) +// any later version. + +// This program is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. + +// You should have received a copy of the GNU General Public License along +// with this program. If not, see <http://www.gnu.org/licenses/>. + +//! Resources needed for drawing on the screen, including sync objects +use crate::types::*; +use super::{ + camera::WorkingCamera, + texture::image::LoadedImage +}; + +use core::{ + iter::once, + mem::ManuallyDrop +}; + +use arrayvec::ArrayVec; +use hal::{ + prelude::*, + queue::Submission, + pso::Viewport, + format::{ChannelType, Format, Swizzle}, + image::{Extent, ViewKind, Usage as ImgUsage}, + window::{CompositeAlphaMode, PresentMode, SwapchainConfig, Extent2D} +}; + +/// Defines the colour range we use. +const COLOR_RANGE: hal::image::SubresourceRange = hal::image::SubresourceRange { + aspects: hal::format::Aspects::COLOR, + levels: 0..1, + layers: 0..1, +}; + +#[derive(Debug, Clone)] +pub struct SwapchainProperties { + pub format: Format, + pub depth_format: Format, + pub present_mode: PresentMode, + pub composite_alpha_mode: CompositeAlphaMode, + pub viewport: Viewport, + pub extent: Extent +} + +impl SwapchainProperties { + pub fn find_best(adapter: &Adapter, surface: &Surface) -> Result<SwapchainProperties, ()> { + let caps = surface.capabilities(&adapter.physical_device); + let formats = surface.supported_formats(&adapter.physical_device); + + // Find which settings we'll actually use based on preset preferences + let format = formats.clone().map_or(Format::Rgba8Srgb, |formats| { + formats.iter() + .find(|format| format.base_format().1 == ChannelType::Srgb) + .map(|format| *format) + .unwrap_or(formats[0]) + }); + + let depth_format = *[Format::D32SfloatS8Uint, Format::D24UnormS8Uint, Format::D32Sfloat].iter().find(|format| { + use hal::format::ImageFeature; + + format.is_depth() && adapter.physical_device.format_properties(Some(**format)).optimal_tiling.contains(ImageFeature::DEPTH_STENCIL_ATTACHMENT) + }).ok_or_else(|| ())?; + + let present_mode = { + [PresentMode::MAILBOX, PresentMode::FIFO, PresentMode::RELAXED, PresentMode::IMMEDIATE] + .iter() + .cloned() + .find(|pm| caps.present_modes.contains(*pm)) + .ok_or(())? + }; + let composite_alpha_mode = { + [CompositeAlphaMode::OPAQUE, CompositeAlphaMode::INHERIT, CompositeAlphaMode::PREMULTIPLIED, CompositeAlphaMode::POSTMULTIPLIED] + .iter() + .cloned() + .find(|ca| caps.composite_alpha_modes.contains(*ca)) + .ok_or(())? + }; + + let extent = caps.extents.end().to_extent(); // Size + let viewport = Viewport { + rect: extent.rect(), + depth: 0.0..1.0 + }; + + Ok(SwapchainProperties {format, depth_format, present_mode, composite_alpha_mode, extent, viewport}) + } +} + +pub struct TargetChain { + /// Swapchain we're targeting + pub swapchain: ManuallyDrop<Swapchain>, + + pub properties: SwapchainProperties, + + /// The depth buffer/image used for drawing + pub depth_buffer: ManuallyDrop<LoadedImage>, + + /// Resources tied to each target frame in the swapchain + pub targets: Box<[TargetResources]>, + + /// The last target drawn to + last_drawn: usize, + + /// Last image index of the swapchain drawn to + last_image_index: u32, +} + +impl TargetChain { + pub fn new(device: &mut Device, adapter: &Adapter, surface: &mut Surface, renderpass: &RenderPass, cmd_pool: &mut CommandPool, properties: SwapchainProperties, old_swapchain: Option<Swapchain>) -> Result<TargetChain, TargetChainCreationError> { + let caps = surface.capabilities(&adapter.physical_device); + + // Number of frames to pre-render + let image_count = if properties.present_mode == PresentMode::MAILBOX { + ((*caps.image_count.end()) - 1).min((*caps.image_count.start()).max(3)) + } else { + ((*caps.image_count.end()) - 1).min((*caps.image_count.start()).max(2)) + }; + + // Swap config + let swap_config = SwapchainConfig { + present_mode: properties.present_mode, + composite_alpha_mode: properties.composite_alpha_mode, + format: properties.format, + extent: Extent2D { width: properties.extent.width, height: properties.extent.height }, + image_count, + image_layers: 1, + image_usage: ImgUsage::COLOR_ATTACHMENT, + }; + + // Swapchain + let (swapchain, mut backbuffer) = unsafe { + device.create_swapchain(surface, swap_config, old_swapchain) + .map_err(|_| TargetChainCreationError::Todo)? + }; + + let depth_buffer: LoadedImage = { + use hal::image::SubresourceRange; + use hal::format::Aspects; + + LoadedImage::new(device, adapter, properties.depth_format, ImgUsage::DEPTH_STENCIL_ATTACHMENT, SubresourceRange { + aspects: Aspects::DEPTH, + levels: 0..1, + layers: 0..1, + },properties.extent.width as usize, properties.extent.height as usize).map_err(|_| TargetChainCreationError::Todo) + }?; + + let mut targets: Vec<TargetResources> = Vec::with_capacity(backbuffer.len()); + for image in backbuffer.drain(..) { + targets.push(TargetResources::new(device, cmd_pool, renderpass, image, &(*depth_buffer.image_view), properties.extent, properties.format) + .map_err(|_| TargetChainCreationError::Todo)?); + } + + Ok(TargetChain { + swapchain: ManuallyDrop::new(swapchain), + targets: targets.into_boxed_slice(), + depth_buffer: ManuallyDrop::new(depth_buffer), + properties, + last_drawn: (image_count - 1) as usize, // This means the next one to be used is index 0 + last_image_index: 0 + }) + } + + pub fn deactivate(self, device: &mut Device, cmd_pool: &mut CommandPool) -> () { + use core::ptr::read; + unsafe { + ManuallyDrop::into_inner(read(&self.depth_buffer)).deactivate(device); + + for i in 0..self.targets.len() { + read(&self.targets[i]).deactivate(device, cmd_pool); + } + + device.destroy_swapchain(ManuallyDrop::into_inner(read(&self.swapchain))); + } + } + + pub fn deactivate_with_recyling(self, device: &mut Device, cmd_pool: &mut CommandPool) -> Swapchain { + use core::ptr::read; + unsafe { + ManuallyDrop::into_inner(read(&self.depth_buffer)).deactivate(device); + + for i in 0..self.targets.len() { + read(&self.targets[i]).deactivate(device, cmd_pool); + } + } + + return unsafe { ManuallyDrop::into_inner(read(&self.swapchain)) }; + } + + pub fn prep_next_target<'a>(&'a mut self, device: &mut Device, vert_buffer: &Buffer, index_buffer: &Buffer, renderpass: &RenderPass, pipeline: &GraphicsPipeline, pipeline_layout: &PipelineLayout, camera: &mut WorkingCamera) -> Result<&'a mut crate::types::CommandBuffer, &'static str> { + self.last_drawn = (self.last_drawn + 1) % self.targets.len(); + + let target = &mut self.targets[self.last_drawn]; + + // Get the image + let (image_index, _) = unsafe { + self.swapchain + .acquire_image(core::u64::MAX, Some(&target.get_image), None) + .map_err(|_| "FrameError::AcquireError")? + }; + + self.last_image_index = image_index; + + // Make sure whatever was last using this has finished + unsafe { + device + .wait_for_fence(&target.present_complete, core::u64::MAX) + .map_err(|_| "FrameError::SyncObjectError")?; + device + .reset_fence(&target.present_complete) + .map_err(|_| "FrameError::SyncObjectError")?; + }; + + // Record commands + unsafe { + use hal::buffer::{IndexBufferView, SubRange}; + use hal::command::{SubpassContents, CommandBufferFlags, ClearValue, ClearColor, ClearDepthStencil}; + use hal::pso::ShaderStageFlags; + + // Colour to clear window to + let clear_values = [ClearValue { + color: ClearColor { + float32: [0.0, 0.0, 0.0, 1.0] + } + }, ClearValue { + depth_stencil: ClearDepthStencil { + depth: 1.0, + stencil: 0 + } + }]; + + // Get references to our buffers + let (vbufs, ibuf) = { + let vbufref: &<back::Backend as hal::Backend>::Buffer = vert_buffer; + + let vbufs: ArrayVec<[_; 1]> = [(vbufref, SubRange::WHOLE)].into(); + let ibuf = index_buffer; + + (vbufs, ibuf) + }; + + target.cmd_buffer.begin_primary(CommandBufferFlags::EMPTY); + // Main render pass / pipeline + target.cmd_buffer.begin_render_pass( + renderpass, + &target.framebuffer, + self.properties.viewport.rect, + clear_values.iter(), + SubpassContents::Inline + ); + target.cmd_buffer.bind_graphics_pipeline(&pipeline); + + // VP Matrix + let vp = camera.get_matrix().as_slice(); + let vp = std::mem::transmute::<&[f32], &[u32]>(vp); + + target.cmd_buffer.push_graphics_constants( + &pipeline_layout, + ShaderStageFlags::VERTEX, + 0, + vp); + + // Bind buffers + target.cmd_buffer.bind_vertex_buffers(0, vbufs); + target.cmd_buffer.bind_index_buffer(IndexBufferView { + buffer: ibuf, + range: SubRange::WHOLE, + index_type: hal::IndexType::U16 + }); + }; + + Ok(&mut target.cmd_buffer) + } + + pub fn finish_and_submit_target(&mut self, command_queue: &mut CommandQueue) -> Result<(), &'static str> { + let target = &mut self.targets[self.last_drawn]; + + unsafe { + target.cmd_buffer.end_render_pass(); + target.cmd_buffer.finish(); + } + + // Make submission object + let command_buffers: std::iter::Once<&CommandBuffer> = once(&target.cmd_buffer); + let wait_semaphores: std::iter::Once<(&Semaphore, hal::pso::PipelineStage)> = once((&target.get_image, hal::pso::PipelineStage::COLOR_ATTACHMENT_OUTPUT)); + let signal_semaphores: std::iter::Once<&Semaphore> = once(&target.render_complete); + + let present_wait_semaphores: std::iter::Once<&Semaphore> = once(&target.render_complete); + + let submission = Submission { + command_buffers, + wait_semaphores, + signal_semaphores, + }; + + // Submit it + unsafe { + command_queue.submit(submission, Some(&target.present_complete)); + self.swapchain + .present(command_queue, self.last_image_index as u32, present_wait_semaphores) + .map_err(|_| "FrameError::PresentError")?; + }; + + // TODO + Ok(()) + } +} + +/// Resources for a single target frame, including sync objects +pub struct TargetResources { + /// Command buffer to use when drawing + pub cmd_buffer: ManuallyDrop<CommandBuffer>, + + /// The image for this frame + pub image: ManuallyDrop<Image>, + + /// Imageviews for this frame + pub imageview: ManuallyDrop<ImageView>, + + /// Framebuffer for this frame + pub framebuffer: ManuallyDrop<Framebuffer>, + + // Sync objects + + /// Triggered when the image is ready to draw to + pub get_image: ManuallyDrop<Semaphore>, + + /// Triggered when rendering is done + pub render_complete: ManuallyDrop<Semaphore>, + + /// Triggered when the image is on screen + pub present_complete: ManuallyDrop<Fence> +} + + +impl TargetResources { + pub fn new(device: &mut Device, cmd_pool: &mut CommandPool, renderpass: &RenderPass, image: Image, depth_pass: &ImageView, extent: Extent, format: Format) -> Result<TargetResources, TargetResourcesCreationError> { + // Command Buffer + let cmd_buffer = unsafe { cmd_pool.allocate_one(hal::command::Level::Primary) }; + + // ImageView + let imageview = unsafe { device.create_image_view( + &image, + ViewKind::D2, + format, + Swizzle::NO, + COLOR_RANGE.clone(), + ).map_err(|e| TargetResourcesCreationError::ImageViewError (e))? }; + + // Framebuffer + let framebuffer = unsafe { device.create_framebuffer( + &renderpass, + once(&imageview).chain(once(depth_pass)), + extent + ).map_err(|_| TargetResourcesCreationError::FrameBufferNoMemory)? }; + + // Sync objects + let get_image = device.create_semaphore().map_err(|_| TargetResourcesCreationError::SyncObjectsNoMemory)?; + let render_complete = device.create_semaphore().map_err(|_| TargetResourcesCreationError::SyncObjectsNoMemory)?; + let present_complete = device.create_fence(true).map_err(|_| TargetResourcesCreationError::SyncObjectsNoMemory)?; + + Ok(TargetResources { + cmd_buffer: ManuallyDrop::new(cmd_buffer), + image: ManuallyDrop::new(image), + imageview: ManuallyDrop::new(imageview), + framebuffer: ManuallyDrop::new(framebuffer), + get_image: ManuallyDrop::new(get_image), + render_complete: ManuallyDrop::new(render_complete), + present_complete: ManuallyDrop::new(present_complete) + }) + } + + pub fn deactivate(self, device: &mut Device, cmd_pool: &mut CommandPool) -> () { + use core::ptr::read; + unsafe { + cmd_pool.free(once(ManuallyDrop::into_inner(read(&self.cmd_buffer)))); + + device.destroy_framebuffer(ManuallyDrop::into_inner(read(&self.framebuffer))); + device.destroy_image_view(ManuallyDrop::into_inner(read(&self.imageview))); + + device.destroy_semaphore(ManuallyDrop::into_inner(read(&self.get_image))); + device.destroy_semaphore(ManuallyDrop::into_inner(read(&self.render_complete))); + device.destroy_fence(ManuallyDrop::into_inner(read(&self.present_complete))); + } + } +} + +#[derive(Debug)] +pub enum TargetChainCreationError { + Todo +} + +#[derive(Debug)] +pub enum TargetResourcesCreationError { + ImageViewError (hal::image::ViewCreationError), + FrameBufferNoMemory, + SyncObjectsNoMemory +} diff --git a/stockton-render/src/draw/texture/chunk.rs b/stockton-render/src/draw/texture/chunk.rs index 22541a5..40eb21e 100644 --- a/stockton-render/src/draw/texture/chunk.rs +++ b/stockton-render/src/draw/texture/chunk.rs @@ -31,7 +31,7 @@ use crate::{ use log::debug; use super::resolver::TextureResolver; -use super::image::LoadedImage; +use super::image::SampledImage; use stockton_levels::prelude::*; /// The size of a chunk. Needs to match up with the fragment shader @@ -40,7 +40,7 @@ pub const CHUNK_SIZE: usize = 8; /// An array of textures pub struct TextureChunk { pub(crate) descriptor_set: DescriptorSet, - loaded_images: Vec<LoadedImage>, + sampled_images: Vec<SampledImage>, } impl TextureChunk { @@ -65,7 +65,7 @@ impl TextureChunk { let mut store = TextureChunk { descriptor_set: descriptor_set, - loaded_images: Vec::with_capacity(CHUNK_SIZE), + sampled_images: Vec::with_capacity(CHUNK_SIZE), }; let mut local_idx = 0; @@ -110,12 +110,14 @@ impl TextureChunk { command_pool: &mut CommandPool) -> Result<(), &'static str>{ // Load the image - let texture = LoadedImage::load( + let texture = SampledImage::load_into_new( image, device, adapter, command_queue, command_pool, + hal::format::Format::Rgba8Srgb, // TODO + hal::image::Usage::empty() )?; // Write it to the descriptor set @@ -129,7 +131,7 @@ impl TextureChunk { binding: 0, array_offset: idx, descriptors: Some(Descriptor::Image( - texture.image_view.deref(), + texture.image.image_view.deref(), Layout::ShaderReadOnlyOptimal )), }, @@ -144,17 +146,17 @@ impl TextureChunk { // Store it so we can safely deactivate it when we need to // Deactivate the old image if we need to - if idx < self.loaded_images.len() { - replace(&mut self.loaded_images[idx], texture).deactivate(device); + if idx < self.sampled_images.len() { + replace(&mut self.sampled_images[idx], texture).deactivate(device); } else { - self.loaded_images.push(texture); + self.sampled_images.push(texture); } Ok(()) } pub fn deactivate(mut self, device: &mut Device) -> () { - for img in self.loaded_images.drain(..) { + for img in self.sampled_images.drain(..) { img.deactivate(device); } } diff --git a/stockton-render/src/draw/texture/image.rs b/stockton-render/src/draw/texture/image.rs index 583c2d9..530628a 100644 --- a/stockton-render/src/draw/texture/image.rs +++ b/stockton-render/src/draw/texture/image.rs @@ -26,7 +26,7 @@ use hal::{ MemoryTypeId, buffer::Usage as BufUsage, format::{Format, Swizzle, Aspects}, - image::{ViewKind, SubresourceRange}, + image::{ViewKind, SubresourceRange, Usage as ImgUsage}, queue::Submission, memory::{Properties as MemProperties, Dependencies as MemDependencies, Segment}, prelude::*, @@ -46,28 +46,91 @@ pub struct LoadedImage { /// The full view of the image pub image_view: ManuallyDrop<ImageView>, - /// A sampler for the image - pub sampler: ManuallyDrop<Sampler>, - /// The memory backing the image memory: ManuallyDrop<Memory> } +pub fn create_image_view(device: &mut Device, adapter: &Adapter, format: Format, usage: ImgUsage, width: usize, height: usize) -> Result<(Memory, Image), &'static str> { + // Round up the size to align properly + let initial_row_size = PIXEL_SIZE * width; + let limits = adapter.physical_device.limits(); + let row_alignment_mask = limits.optimal_buffer_copy_pitch_alignment as u32 - 1; + + let row_size = ((initial_row_size as u32 + row_alignment_mask) & !row_alignment_mask) as usize; + debug_assert!(row_size as usize >= initial_row_size); + + // Make the image + let mut image_ref = unsafe { + use hal::image::{Kind, Tiling, ViewCapabilities}; + + device.create_image( + Kind::D2(width as u32, height as u32, 1, 1), + 1, + format, + Tiling::Optimal, + usage, + ViewCapabilities::empty() + ) + }.map_err(|_| "Couldn't create image")?; + + // Allocate memory + let memory = unsafe { + let requirements = device.get_image_requirements(&image_ref); + + let memory_type_id = adapter.physical_device + .memory_properties().memory_types + .iter().enumerate() + .find(|&(id, memory_type)| { + requirements.type_mask & (1 << id) != 0 && memory_type.properties.contains(MemProperties::DEVICE_LOCAL) + }) + .map(|(id, _)| MemoryTypeId(id)) + .ok_or("Couldn't find a memory type for image memory")?; + + let memory = device + .allocate_memory(memory_type_id, requirements.size) + .map_err(|_| "Couldn't allocate image memory")?; + + device.bind_image_memory(&memory, 0, &mut image_ref) + .map_err(|_| "Couldn't bind memory to image")?; + + Ok(memory) + }?; + + Ok((memory, image_ref)) + +} + impl LoadedImage { - /// Load the given image into a new buffer - pub fn load(img: RgbaImage, device: &mut Device, adapter: &Adapter, - command_queue: &mut CommandQueue, - command_pool: &mut CommandPool) -> Result<LoadedImage, &'static str> { - // Round up the size to align properly - let initial_row_size = PIXEL_SIZE * (img.width() as usize); + pub fn new(device: &mut Device, adapter: &Adapter, format: Format, usage: ImgUsage, resources: SubresourceRange, width: usize, height: usize) -> Result<LoadedImage, &'static str> { + let (memory, image_ref) = create_image_view(device, adapter, format, usage, width, height)?; + + // Create ImageView and sampler + let image_view = unsafe { device.create_image_view( + &image_ref, + ViewKind::D2, + format, + Swizzle::NO, + resources, + )}.map_err(|_| "Couldn't create the image view!")?; + + Ok(LoadedImage { + image: ManuallyDrop::new(image_ref), + image_view: ManuallyDrop::new(image_view), + memory: ManuallyDrop::new(memory) + }) + } + + /// Load the given image + pub fn load(&mut self, img: RgbaImage, device: &mut Device, adapter: &Adapter, command_queue: &mut CommandQueue, + command_pool: &mut CommandPool) -> Result<(), &'static str> { + let initial_row_size = PIXEL_SIZE * img.width() as usize; let limits = adapter.physical_device.limits(); let row_alignment_mask = limits.optimal_buffer_copy_pitch_alignment as u32 - 1; - + let row_size = ((initial_row_size as u32 + row_alignment_mask) & !row_alignment_mask) as usize; + let total_size = (row_size * (img.height() as usize)) as u64; debug_assert!(row_size as usize >= initial_row_size); - let total_size = (row_size * img.height() as usize) as u64; - // Make a staging buffer let (staging_buffer, staging_memory) = create_buffer(device, adapter, BufUsage::TRANSFER_SRC, MemProperties::CPU_VISIBLE, total_size) .map_err(|_| "Couldn't create staging buffer")?; @@ -86,43 +149,6 @@ impl LoadedImage { device.unmap_memory(&staging_memory); } - // Make the image - let mut image_ref = unsafe { - use hal::image::{Kind, Tiling, Usage, ViewCapabilities}; - - device.create_image( - Kind::D2(img.width(), img.height(), 1, 1), - 1, - Format::Rgba8Srgb, - Tiling::Optimal, - Usage::TRANSFER_DST | Usage::SAMPLED, - ViewCapabilities::empty() - ) - }.map_err(|_| "Couldn't create image")?; - - // Allocate memory - let memory = unsafe { - let requirements = device.get_image_requirements(&image_ref); - - let memory_type_id = adapter.physical_device - .memory_properties().memory_types - .iter().enumerate() - .find(|&(id, memory_type)| { - requirements.type_mask & (1 << id) != 0 && memory_type.properties.contains(MemProperties::DEVICE_LOCAL) - }) - .map(|(id, _)| MemoryTypeId(id)) - .ok_or("Couldn't find a memory type for image memory")?; - - let memory = device - .allocate_memory(memory_type_id, requirements.size) - .map_err(|_| "Couldn't allocate image memory")?; - - device.bind_image_memory(&memory, 0, &mut image_ref) - .map_err(|_| "Couldn't bind memory to image")?; - - Ok(memory) - }?; - // Copy from staging to image memory let buf = unsafe { use hal::command::{CommandBufferFlags, BufferImageCopy}; @@ -141,7 +167,7 @@ impl LoadedImage { Access::TRANSFER_WRITE, Layout::TransferDstOptimal, ), - target: &image_ref, + target: &(*self.image), families: None, range: SubresourceRange { aspects: Aspects::COLOR, @@ -156,7 +182,7 @@ impl LoadedImage { ); // Copy from buffer to image - buf.copy_buffer_to_image(&staging_buffer, &image_ref, + buf.copy_buffer_to_image(&staging_buffer, &(*self.image), Layout::TransferDstOptimal, &[ BufferImageCopy { buffer_offset: 0, @@ -187,7 +213,7 @@ impl LoadedImage { Access::SHADER_READ, Layout::ShaderReadOnlyOptimal, ), - target: &image_ref, + target: &(*self.image), families: None, range: SubresourceRange { aspects: Aspects::COLOR, @@ -229,18 +255,49 @@ impl LoadedImage { device.destroy_buffer(staging_buffer); } - // Create ImageView and sampler - let image_view = unsafe { device.create_image_view( - &image_ref, - ViewKind::D2, - Format::Rgba8Srgb, - Swizzle::NO, - SubresourceRange { - aspects: Aspects::COLOR, - levels: 0..1, - layers: 0..1, - }, - )}.map_err(|_| "Couldn't create the image view!")?; + Ok(()) + } + + /// Load the given image into a new buffer + pub fn load_into_new(img: RgbaImage, device: &mut Device, adapter: &Adapter, + command_queue: &mut CommandQueue, + command_pool: &mut CommandPool, format: Format, usage: ImgUsage) -> Result<LoadedImage, &'static str> { + let mut loaded_image = Self::new(device, adapter, format, usage | ImgUsage::TRANSFER_DST, SubresourceRange { + aspects: Aspects::COLOR, + levels: 0..1, + layers: 0..1, + }, img.width() as usize, img.height() as usize)?; + loaded_image.load(img, device, adapter, command_queue, command_pool)?; + + Ok(loaded_image) + } + + /// Properly frees/destroys all the objects in this struct + /// Dropping without doing this is a bad idea + pub fn deactivate(self, device: &Device) -> () { + unsafe { + use core::ptr::read; + + device.destroy_image_view(ManuallyDrop::into_inner(read(&self.image_view))); + device.destroy_image(ManuallyDrop::into_inner(read(&self.image))); + device.free_memory(ManuallyDrop::into_inner(read(&self.memory))); + } + } +} + + +pub struct SampledImage { + pub image: ManuallyDrop<LoadedImage>, + pub sampler: ManuallyDrop<Sampler> +} + +impl SampledImage { + pub fn new(device: &mut Device, adapter: &Adapter, format: Format, usage: ImgUsage, width: usize, height: usize) -> Result<SampledImage, &'static str> { + let image = LoadedImage::new(device, adapter, format, usage | ImgUsage::SAMPLED, SubresourceRange { + aspects: Aspects::COLOR, + levels: 0..1, + layers: 0..1, + }, width, height)?; let sampler = unsafe { use hal::image::{SamplerDesc, Filter, WrapMode}; @@ -251,24 +308,29 @@ impl LoadedImage { )) }.map_err(|_| "Couldn't create the sampler!")?; - Ok(LoadedImage { - image: ManuallyDrop::new(image_ref), - image_view: ManuallyDrop::new(image_view), - sampler: ManuallyDrop::new(sampler), - memory: ManuallyDrop::new(memory) + Ok(SampledImage { + image: ManuallyDrop::new(image), + sampler: ManuallyDrop::new(sampler) }) } - /// Properly frees/destroys all the objects in this struct - /// Dropping without doing this is a bad idea - pub fn deactivate(self, device: &Device) -> () { + pub fn load_into_new(img: RgbaImage, device: &mut Device, adapter: &Adapter, + command_queue: &mut CommandQueue, + command_pool: &mut CommandPool, format: Format, usage: ImgUsage) -> Result<SampledImage, &'static str> { + let mut sampled_image = SampledImage::new(device, adapter, format, usage | ImgUsage::TRANSFER_DST, img.width() as usize, img.height() as usize)?; + sampled_image.image.load(img, device, adapter, command_queue, command_pool)?; + + Ok(sampled_image) + + } + + pub fn deactivate(self, device: &mut Device) -> () { unsafe { use core::ptr::read; device.destroy_sampler(ManuallyDrop::into_inner(read(&self.sampler))); - device.destroy_image_view(ManuallyDrop::into_inner(read(&self.image_view))); - device.destroy_image(ManuallyDrop::into_inner(read(&self.image))); - device.free_memory(ManuallyDrop::into_inner(read(&self.memory))); + + ManuallyDrop::into_inner(read(&self.image)).deactivate(device); } } -}
\ No newline at end of file +} diff --git a/stockton-render/src/draw/texture/loader.rs b/stockton-render/src/draw/texture/loader.rs index 483fb7d..1ac0961 100644 --- a/stockton-render/src/draw/texture/loader.rs +++ b/stockton-render/src/draw/texture/loader.rs @@ -165,11 +165,6 @@ impl TextureStore { } } - /// Get number of chunks being used - pub fn get_n_chunks(&self) -> usize { - self.chunks.len() - } - /// Get the descriptor set for a given chunk pub fn get_chunk_descriptor_set<'a>(&'a self, idx: usize) -> &'a DescriptorSet { &self.chunks[idx].descriptor_set diff --git a/stockton-render/src/draw/texture/mod.rs b/stockton-render/src/draw/texture/mod.rs index 9951eeb..05dfe38 100644 --- a/stockton-render/src/draw/texture/mod.rs +++ b/stockton-render/src/draw/texture/mod.rs @@ -16,9 +16,9 @@ //! Everything related to loading textures into GPU memory mod resolver; -mod image; +pub mod image; mod chunk; pub mod loader; pub use self::loader::TextureStore; -pub use self::image::LoadedImage;
\ No newline at end of file +pub use self::image::{LoadedImage, SampledImage};
\ No newline at end of file diff --git a/stockton-render/src/error.rs b/stockton-render/src/error.rs index 5ab0822..8922423 100644 --- a/stockton-render/src/error.rs +++ b/stockton-render/src/error.rs @@ -15,6 +15,8 @@ //! Error types +use super::draw::target::TargetChainCreationError; + /// An error encountered creating a rendering context. /// Falls into 3 main types: /// - Hardware - No suitable card usually @@ -23,6 +25,7 @@ /// - Runtime - Things caused by runtime conditions, usually resource constraints. Could also be caused by corrupt files #[derive(Debug)] pub enum CreationError { + TargetChainCreationError (TargetChainCreationError), WindowError, BadSurface, |