diff options
author | tcmal <me@aria.rip> | 2024-08-25 17:44:23 +0100 |
---|---|---|
committer | tcmal <me@aria.rip> | 2024-08-25 17:44:23 +0100 |
commit | c52a05e6d3977efce6bd4479aa312dc90e0452e5 (patch) | |
tree | c383c910c1467b406e332651906e081c599d6951 /stockton-render/src/draw/context.rs | |
parent | d5b9ccd3d4a6e5b35a0411688bfb7113f5c7d631 (diff) |
feat(render): proper error handling
Diffstat (limited to 'stockton-render/src/draw/context.rs')
-rw-r--r-- | stockton-render/src/draw/context.rs | 247 |
1 files changed, 112 insertions, 135 deletions
diff --git a/stockton-render/src/draw/context.rs b/stockton-render/src/draw/context.rs index 21a69fa..dae04ac 100644 --- a/stockton-render/src/draw/context.rs +++ b/stockton-render/src/draw/context.rs @@ -8,11 +8,11 @@ use std::{ sync::{Arc, RwLock}, }; +use anyhow::{Context, Result}; use arrayvec::ArrayVec; -use hal::{memory::SparseFlags, pool::CommandPoolCreateFlags}; +use hal::pool::CommandPoolCreateFlags; use log::debug; use na::Mat4; -use rendy_memory::DynamicConfig; use winit::window::Window; use super::{ @@ -27,9 +27,12 @@ use super::{ do_render as do_render_ui, ensure_textures as ensure_textures_ui, UiPipeline, UiPoint, UiTextures, }, - utils::find_memory_type_id, }; -use crate::{error, types::*, window::UiState}; +use crate::{ + error::{EnvironmentError, LockPoisoned}, + types::*, + window::UiState, +}; use stockton_levels::prelude::*; /// Contains all the hal related stuff. @@ -76,10 +79,6 @@ pub struct RenderingContext<'a, M: 'static + MinBspFeatures<VulkanSystem>> { /// Buffers used for drawing the UI ui_draw_buffers: ManuallyDrop<DrawBuffers<'a, UiPoint>>, - /// Memory allocator used for any sort of textures / maps - /// Guaranteed suitable for 2D RGBA images with `Optimal` tiling and `Usage::Sampled` - texture_allocator: ManuallyDrop<DynamicAllocator>, - /// View projection matrix pub(crate) vp_matrix: Mat4, @@ -88,15 +87,15 @@ pub struct RenderingContext<'a, M: 'static + MinBspFeatures<VulkanSystem>> { impl<'a, M: 'static + MinBspFeatures<VulkanSystem>> RenderingContext<'a, M> { /// Create a new RenderingContext for the given window. - pub fn new(window: &Window, map: M) -> Result<Self, error::CreationError> { + pub fn new(window: &Window, map: M) -> Result<Self> { let map = Arc::new(RwLock::new(map)); // Create surface let (instance, surface, mut adapters) = unsafe { - let instance = back::Instance::create("stockton", 1) - .map_err(|_| error::CreationError::WindowError)?; + let instance = + back::Instance::create("stockton", 1).context("Error creating vulkan instance")?; let surface = instance .create_surface(window) - .map_err(|_| error::CreationError::WindowError)?; + .context("Error creating surface")?; let adapters = instance.enumerate_adapters(); (instance, surface, adapters) @@ -108,21 +107,22 @@ impl<'a, M: 'static + MinBspFeatures<VulkanSystem>> RenderingContext<'a, M> { let mut draw_queue_negotiator = QueueNegotiator::find(&adapter, |family| { surface.supports_queue_family(family) && family.queue_type().supports_graphics() }) - .unwrap(); + .context("Error creating draw queue negotiator")?; let mut tex_queue_negotiator = - QueueNegotiator::find(&adapter, TextureRepo::queue_family_filter).unwrap(); - // Device & Queue group - let (device_lock, mut queue_groups) = { - debug!( - "Using draw queue family {:?}", - draw_queue_negotiator.family_id() - ); - debug!( - "Using tex queue family {:?}", - tex_queue_negotiator.family_id() - ); + QueueNegotiator::find(&adapter, TextureRepo::queue_family_filter) + .context("Error creating texture queue negotiator")?; + debug!( + "Using draw queue family {:?}", + draw_queue_negotiator.family_id() + ); + debug!( + "Using tex queue family {:?}", + tex_queue_negotiator.family_id() + ); + // Device & Queue groups + let (device_lock, mut queue_groups) = { let gpu = unsafe { adapter .physical_device @@ -133,24 +133,22 @@ impl<'a, M: 'static + MinBspFeatures<VulkanSystem>> RenderingContext<'a, M> { ], hal::Features::empty(), ) - .unwrap() + .context("Error opening logical device")? }; (Arc::new(RwLock::new(gpu.device)), gpu.queue_groups) }; - let mut device = device_lock.write().unwrap(); - - let device_props = adapter.physical_device.properties(); + let mut device = device_lock + .write() + .map_err(|_| LockPoisoned::Device) + .context("Error getting device lock")?; // Figure out what our swapchain will look like let swapchain_properties = SwapchainProperties::find_best(&adapter, &surface) - .map_err(|_| error::CreationError::BadSurface)?; + .context("Error getting properties for swapchain")?; - debug!( - "Detected following swapchain properties: {:?}", - swapchain_properties - ); + debug!("Detected swapchain properties: {:?}", swapchain_properties); // Command pool let mut cmd_pool = unsafe { @@ -159,62 +157,17 @@ impl<'a, M: 'static + MinBspFeatures<VulkanSystem>> RenderingContext<'a, M> { CommandPoolCreateFlags::RESET_INDIVIDUAL, ) } - .map_err(|_| error::CreationError::OutOfMemoryError)?; + .context("Error creating draw command pool")?; // Vertex and index buffers - let draw_buffers = DrawBuffers::new(&mut device, &adapter)?; + let draw_buffers = + DrawBuffers::new(&mut device, &adapter).context("Error creating 3D draw buffers")?; // UI Vertex and index buffers - let ui_draw_buffers = DrawBuffers::new(&mut device, &adapter)?; - - // Memory allocators - let texture_allocator = unsafe { - use hal::{ - format::Format, - image::{Kind, Tiling, Usage, ViewCapabilities}, - memory::Properties, - }; - - // We create an empty image with the same format as used for textures - // this is to get the type_mask required, which will stay the same for - // all colour images of the same tiling. (certain memory flags excluded). - - // Size and alignment don't necessarily stay the same, so we're forced to - // guess at the alignment for our allocator. - - // TODO: Way to tune these options - - let img = device - .create_image( - Kind::D2(16, 16, 1, 1), - 1, - Format::Rgba8Srgb, - Tiling::Optimal, - Usage::SAMPLED, - SparseFlags::empty(), - ViewCapabilities::empty(), - ) - .map_err(|_| error::CreationError::OutOfMemoryError)?; - - let type_mask = device.get_image_requirements(&img).type_mask; - - device.destroy_image(img); - - let props = Properties::DEVICE_LOCAL; - - DynamicAllocator::new( - find_memory_type_id(&adapter, type_mask, props) - .ok_or(error::CreationError::OutOfMemoryError)?, - props, - DynamicConfig { - block_size_granularity: 4 * 32 * 32, // 32x32 image - max_chunk_size: u64::pow(2, 63), - min_device_allocation: 4 * 32 * 32, - }, - device_props.limits.non_coherent_atom_size as u64, - ) - }; + let ui_draw_buffers = + DrawBuffers::new(&mut device, &adapter).context("Error creating UI draw buffers")?; + // We have to unlock device for creating texture repos drop(device); // Texture repos @@ -222,28 +175,34 @@ impl<'a, M: 'static + MinBspFeatures<VulkanSystem>> RenderingContext<'a, M> { let tex_repo = TextureRepo::new( device_lock.clone(), tex_queue_negotiator.family_id(), - tex_queue_negotiator.get_queue(&mut queue_groups).unwrap(), + tex_queue_negotiator + .get_queue(&mut queue_groups) + .ok_or(EnvironmentError::NoQueues) + .context("Error getting 3D texture loader queue")?, &adapter, map.clone(), BasicFsResolver::new(std::path::Path::new(".")), ) - .unwrap(); // TODO + .context("Error creating 3D Texture repo")?; // TODO debug!("Creating UI Texture Repo"); let ui_tex_repo = TextureRepo::new( device_lock.clone(), tex_queue_negotiator.family_id(), - tex_queue_negotiator.get_queue(&mut queue_groups).unwrap(), + tex_queue_negotiator + .get_queue(&mut queue_groups) + .ok_or(EnvironmentError::NoQueues) + .context("Error getting UI texture loader queue")?, &adapter, Arc::new(RwLock::new(UiTextures)), BasicFsResolver::new(std::path::Path::new(".")), ) - .unwrap(); // TODO + .context("Error creating UI texture repo")?; // TODO - let mut device = device_lock.write().unwrap(); + let mut device = device_lock.write().map_err(|_| LockPoisoned::Device)?; - let ds_layout_lock = tex_repo.get_ds_layout(); - let ui_ds_layout_lock = ui_tex_repo.get_ds_layout(); + let ds_layout_lock = tex_repo.get_ds_layout()?; + let ui_ds_layout_lock = ui_tex_repo.get_ds_layout()?; // Graphics pipeline let pipeline = CompletePipeline::new( @@ -271,7 +230,7 @@ impl<'a, M: 'static + MinBspFeatures<VulkanSystem>> RenderingContext<'a, M> { &mut cmd_pool, swapchain_properties, ) - .map_err(error::CreationError::TargetChainCreationError)?; + .context("Error creating target chain")?; drop(device); drop(ds_layout_lock); @@ -284,7 +243,10 @@ impl<'a, M: 'static + MinBspFeatures<VulkanSystem>> RenderingContext<'a, M> { device: device_lock, adapter, - queue: draw_queue_negotiator.get_queue(&mut queue_groups).unwrap(), + queue: draw_queue_negotiator + .get_queue(&mut queue_groups) + .ok_or(EnvironmentError::NoQueues) + .context("Error getting draw queue")?, target_chain: ManuallyDrop::new(target_chain), cmd_pool: ManuallyDrop::new(cmd_pool), @@ -298,8 +260,6 @@ impl<'a, M: 'static + MinBspFeatures<VulkanSystem>> RenderingContext<'a, M> { draw_buffers: ManuallyDrop::new(draw_buffers), ui_draw_buffers: ManuallyDrop::new(ui_draw_buffers), - texture_allocator: ManuallyDrop::new(texture_allocator), - vp_matrix: Mat4::identity(), pixels_per_point: window.scale_factor() as f32, @@ -309,23 +269,29 @@ impl<'a, M: 'static + MinBspFeatures<VulkanSystem>> RenderingContext<'a, M> { /// If this function fails the whole context is probably dead /// # Safety /// The context must not be used while this is being called - pub unsafe fn handle_surface_change(&mut self) -> Result<(), error::CreationError> { - let mut device = self.device.write().unwrap(); + pub unsafe fn handle_surface_change(&mut self) -> Result<()> { + let mut device = self + .device + .write() + .map_err(|_| LockPoisoned::Device) + .context("Error getting device lock")?; - device.wait_idle().unwrap(); + device + .wait_idle() + .context("Error waiting for device to become idle")?; let surface = ManuallyDrop::into_inner(read(&self.target_chain)) .deactivate_with_recyling(&mut device, &mut self.cmd_pool); let properties = SwapchainProperties::find_best(&self.adapter, &surface) - .map_err(|_| error::CreationError::BadSurface)?; + .context("Error finding best swapchain properties")?; use core::ptr::read; // Graphics pipeline // TODO: Recycle - let ds_layout_handle = self.tex_repo.get_ds_layout(); - let ui_ds_layout_handle = self.tex_repo.get_ds_layout(); + let ds_layout_handle = self.tex_repo.get_ds_layout()?; + let ui_ds_layout_handle = self.tex_repo.get_ds_layout()?; ManuallyDrop::into_inner(read(&self.pipeline)).deactivate(&mut device); self.pipeline = ManuallyDrop::new({ @@ -334,7 +300,8 @@ impl<'a, M: 'static + MinBspFeatures<VulkanSystem>> RenderingContext<'a, M> { properties.extent, &properties, once(&*ds_layout_handle), - )? + ) + .context("Error creating 3D Pipeline")? }); // 2D Graphics pipeline @@ -349,7 +316,8 @@ impl<'a, M: 'static + MinBspFeatures<VulkanSystem>> RenderingContext<'a, M> { properties.extent, &properties, once(&*ui_ds_layout_handle), - )? + ) + .context("Error creating UI Pipeline")? }); self.target_chain = ManuallyDrop::new( @@ -362,78 +330,89 @@ impl<'a, M: 'static + MinBspFeatures<VulkanSystem>> RenderingContext<'a, M> { &mut self.cmd_pool, properties, ) - .map_err(error::CreationError::TargetChainCreationError)?, + .context("Error creating target chain")?, ); Ok(()) } /// Draw all vertices in the buffer - pub fn draw_vertices(&mut self, ui: &mut UiState, faces: &[u32]) -> Result<(), &'static str> { - let mut device = self.device.write().unwrap(); - let mut queue = self.queue.write().unwrap(); + pub fn draw_vertices(&mut self, ui: &mut UiState, faces: &[u32]) -> Result<()> { + let mut device = self + .device + .write() + .map_err(|_| LockPoisoned::Device) + .context("Error getting device lock")?; + let mut queue = self + .queue + .write() + .map_err(|_| LockPoisoned::Map) + .context("Error getting map lock")?; // Ensure UI texture(s) are loaded - ensure_textures_ui( - &mut self.ui_tex_repo, - ui, - &mut device, - &mut self.adapter, - &mut self.texture_allocator, - &mut queue, - &mut self.cmd_pool, - ); + ensure_textures_ui(&mut self.ui_tex_repo, ui)?; // Get any textures that just finished loading self.tex_repo.process_responses(); // 3D Pass - let (cmd_buffer, img) = self.target_chain.prep_next_target( - &mut device, - &mut self.draw_buffers, - &self.pipeline, - &self.vp_matrix, - )?; + let (cmd_buffer, img) = self + .target_chain + .prep_next_target( + &mut device, + &mut self.draw_buffers, + &self.pipeline, + &self.vp_matrix, + ) + .context("Error preparing next target")?; + do_render( cmd_buffer, &mut self.draw_buffers, &mut self.tex_repo, &self.pipeline.pipeline_layout, - &*self.map.read().unwrap(), + &*self + .map + .read() + .map_err(|_| LockPoisoned::Map) + .context("Error getting map read lock")?, faces, - ); + )?; // 2D Pass - let cmd_buffer = - self.target_chain - .target_2d_pass(&mut self.ui_draw_buffers, &img, &self.ui_pipeline)?; + let cmd_buffer = self + .target_chain + .target_2d_pass(&mut self.ui_draw_buffers, &img, &self.ui_pipeline) + .context("Error switching to 2D pass")?; + do_render_ui( cmd_buffer, &self.ui_pipeline.pipeline_layout, &mut self.ui_draw_buffers, &mut self.ui_tex_repo, ui, - ); + )?; // Update our buffers before we actually start drawing self.draw_buffers .vertex_buffer - .commit(&device, &mut queue, &mut self.cmd_pool); + .commit(&device, &mut queue, &mut self.cmd_pool)?; self.draw_buffers .index_buffer - .commit(&device, &mut queue, &mut self.cmd_pool); + .commit(&device, &mut queue, &mut self.cmd_pool)?; self.ui_draw_buffers .vertex_buffer - .commit(&device, &mut queue, &mut self.cmd_pool); + .commit(&device, &mut queue, &mut self.cmd_pool)?; self.ui_draw_buffers .index_buffer - .commit(&device, &mut queue, &mut self.cmd_pool); + .commit(&device, &mut queue, &mut self.cmd_pool)?; // Send commands off to GPU self.target_chain - .finish_and_submit_target(img, &mut queue)?; + .finish_and_submit_target(img, &mut queue) + .context("Error finishing and submitting target")?; Ok(()) } @@ -456,8 +435,6 @@ impl<'a, M: MinBspFeatures<VulkanSystem>> core::ops::Drop for RenderingContext<' ManuallyDrop::into_inner(read(&self.draw_buffers)).deactivate(&mut device); ManuallyDrop::into_inner(read(&self.ui_draw_buffers)).deactivate(&mut device); - ManuallyDrop::into_inner(read(&self.texture_allocator)).dispose(); - ManuallyDrop::into_inner(read(&self.target_chain)).deactivate( &mut self.instance, &mut device, |