From c52a05e6d3977efce6bd4479aa312dc90e0452e5 Mon Sep 17 00:00:00 2001 From: tcmal Date: Sun, 25 Aug 2024 17:44:23 +0100 Subject: feat(render): proper error handling --- stockton-render/src/draw/buffer.rs | 57 +++---- stockton-render/src/draw/context.rs | 247 ++++++++++++--------------- stockton-render/src/draw/depth_buffer.rs | 46 ++--- stockton-render/src/draw/draw_buffers.rs | 14 +- stockton-render/src/draw/pipeline.rs | 22 +-- stockton-render/src/draw/queue_negotiator.rs | 11 +- stockton-render/src/draw/render.rs | 27 +-- stockton-render/src/draw/target.rs | 139 ++++++--------- stockton-render/src/draw/texture/loader.rs | 16 +- stockton-render/src/draw/texture/repo.rs | 7 +- stockton-render/src/draw/ui/pipeline.rs | 22 +-- stockton-render/src/draw/ui/render.rs | 11 +- stockton-render/src/draw/ui/texture.rs | 16 +- stockton-render/src/error.rs | 77 +++++---- stockton-render/src/lib.rs | 40 +++-- stockton-render/src/window.rs | 8 +- 16 files changed, 371 insertions(+), 389 deletions(-) (limited to 'stockton-render') diff --git a/stockton-render/src/draw/buffer.rs b/stockton-render/src/draw/buffer.rs index 227bb12..77ac38a 100644 --- a/stockton-render/src/draw/buffer.rs +++ b/stockton-render/src/draw/buffer.rs @@ -3,14 +3,14 @@ use std::convert::TryInto; use std::iter::{empty, once}; use std::ops::{Index, IndexMut}; +use anyhow::{Context, Result}; use hal::{ buffer::Usage, memory::{Properties, Segment, SparseFlags}, MemoryTypeId, }; -use crate::error::CreationError; -use crate::types::*; +use crate::{error::EnvironmentError, types::*}; /// Create a buffer of the given specifications, allocating more device memory. // TODO: Use a different memory allocator? @@ -20,9 +20,9 @@ pub(crate) fn create_buffer( usage: Usage, properties: Properties, size: u64, -) -> Result<(BufferT, MemoryT), CreationError> { +) -> Result<(BufferT, MemoryT)> { let mut buffer = unsafe { device.create_buffer(size, usage, SparseFlags::empty()) } - .map_err(CreationError::BufferError)?; + .context("Error creating buffer")?; let requirements = unsafe { device.get_buffer_requirements(&buffer) }; let memory_type_id = adapter @@ -35,13 +35,13 @@ pub(crate) fn create_buffer( requirements.type_mask & (1 << id) != 0 && memory_type.properties.contains(properties) }) .map(|(id, _)| MemoryTypeId(id)) - .ok_or(CreationError::BufferNoMemory)?; + .ok_or(EnvironmentError::NoMemoryTypes)?; let memory = unsafe { device.allocate_memory(memory_type_id, requirements.size) } - .map_err(|_| CreationError::OutOfMemoryError)?; + .context("Error allocating memory")?; unsafe { device.bind_buffer_memory(&memory, 0, &mut buffer) } - .map_err(|_| CreationError::BufferNoMemory)?; + .context("Error binding memory to buffer")?; Ok((buffer, memory)) } @@ -57,7 +57,7 @@ pub trait ModifiableBuffer: IndexMut { device: &DeviceT, command_queue: &mut QueueT, command_pool: &mut CommandPoolT, - ) -> &'a BufferT; + ) -> Result<&'a BufferT>; } /// A GPU buffer that is written to using a staging buffer @@ -86,12 +86,7 @@ pub struct StagedBuffer<'a, T: Sized> { impl<'a, T: Sized> StagedBuffer<'a, T> { /// size is the size in T - pub fn new( - device: &mut DeviceT, - adapter: &Adapter, - usage: Usage, - size: u64, - ) -> Result { + pub fn new(device: &mut DeviceT, adapter: &Adapter, usage: Usage, size: u64) -> Result { // Convert size to bytes let size_bytes = size * size_of::() as u64; @@ -102,7 +97,8 @@ impl<'a, T: Sized> StagedBuffer<'a, T> { Usage::TRANSFER_SRC, Properties::CPU_VISIBLE, size_bytes, - )?; + ) + .context("Error creating staging buffer")?; // Get GPU Buffer let (buffer, memory) = create_buffer( @@ -111,7 +107,8 @@ impl<'a, T: Sized> StagedBuffer<'a, T> { Usage::TRANSFER_DST | usage, Properties::DEVICE_LOCAL | Properties::COHERENT, size_bytes, - )?; + ) + .context("Error creating GPU buffer")?; // Map it somewhere and get a slice to that memory let staged_mapped_memory = unsafe { @@ -123,9 +120,9 @@ impl<'a, T: Sized> StagedBuffer<'a, T> { size: Some(size_bytes), }, ) - .unwrap(); // TODO + .context("Error mapping staged memory")?; - std::slice::from_raw_parts_mut(ptr as *mut T, size.try_into().unwrap()) + std::slice::from_raw_parts_mut(ptr as *mut T, size.try_into()?) }; Ok(StagedBuffer { @@ -163,7 +160,7 @@ impl<'a, T: Sized> ModifiableBuffer for StagedBuffer<'a, T> { device: &DeviceT, command_queue: &mut QueueT, command_pool: &mut CommandPoolT, - ) -> &'b BufferT { + ) -> Result<&'b BufferT> { // Only commit if there's changes to commit. if self.staged_is_dirty { // Copy from staged to buffer @@ -189,21 +186,19 @@ impl<'a, T: Sized> ModifiableBuffer for StagedBuffer<'a, T> { }; // Submit it and wait for completion - // TODO: We could use more semaphores or something? - // TODO: Better error handling + // TODO: Proper management of transfer operations unsafe { - let mut copy_finished = device.create_fence(false).unwrap(); - command_queue - .submit::, std::iter::Empty<_>, std::iter::Empty<_>>( - once(&buf), - empty::<(&SemaphoreT, hal::pso::PipelineStage)>(), - empty::<&SemaphoreT>(), - Some(&mut copy_finished), - ); + let mut copy_finished = device.create_fence(false)?; + command_queue.submit( + once(&buf), + empty::<(&SemaphoreT, hal::pso::PipelineStage)>(), + empty::<&SemaphoreT>(), + Some(&mut copy_finished), + ); device .wait_for_fence(©_finished, core::u64::MAX) - .unwrap(); + .context("Error waiting for fence")?; // Destroy temporary resources device.destroy_fence(copy_finished); @@ -213,7 +208,7 @@ impl<'a, T: Sized> ModifiableBuffer for StagedBuffer<'a, T> { self.staged_is_dirty = false; } - &self.buffer + Ok(&self.buffer) } } 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> { /// Buffers used for drawing the UI ui_draw_buffers: ManuallyDrop>, - /// Memory allocator used for any sort of textures / maps - /// Guaranteed suitable for 2D RGBA images with `Optimal` tiling and `Usage::Sampled` - texture_allocator: ManuallyDrop, - /// View projection matrix pub(crate) vp_matrix: Mat4, @@ -88,15 +87,15 @@ pub struct RenderingContext<'a, M: 'static + MinBspFeatures> { impl<'a, M: 'static + MinBspFeatures> RenderingContext<'a, M> { /// Create a new RenderingContext for the given window. - pub fn new(window: &Window, map: M) -> Result { + pub fn new(window: &Window, map: M) -> Result { 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> 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> 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> 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> 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> 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> 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> 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> 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> 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> 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> 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> 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, diff --git a/stockton-render/src/draw/depth_buffer.rs b/stockton-render/src/draw/depth_buffer.rs index 8af1514..e3306c5 100644 --- a/stockton-render/src/draw/depth_buffer.rs +++ b/stockton-render/src/draw/depth_buffer.rs @@ -10,7 +10,9 @@ use hal::{ use std::{array::IntoIter, convert::TryInto, iter::empty}; use crate::types::*; +use anyhow::{Context, Result}; use std::mem::ManuallyDrop; +use thiserror::Error; use super::texture::{LoadableImage, PIXEL_SIZE}; @@ -26,6 +28,12 @@ pub struct DedicatedLoadedImage { memory: ManuallyDrop, } +#[derive(Debug, Error)] +pub enum ImageLoadError { + #[error("No suitable memory type for image memory")] + NoMemoryTypes, +} + impl DedicatedLoadedImage { pub fn new( device: &mut DeviceT, @@ -35,7 +43,7 @@ impl DedicatedLoadedImage { resources: SubresourceRange, width: usize, height: usize, - ) -> Result { + ) -> Result { let (memory, image_ref) = { // Round up the size to align properly let initial_row_size = PIXEL_SIZE * width; @@ -60,9 +68,7 @@ impl DedicatedLoadedImage { ViewCapabilities::empty(), ) } - .map_err(|_| "Couldn't create image")?; - - // Allocate memory + .context("Error creating image")?; // Allocate memory let memory = unsafe { @@ -79,21 +85,21 @@ impl DedicatedLoadedImage { && memory_type.properties.contains(Properties::DEVICE_LOCAL) }) .map(|(id, _)| MemoryTypeId(id)) - .ok_or("Couldn't find a memory type for image memory")?; + .ok_or(ImageLoadError::NoMemoryTypes)?; let memory = device .allocate_memory(memory_type_id, requirements.size) - .map_err(|_| "Couldn't allocate image memory")?; + .context("Error allocating memory for image")?; device .bind_image_memory(&memory, 0, &mut image_ref) - .map_err(|_| "Couldn't bind memory to image")?; + .context("Error binding memory to image")?; - Ok(memory) - }?; + memory + }; - Ok((memory, image_ref)) - }?; + (memory, image_ref) + }; // Create ImageView and sampler let image_view = unsafe { @@ -106,7 +112,7 @@ impl DedicatedLoadedImage { resources, ) } - .map_err(|_| "Couldn't create the image view!")?; + .context("Error creating image view")?; Ok(DedicatedLoadedImage { image: ManuallyDrop::new(image_ref), @@ -123,7 +129,7 @@ impl DedicatedLoadedImage { adapter: &Adapter, command_queue: &mut QueueT, command_pool: &mut CommandPoolT, - ) -> Result<(), &'static str> { + ) -> Result<()> { let initial_row_size = PIXEL_SIZE * img.width() as usize; let limits = adapter.physical_device.properties().limits; let row_alignment_mask = limits.optimal_buffer_copy_pitch_alignment as u32 - 1; @@ -141,7 +147,7 @@ impl DedicatedLoadedImage { memory::Properties::CPU_VISIBLE | memory::Properties::COHERENT, total_size, ) - .map_err(|_| "Couldn't create staging buffer")?; + .context("Error creating staging buffer")?; // Copy everything into it unsafe { @@ -154,11 +160,11 @@ impl DedicatedLoadedImage { size: None, }, ) - .map_err(|_| "Couldn't map buffer memory")?, + .context("Error mapping staging memory")?, ); for y in 0..img.height() as usize { - let dest_base: isize = (y * row_size).try_into().unwrap(); + let dest_base: isize = (y * row_size).try_into()?; img.copy_row(y as u32, mapped_memory.offset(dest_base)); } @@ -247,7 +253,9 @@ impl DedicatedLoadedImage { // Submit our commands and wait for them to finish unsafe { - let mut setup_finished = device.create_fence(false).unwrap(); + let mut setup_finished = device + .create_fence(false) + .context("Error creating setup_finished fence")?; command_queue.submit( IntoIter::new([&buf]), empty(), @@ -257,7 +265,7 @@ impl DedicatedLoadedImage { device .wait_for_fence(&setup_finished, core::u64::MAX) - .unwrap(); + .context("Error waiting for image load to finish")?; device.destroy_fence(setup_finished); }; @@ -281,7 +289,7 @@ impl DedicatedLoadedImage { command_pool: &mut CommandPoolT, format: Format, usage: Usage, - ) -> Result { + ) -> Result { let mut loaded_image = Self::new( device, adapter, diff --git a/stockton-render/src/draw/draw_buffers.rs b/stockton-render/src/draw/draw_buffers.rs index 67687dd..fba3eed 100644 --- a/stockton-render/src/draw/draw_buffers.rs +++ b/stockton-render/src/draw/draw_buffers.rs @@ -1,4 +1,5 @@ -use crate::{draw::buffer::StagedBuffer, error::CreationError, types::*}; +use crate::{draw::buffer::StagedBuffer, types::*}; +use anyhow::{Context, Result}; use hal::buffer::Usage; use std::mem::ManuallyDrop; use stockton_types::{Vector2, Vector3}; @@ -20,12 +21,11 @@ pub struct DrawBuffers<'a, T: Sized> { } impl<'a, T> DrawBuffers<'a, T> { - pub fn new( - device: &mut DeviceT, - adapter: &Adapter, - ) -> Result, CreationError> { - let vert = StagedBuffer::new(device, &adapter, Usage::VERTEX, INITIAL_VERT_SIZE)?; - let index = StagedBuffer::new(device, &adapter, Usage::INDEX, INITIAL_INDEX_SIZE)?; + pub fn new(device: &mut DeviceT, adapter: &Adapter) -> Result> { + let vert = StagedBuffer::new(device, &adapter, Usage::VERTEX, INITIAL_VERT_SIZE) + .context("Error creating vertex buffer")?; + let index = StagedBuffer::new(device, &adapter, Usage::INDEX, INITIAL_INDEX_SIZE) + .context("Error creating index buffer")?; Ok(DrawBuffers { vertex_buffer: ManuallyDrop::new(vert), diff --git a/stockton-render/src/draw/pipeline.rs b/stockton-render/src/draw/pipeline.rs index 0a02947..84b541f 100644 --- a/stockton-render/src/draw/pipeline.rs +++ b/stockton-render/src/draw/pipeline.rs @@ -16,8 +16,8 @@ use std::{ }; use super::target::SwapchainProperties; -use crate::error; -use crate::types::*; +use crate::{error::EnvironmentError, types::*}; +use anyhow::{Context, Result}; // TODO: Generalise so we can use for UI also /// A complete graphics pipeline and associated resources @@ -44,7 +44,7 @@ impl CompletePipeline { extent: hal::image::Extent, swapchain_properties: &SwapchainProperties, set_layouts: T, - ) -> Result { + ) -> Result { use hal::format::Format; use hal::pso::*; @@ -89,7 +89,7 @@ impl CompletePipeline { empty(), ) } - .map_err(|_| error::CreationError::OutOfMemoryError)? + .context("Error creating render pass")? }; // Subpass @@ -100,7 +100,7 @@ impl CompletePipeline { // Shader modules let (vs_module, fs_module) = { - let mut compiler = shaderc::Compiler::new().ok_or(error::CreationError::NoShaderC)?; + let mut compiler = shaderc::Compiler::new().ok_or(EnvironmentError::NoShaderC)?; let vertex_compile_artifact = compiler .compile_into_spirv( @@ -110,7 +110,7 @@ impl CompletePipeline { ENTRY_NAME, None, ) - .map_err(error::CreationError::ShaderCError)?; + .context("Error compiling vertex shader")?; let fragment_compile_artifact = compiler .compile_into_spirv( @@ -120,17 +120,17 @@ impl CompletePipeline { ENTRY_NAME, None, ) - .map_err(error::CreationError::ShaderCError)?; + .context("Error compiling fragment shader")?; // Make into shader module unsafe { ( device .create_shader_module(vertex_compile_artifact.as_binary()) - .map_err(error::CreationError::ShaderModuleFailed)?, + .context("Error creating vertex shader module")?, device .create_shader_module(fragment_compile_artifact.as_binary()) - .map_err(error::CreationError::ShaderModuleFailed)?, + .context("Error creating fragment shader module")?, ) } }; @@ -178,7 +178,7 @@ impl CompletePipeline { IntoIter::new([(ShaderStageFlags::VERTEX, 0..64)]), ) } - .map_err(|_| error::CreationError::OutOfMemoryError)?; + .context("Error creating pipeline layout")?; // Colour blending let blender = { @@ -270,7 +270,7 @@ impl CompletePipeline { // Pipeline let pipeline = unsafe { device.create_graphics_pipeline(&pipeline_desc, None) } - .map_err(error::CreationError::PipelineError)?; + .context("Error creating graphics pipeline")?; Ok(CompletePipeline { renderpass: ManuallyDrop::new(renderpass), diff --git a/stockton-render/src/draw/queue_negotiator.rs b/stockton-render/src/draw/queue_negotiator.rs index b128846..4ad6823 100644 --- a/stockton-render/src/draw/queue_negotiator.rs +++ b/stockton-render/src/draw/queue_negotiator.rs @@ -1,8 +1,7 @@ -use crate::types::*; +use crate::{error::EnvironmentError, types::*}; use anyhow::Result; use hal::queue::family::QueueFamilyId; use std::sync::{Arc, RwLock}; -use thiserror::Error; pub struct QueueNegotiator { family_id: QueueFamilyId, @@ -16,7 +15,7 @@ impl QueueNegotiator { .queue_families .iter() .find(filter) - .ok_or(QueueNegotiatorError::NoSuitableFamilies)?; + .ok_or(EnvironmentError::NoSuitableFamilies)?; Ok(QueueNegotiator { family_id: family.id(), @@ -64,9 +63,3 @@ impl QueueNegotiator { } } } - -#[derive(Error, Debug)] -pub enum QueueNegotiatorError { - #[error("No suitable queue families found")] - NoSuitableFamilies, -} diff --git a/stockton-render/src/draw/render.rs b/stockton-render/src/draw/render.rs index 2cbdef4..ac18dea 100644 --- a/stockton-render/src/draw/render.rs +++ b/stockton-render/src/draw/render.rs @@ -11,6 +11,7 @@ use stockton_types::Vector2; use crate::draw::draw_buffers::DrawBuffers; use crate::types::*; +use anyhow::Result; use super::texture::TextureRepo; @@ -21,15 +22,17 @@ fn draw_or_queue( pipeline_layout: &PipelineLayoutT, chunk_start: u32, curr_idx_idx: u32, -) { +) -> Result<()> { if let Some(ds) = tex_repo.attempt_get_descriptor_set(current_chunk) { unsafe { cmd_buffer.bind_graphics_descriptor_sets(pipeline_layout, 0, once(ds), empty()); cmd_buffer.draw_indexed(chunk_start * 3..(curr_idx_idx * 3) + 1, 0, 0..1); } } else { - tex_repo.queue_load(current_chunk).unwrap() + tex_repo.queue_load(current_chunk)? } + + Ok(()) } pub fn do_render>( @@ -39,7 +42,7 @@ pub fn do_render>( pipeline_layout: &PipelineLayoutT, file: &M, faces: &[u32], -) { +) -> Result<()> { // 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; @@ -57,7 +60,7 @@ pub fn do_render>( pipeline_layout, chunk_start as u32, curr_idx_idx as u32, - ); + )?; // Next group of same-chunked faces starts here. chunk_start = curr_idx_idx; @@ -69,13 +72,13 @@ pub fn do_render>( 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(); + let start_idx: u16 = curr_vert_idx.try_into()?; 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); + let uvp = UvPoint(vert.position, face.texture_idx.try_into()?, uv); draw_buffers.vertex_buffer[curr_vert_idx] = uvp; curr_vert_idx += 1; @@ -85,8 +88,8 @@ pub fn do_render>( curr_idx_idx += 1; - if curr_vert_idx >= INITIAL_VERT_SIZE.try_into().unwrap() - || curr_idx_idx >= INITIAL_INDEX_SIZE.try_into().unwrap() + if curr_vert_idx >= INITIAL_VERT_SIZE.try_into()? + || curr_idx_idx >= INITIAL_INDEX_SIZE.try_into()? { println!("out of vertex buffer space!"); break; @@ -96,8 +99,8 @@ pub fn do_render>( // TODO: Other types of faces } - if curr_vert_idx >= INITIAL_VERT_SIZE.try_into().unwrap() - || curr_idx_idx >= INITIAL_INDEX_SIZE.try_into().unwrap() + if curr_vert_idx >= INITIAL_VERT_SIZE.try_into()? + || curr_idx_idx >= INITIAL_INDEX_SIZE.try_into()? { println!("out of vertex buffer space!"); break; @@ -112,5 +115,7 @@ pub fn do_render>( pipeline_layout, chunk_start as u32, curr_idx_idx as u32, - ); + )?; + + Ok(()) } diff --git a/stockton-render/src/draw/target.rs b/stockton-render/src/draw/target.rs index 8d308d9..23c15cc 100644 --- a/stockton-render/src/draw/target.rs +++ b/stockton-render/src/draw/target.rs @@ -11,7 +11,7 @@ use arrayvec::ArrayVec; use hal::{ buffer::SubRange, command::RenderAttachmentInfo, - format::{ChannelType, Format}, + format::{ChannelType, Format, ImageFeature}, image::{Extent, FramebufferAttachment, Usage as ImgUsage, ViewCapabilities}, pso::Viewport, window::{CompositeAlphaMode, Extent2D, PresentMode, SwapchainConfig}, @@ -25,7 +25,8 @@ use super::{ pipeline::CompletePipeline, ui::{UiPipeline, UiPoint}, }; -use crate::types::*; +use crate::{error::EnvironmentError, types::*}; +use anyhow::{Context, Result}; #[derive(Debug, Clone)] pub struct SwapchainProperties { @@ -37,29 +38,23 @@ pub struct SwapchainProperties { pub extent: Extent, } -/// Indicates the given property has no acceptable values -pub enum NoSupportedValuesError { - DepthFormat, - PresentMode, - CompositeAlphaMode, -} - impl SwapchainProperties { pub fn find_best( adapter: &Adapter, surface: &SurfaceT, - ) -> Result { + ) -> Result { 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 + let format = match formats { + Some(formats) => formats .iter() .find(|format| format.base_format().1 == ChannelType::Srgb) .copied() - .unwrap_or(formats[0]) - }); + .ok_or(EnvironmentError::ColorFormat), + None => Ok(Format::Rgba8Srgb), + }?; let depth_format = *[ Format::D32SfloatS8Uint, @@ -68,8 +63,6 @@ impl SwapchainProperties { ] .iter() .find(|format| { - use hal::format::ImageFeature; - format.is_depth() && adapter .physical_device @@ -77,32 +70,29 @@ impl SwapchainProperties { .optimal_tiling .contains(ImageFeature::DEPTH_STENCIL_ATTACHMENT) }) - .ok_or(NoSupportedValuesError::DepthFormat)?; - - let present_mode = { - [ - PresentMode::MAILBOX, - PresentMode::FIFO, - PresentMode::RELAXED, - PresentMode::IMMEDIATE, - ] - .iter() - .cloned() - .find(|pm| caps.present_modes.contains(*pm)) - .ok_or(NoSupportedValuesError::PresentMode)? - }; - let composite_alpha_mode = { - [ - CompositeAlphaMode::OPAQUE, - CompositeAlphaMode::INHERIT, - CompositeAlphaMode::PREMULTIPLIED, - CompositeAlphaMode::POSTMULTIPLIED, - ] - .iter() - .cloned() - .find(|ca| caps.composite_alpha_modes.contains(*ca)) - .ok_or(NoSupportedValuesError::CompositeAlphaMode)? - }; + .ok_or(EnvironmentError::DepthFormat)?; + + let present_mode = [ + PresentMode::MAILBOX, + PresentMode::FIFO, + PresentMode::RELAXED, + PresentMode::IMMEDIATE, + ] + .iter() + .cloned() + .find(|pm| caps.present_modes.contains(*pm)) + .ok_or(EnvironmentError::PresentMode)?; + + let composite_alpha_mode = [ + CompositeAlphaMode::OPAQUE, + CompositeAlphaMode::INHERIT, + CompositeAlphaMode::PREMULTIPLIED, + CompositeAlphaMode::POSTMULTIPLIED, + ] + .iter() + .cloned() + .find(|ca| caps.composite_alpha_modes.contains(*ca)) + .ok_or(EnvironmentError::CompositeAlphaMode)?; let extent = caps.extents.end().to_extent(); // Size let viewport = Viewport { @@ -153,7 +143,7 @@ impl TargetChain { ui_pipeline: &UiPipeline, cmd_pool: &mut CommandPoolT, properties: SwapchainProperties, - ) -> Result { + ) -> Result { let caps = surface.capabilities(&adapter.physical_device); // Number of frames to pre-render @@ -196,14 +186,15 @@ impl TargetChain { properties.extent.width as usize, properties.extent.height as usize, ) - .map_err(|_| TargetChainCreationError::Todo) - }?; + .context("Error creating depth buffer")? + }; let fat = swap_config.framebuffer_attachment(); let mut targets: Vec = Vec::with_capacity(swap_config.image_count as usize); let mut sync_objects: Vec = Vec::with_capacity(swap_config.image_count as usize); + for _ in 0..swap_config.image_count { targets.push( TargetResources::new( @@ -219,18 +210,17 @@ impl TargetChain { }, &properties, ) - .map_err(|_| TargetChainCreationError::Todo)?, + .context("Error creating target resources")?, ); - sync_objects - .push(SyncObjects::new(device).map_err(|_| TargetChainCreationError::Todo)?); + sync_objects.push(SyncObjects::new(device).context("Error creating sync objects")?); } // Configure Swapchain unsafe { surface .configure_swapchain(device, swap_config) - .map_err(|_| TargetChainCreationError::Todo)?; + .context("Error configuring swapchain")?; } Ok(TargetChain { @@ -286,36 +276,31 @@ impl TargetChain { draw_buffers: &mut DrawBuffers, pipeline: &CompletePipeline, vp: &Mat4, - ) -> Result< - ( - &'a mut crate::types::CommandBufferT, - >::SwapchainImage, - ), - &'static str, - > { + ) -> Result<( + &'a mut crate::types::CommandBufferT, + >::SwapchainImage, + )> { self.last_syncs = (self.last_syncs + 1) % self.sync_objects.len(); - - let syncs = &mut self.sync_objects[self.last_syncs]; - self.last_image = (self.last_image + 1) % self.targets.len() as u32; + let syncs = &mut self.sync_objects[self.last_syncs]; let target = &mut self.targets[self.last_image as usize]; // Get the image let (img, _) = unsafe { self.surface .acquire_image(core::u64::MAX) - .map_err(|_| "FrameError::AcquireError")? + .context("Error getting image from swapchain")? }; // Make sure whatever was last using this has finished unsafe { device .wait_for_fence(&syncs.present_complete, core::u64::MAX) - .map_err(|_| "FrameError::SyncObjectError")?; + .context("Error waiting for present_complete")?; device .reset_fence(&mut syncs.present_complete) - .map_err(|_| "FrameError::SyncObjectError")?; + .context("Error resetting present_complete fence")?; }; // Record commands @@ -403,7 +388,7 @@ impl TargetChain { draw_buffers: &mut DrawBuffers, img: &>::SwapchainImage, pipeline: &UiPipeline, - ) -> Result<&'a mut CommandBufferT, &'static str> { + ) -> Result<&'a mut CommandBufferT> { let target = &mut self.targets[self.last_image as usize]; unsafe { @@ -476,7 +461,7 @@ impl TargetChain { &mut self, img: >::SwapchainImage, command_queue: &mut QueueT, - ) -> Result<(), &'static str> { + ) -> Result<()> { let syncs = &mut self.sync_objects[self.last_syncs]; let target = &mut self.targets[self.last_image as usize]; @@ -495,7 +480,7 @@ impl TargetChain { ); command_queue .present(&mut self.surface, img, Some(&mut *syncs.render_complete)) - .map_err(|_| "FrameError::PresentError")?; + .context("Error presenting to surface")?; }; Ok(()) @@ -523,7 +508,7 @@ impl TargetResources { fat: FramebufferAttachment, dat: FramebufferAttachment, properties: &SwapchainProperties, - ) -> Result { + ) -> Result { // Command Buffer let cmd_buffer = unsafe { cmd_pool.allocate_one(hal::command::Level::Primary) }; @@ -535,14 +520,14 @@ impl TargetResources { IntoIter::new([fat.clone(), dat]), properties.extent, ) - .map_err(|_| TargetResourcesCreationError::FrameBufferNoMemory)? + .context("Error creating colour framebuffer")? }; // 2D framebuffer just needs the imageview, not the depth pass let framebuffer_2d = unsafe { device .create_framebuffer(&renderpass_2d, once(fat), properties.extent) - .map_err(|_| TargetResourcesCreationError::FrameBufferNoMemory)? + .context("Error creating depth framebuffer")? }; Ok(TargetResources { @@ -572,14 +557,14 @@ pub struct SyncObjects { } impl SyncObjects { - pub fn new(device: &mut DeviceT) -> Result { + pub fn new(device: &mut DeviceT) -> Result { // Sync objects let render_complete = device .create_semaphore() - .map_err(|_| TargetResourcesCreationError::SyncObjectsNoMemory)?; + .context("Error creating render_complete semaphore")?; let present_complete = device .create_fence(true) - .map_err(|_| TargetResourcesCreationError::SyncObjectsNoMemory)?; + .context("Error creating present_complete fence")?; Ok(SyncObjects { render_complete: ManuallyDrop::new(render_complete), @@ -596,15 +581,3 @@ impl SyncObjects { } } } - -#[derive(Debug)] -pub enum TargetChainCreationError { - Todo, -} - -#[derive(Debug)] -pub enum TargetResourcesCreationError { - ImageViewError, - FrameBufferNoMemory, - SyncObjectsNoMemory, -} diff --git a/stockton-render/src/draw/texture/loader.rs b/stockton-render/src/draw/texture/loader.rs index 2198fe4..3d7d32e 100644 --- a/stockton-render/src/draw/texture/loader.rs +++ b/stockton-render/src/draw/texture/loader.rs @@ -147,13 +147,14 @@ impl, I: LoadableImage> TextureLoader 0 { - let buf = staging_bufs.pop().unwrap(); + for buf in staging_bufs.drain(..) { buf.deactivate(&mut device, &mut self.staging_allocator); } self.buffers.push_back(assets); - self.return_channel.send(block).unwrap(); + self.return_channel + .send(block) + .context("Error returning texture block")?; } else { i += 1; } @@ -365,7 +366,11 @@ impl, I: LoadableImage> TextureLoader, I: LoadableImage> TextureLoader 0 { - let buf = staging_bufs.pop().unwrap(); + for buf in staging_bufs.drain(..) { buf.deactivate(&mut device, &mut self.staging_allocator); } diff --git a/stockton-render/src/draw/texture/repo.rs b/stockton-render/src/draw/texture/repo.rs index d5200be..8191f7b 100644 --- a/stockton-render/src/draw/texture/repo.rs +++ b/stockton-render/src/draw/texture/repo.rs @@ -129,8 +129,11 @@ impl<'a> TextureRepo<'a> { }) } - pub fn get_ds_layout(&self) -> RwLockReadGuard { - self.ds_layout.read().unwrap() + pub fn get_ds_layout(&self) -> Result> { + self.ds_layout + .read() + .map_err(|_| LockPoisoned::Other) + .context("Error locking descriptor set layout") } pub fn queue_load(&mut self, block_id: BlockRef) -> Result<()> { diff --git a/stockton-render/src/draw/ui/pipeline.rs b/stockton-render/src/draw/ui/pipeline.rs index 757c978..2a8b9fc 100644 --- a/stockton-render/src/draw/ui/pipeline.rs +++ b/stockton-render/src/draw/ui/pipeline.rs @@ -16,8 +16,8 @@ use std::{ }; use crate::draw::target::SwapchainProperties; -use crate::error; -use crate::types::*; +use crate::{error::EnvironmentError, types::*}; +use anyhow::{Context, Result}; /// A complete 2D graphics pipeline and associated resources pub struct UiPipeline { @@ -43,7 +43,7 @@ impl UiPipeline { extent: hal::image::Extent, swapchain_properties: &SwapchainProperties, set_layouts: T, - ) -> Result { + ) -> Result { use hal::format::Format; use hal::pso::*; @@ -91,7 +91,7 @@ impl UiPipeline { IntoIter::new([external_dependency]), ) } - .map_err(|_| error::CreationError::OutOfMemoryError)? + .context("Error creating render pass")? }; // Subpass @@ -102,7 +102,7 @@ impl UiPipeline { // Shader modules let (vs_module, fs_module) = { - let mut compiler = shaderc::Compiler::new().ok_or(error::CreationError::NoShaderC)?; + let mut compiler = shaderc::Compiler::new().ok_or(EnvironmentError::NoShaderC)?; let vertex_compile_artifact = compiler .compile_into_spirv( @@ -112,7 +112,7 @@ impl UiPipeline { ENTRY_NAME, None, ) - .map_err(error::CreationError::ShaderCError)?; + .context("Error compiling vertex shader")?; let fragment_compile_artifact = compiler .compile_into_spirv( @@ -122,17 +122,17 @@ impl UiPipeline { ENTRY_NAME, None, ) - .map_err(error::CreationError::ShaderCError)?; + .context("Error compiling fragment shader")?; // Make into shader module unsafe { ( device .create_shader_module(vertex_compile_artifact.as_binary()) - .map_err(error::CreationError::ShaderModuleFailed)?, + .context("Error creating vertex shader module")?, device .create_shader_module(fragment_compile_artifact.as_binary()) - .map_err(error::CreationError::ShaderModuleFailed)?, + .context("Error creating fragment shader module")?, ) } }; @@ -173,7 +173,7 @@ impl UiPipeline { let layout = unsafe { device.create_pipeline_layout(set_layouts, once((ShaderStageFlags::VERTEX, 0..8))) } - .map_err(|_| error::CreationError::OutOfMemoryError)?; + .context("Error creating pipeline layout")?; // Colour blending let blender = { @@ -265,7 +265,7 @@ impl UiPipeline { // Pipeline let pipeline = unsafe { device.create_graphics_pipeline(&pipeline_desc, None) } - .map_err(error::CreationError::PipelineError)?; + .context("Error creating graphics pipeline")?; Ok(UiPipeline { renderpass: ManuallyDrop::new(renderpass), diff --git a/stockton-render/src/draw/ui/render.rs b/stockton-render/src/draw/ui/render.rs index 62a13bd..40cc733 100644 --- a/stockton-render/src/draw/ui/render.rs +++ b/stockton-render/src/draw/ui/render.rs @@ -5,6 +5,7 @@ use super::UiPoint; use crate::draw::draw_buffers::DrawBuffers; use crate::types::*; use crate::UiState; +use anyhow::Result; use std::{array::IntoIter, convert::TryInto, iter::empty}; use stockton_types::Vector2; @@ -14,7 +15,7 @@ pub fn do_render( draw_buffers: &mut DrawBuffers, tex_repo: &mut TextureRepo, ui: &mut UiState, -) { +) -> Result<()> { // TODO: Actual UI Rendering let (_out, paint) = ui.end_frame(); let screen = ui.dimensions(); @@ -32,9 +33,9 @@ pub fn do_render( // Copy triangles/indicies for i in (0..tris.indices.len()).step_by(3) { draw_buffers.index_buffer[i / 3] = ( - tris.indices[i].try_into().unwrap(), - tris.indices[i + 1].try_into().unwrap(), - tris.indices[i + 2].try_into().unwrap(), + tris.indices[i].try_into()?, + tris.indices[i + 1].try_into()?, + tris.indices[i + 2].try_into()?, ); } for (i, vertex) in tris.vertices.iter().enumerate() { @@ -61,4 +62,6 @@ pub fn do_render( // tex_repo.queue_load(0); } } + + Ok(()) } diff --git a/stockton-render/src/draw/ui/texture.rs b/stockton-render/src/draw/ui/texture.rs index 0ec4873..f5ddb3e 100755 --- a/stockton-render/src/draw/ui/texture.rs +++ b/stockton-render/src/draw/ui/texture.rs @@ -1,6 +1,6 @@ use crate::draw::texture::{LoadableImage, TextureRepo}; -use crate::types::*; use crate::UiState; +use anyhow::Result; use egui::Texture; use stockton_levels::{prelude::HasTextures, traits::textures::Texture as LTexture}; @@ -43,19 +43,13 @@ impl LoadableImage for &Texture { } } -pub fn ensure_textures( - _tex_repo: &mut TextureRepo, - ui: &mut UiState, - _device: &mut DeviceT, - _adapter: &mut Adapter, - _allocator: &mut DynamicAllocator, - _command_queue: &mut QueueT, - _command_pool: &mut CommandPoolT, -) { +pub fn ensure_textures(tex_repo: &mut TextureRepo, ui: &mut UiState) -> Result<()> { let tex = ui.ctx.texture(); if tex.version != ui.last_tex_ver { - // tex_repo.force_queue_load(0).unwrap(); // TODO + tex_repo.force_queue_load(0)?; ui.last_tex_ver = tex.version; } + + Ok(()) } diff --git a/stockton-render/src/error.rs b/stockton-render/src/error.rs index 7c9abd4..73726b2 100644 --- a/stockton-render/src/error.rs +++ b/stockton-render/src/error.rs @@ -1,41 +1,8 @@ //! Error types -use super::draw::target::TargetChainCreationError; +use anyhow; use thiserror::Error; -/// An error encountered creating a rendering context. -#[derive(Debug)] -pub enum CreationError { - TargetChainCreationError(TargetChainCreationError), - WindowError, - BadSurface, - - DeviceError(hal::device::CreationError), - - OutOfMemoryError, - - SyncObjectError, - - NoShaderC, - ShaderCError(shaderc::Error), - ShaderModuleFailed(hal::device::ShaderError), - RenderPassError, - PipelineError(hal::pso::CreationError), - BufferError(hal::buffer::CreationError), - BufferNoMemory, - - SwapchainError, - ImageViewError, - - BadDataError, -} - -/// An error encountered when rendering. -/// Usually this is out of memory or something happened to the device/surface. -/// You'll likely need to exit or create a new context. -#[derive(Debug, Clone)] -pub enum FrameError {} - #[derive(Error, Debug)] pub enum LockPoisoned { #[error("Device lock poisoned")] @@ -46,4 +13,46 @@ pub enum LockPoisoned { #[error("Queue lock poisoned")] Queue, + + #[error("Other lock poisoned")] + Other, +} + +/// Indicates the given property has no acceptable values +#[derive(Debug, Error)] +pub enum EnvironmentError { + #[error("No supported color format")] + ColorFormat, + + #[error("No supported depth format")] + DepthFormat, + + #[error("No supported present mode")] + PresentMode, + + #[error("No supported composite alpha mode")] + CompositeAlphaMode, + + #[error("No suitable queue families found")] + NoSuitableFamilies, + + #[error("No suitable memory types found")] + NoMemoryTypes, + + #[error("Couldn't use shaderc")] + NoShaderC, + + #[error("No suitable queues")] + NoQueues, +} + +pub fn full_error_display(err: anyhow::Error) -> String { + let cont = err + .chain() + .skip(1) + .map(|cause| format!(" caused by: {}", cause)) + .collect::>() + .join("\n"); + + format!("Error: {}\n{}", err, cont) } diff --git a/stockton-render/src/lib.rs b/stockton-render/src/lib.rs index a0d745c..2d3da06 100644 --- a/stockton-render/src/lib.rs +++ b/stockton-render/src/lib.rs @@ -10,13 +10,15 @@ extern crate legion; mod culling; pub mod draw; -mod error; +pub mod error; pub mod systems; mod types; pub mod window; use culling::get_visible_faces; use draw::RenderingContext; +use error::full_error_display; +use error::LockPoisoned; use legion::world::SubWorld; use legion::IntoQuery; use std::sync::mpsc::{Receiver, Sender}; @@ -24,6 +26,8 @@ use std::sync::Arc; use std::sync::RwLock; pub use window::{UiState, WindowEvent}; +use anyhow::{Context, Result}; +use log::error; use stockton_levels::prelude::*; use stockton_types::components::{CameraSettings, Transform}; use stockton_types::Vector3; @@ -47,36 +51,46 @@ pub struct Renderer<'a, M: 'static + MinBspFeatures> { impl<'a, M: 'static + MinBspFeatures> Renderer<'a, M> { /// Create a new Renderer. - pub fn new(window: &Window, file: M) -> (Self, Sender) { + pub fn new(window: &Window, file: M) -> Result<(Self, Sender)> { let (tx, rx) = channel(); let update_control_flow = Arc::new(RwLock::new(ControlFlow::Poll)); - ( + Ok(( Renderer { - context: RenderingContext::new(window, file).unwrap(), + context: RenderingContext::new(window, file)?, window_events: rx, update_control_flow, }, tx, - ) + )) } /// Render a single frame of the given map. - fn render(&mut self, ui: &mut UiState, pos: Vector3) { + fn render(&mut self, ui: &mut UiState, pos: Vector3) -> Result<()> { // Get visible faces - let faces = get_visible_faces(pos, &*self.context.map.read().unwrap()); + let faces = get_visible_faces( + pos, + &*self + .context + .map + .read() + .map_err(|_| LockPoisoned::Map) + .context("Error getting read lock on map")?, + ); // Then draw them if self.context.draw_vertices(ui, &faces).is_err() { - unsafe { self.context.handle_surface_change().unwrap() }; + unsafe { self.context.handle_surface_change()? }; // If it fails twice, then error - self.context.draw_vertices(ui, &faces).unwrap(); + self.context.draw_vertices(ui, &faces)?; } + + Ok(()) } - fn resize(&mut self) { - unsafe { self.context.handle_surface_change().unwrap() }; + fn resize(&mut self) -> Result<()> { + unsafe { self.context.handle_surface_change() } } } @@ -91,6 +105,8 @@ pub fn do_render>( ) { let mut query = <(&Transform, &CameraSettings)>::query(); for (transform, _) in query.iter(world) { - renderer.render(ui, transform.position); + if let Err(err) = renderer.render(ui, transform.position) { + error!("{}", full_error_display(err)); + } } } diff --git a/stockton-render/src/window.rs b/stockton-render/src/window.rs index e8e9957..ea172bc 100644 --- a/stockton-render/src/window.rs +++ b/stockton-render/src/window.rs @@ -1,4 +1,4 @@ -use crate::Renderer; +use crate::{error::full_error_display, Renderer}; use egui::Context; use legion::systems::Runnable; use log::debug; @@ -6,7 +6,7 @@ use std::sync::Arc; use stockton_levels::prelude::{MinBspFeatures, VulkanSystem}; use egui::{Output, PaintJobs, Pos2, RawInput, Ui}; - +use log::error; use stockton_input::{Action as KBAction, InputManager, Mouse}; use winit::event::{ @@ -162,7 +162,9 @@ pub fn _process_window_events< while let Ok(event) = renderer.window_events.try_recv() { match event { WindowEvent::SizeChanged(w, h) => { - renderer.resize(); + if let Err(err) = renderer.resize() { + error!("{}", full_error_display(err)); + }; ui_state.set_dimensions(w, h); } WindowEvent::CloseRequested => { -- cgit v1.2.3