From 10b3d4ac59e826b31d2114999e31893390acfb9c Mon Sep 17 00:00:00 2001 From: tcmal Date: Sun, 25 Aug 2024 17:44:22 +0100 Subject: feat(render): WIP switch to anyhow for errors --- stockton-render/Cargo.toml | 2 + stockton-render/src/draw/texture/load.rs | 65 +++++++++++++----- stockton-render/src/draw/texture/loader.rs | 80 ++++++++++++++-------- stockton-render/src/draw/texture/repo.rs | 35 +++++++--- stockton-render/src/draw/texture/staging_buffer.rs | 12 ++-- stockton-render/src/types.rs | 44 ++++++++++++ 6 files changed, 179 insertions(+), 59 deletions(-) diff --git a/stockton-render/Cargo.toml b/stockton-render/Cargo.toml index 5982a22..0348c60 100644 --- a/stockton-render/Cargo.toml +++ b/stockton-render/Cargo.toml @@ -19,6 +19,8 @@ legion = { version = "^0.3" } egui = "^0.2" rendy-memory = "0.5.2" rendy-descriptor = "0.5.1" +anyhow = "1.0.40" +thiserror = "1.0.25" [features] default = ["vulkan"] diff --git a/stockton-render/src/draw/texture/load.rs b/stockton-render/src/draw/texture/load.rs index b54b37d..7ca07cb 100644 --- a/stockton-render/src/draw/texture/load.rs +++ b/stockton-render/src/draw/texture/load.rs @@ -5,6 +5,7 @@ use super::{ use crate::types::*; use stockton_levels::prelude::*; +use anyhow::{Context, Result}; use arrayvec::ArrayVec; use hal::{ command::{BufferImageCopy, CommandBufferFlags}, @@ -21,6 +22,19 @@ use hal::{ use rendy_descriptor::{DescriptorRanges, DescriptorSetLayoutBinding, DescriptorType}; use rendy_memory::{Allocator, Block}; use std::mem::ManuallyDrop; +use thiserror::Error; + +#[derive(Error, Debug)] +pub enum TextureLoadError { + #[error("No available resources")] + NoResources, + + #[error("Texture is not in map")] + NotInMap(usize), + + #[error("Texture could not be resolved")] + ResolveFailed(usize), +} pub struct QueuedLoad> { pub fence: Fence, @@ -57,12 +71,21 @@ impl<'a, T: HasTextures, R: TextureResolver, I: LoadableImage> TextureLoader< pub(crate) unsafe fn attempt_queue_load( &mut self, block_ref: usize, - ) -> Option> { - let mut device = self.device.write().unwrap(); + ) -> Result> { + let mut device = self + .device + .write() + .map_err(|_| LockPoisoned::Device) + .context("Error getting device lock")?; + let textures = self.textures.read().unwrap(); // Get assets to use - let (fence, mut buf) = self.buffers.pop_front()?; + let (fence, mut buf) = self + .buffers + .pop_front() + .ok_or(TextureLoadError::NoResources) + .context("Error getting resources to use")?; // Create descriptor set let descriptor_set = { @@ -90,7 +113,8 @@ impl<'a, T: HasTextures, R: TextureResolver, I: LoadableImage> TextureLoader< 1, &mut v, ) - .unwrap(); + .map_err::(|e| e.into()) + .context("Error creating descriptor set")?; v.pop().unwrap() }; @@ -110,9 +134,12 @@ impl<'a, T: HasTextures, R: TextureResolver, I: LoadableImage> TextureLoader< break; // Past the end // TODO: We should actually write blank descriptors } - let tex = tex.unwrap(); + let tex = tex.ok_or(TextureLoadError::NotInMap(tex_idx))?; - let img_data = self.resolver.resolve(tex).expect("Invalid texture"); + let img_data = self + .resolver + .resolve(tex) + .ok_or(TextureLoadError::ResolveFailed(tex_idx))?; // Calculate buffer size let (row_size, total_size) = @@ -125,12 +152,13 @@ impl<'a, T: HasTextures, R: TextureResolver, I: LoadableImage> TextureLoader< total_size as u64, self.staging_memory_type, ) - .expect("Couldn't create staging buffer"); + .context("Error creating staging buffer")?; // Write to staging buffer let mapped_memory = staging_buffer .map_memory(&mut device) - .expect("Error mapping staged memory"); + .map_err::(|e| e.into()) + .context("Error mapping staged memory")?; img_data.copy_into(mapped_memory, row_size); @@ -144,7 +172,7 @@ impl<'a, T: HasTextures, R: TextureResolver, I: LoadableImage> TextureLoader< ImgUsage::SAMPLED, &img_data, ) - .unwrap(); + .context("Error creating image")?; // Create image view let img_view = device @@ -155,7 +183,8 @@ impl<'a, T: HasTextures, R: TextureResolver, I: LoadableImage> TextureLoader< Swizzle::NO, Self::RESOURCES, ) - .expect("Error creating image view"); + .map_err::(|e| e.into()) + .context("Error creating image view")?; // Queue copy from buffer to image copy_cmds.push(BufferImageCopy { @@ -174,7 +203,8 @@ impl<'a, T: HasTextures, R: TextureResolver, I: LoadableImage> TextureLoader< // Create sampler let sampler = device .create_sampler(&SamplerDesc::new(Filter::Nearest, WrapMode::Tile)) - .expect("Failed to create sampler"); + .map_err::(|e| e.into()) + .context("Error creating sampler")?; // Write to descriptor set { @@ -283,7 +313,7 @@ impl<'a, T: HasTextures, R: TextureResolver, I: LoadableImage> TextureLoader< Some(&fence), ); - Some(QueuedLoad { + Ok(QueuedLoad { staging_bufs, fence, buf, @@ -313,7 +343,7 @@ fn create_image_view( format: Format, usage: ImgUsage, img: &I, -) -> Result<(T::Block, Image), &'static str> +) -> Result<(T::Block, Image)> where T: Allocator, I: LoadableImage, @@ -331,7 +361,8 @@ where ViewCapabilities::empty(), ) } - .map_err(|_| "Couldn't create image")?; + .map_err::(|e| e.into()) + .context("Error creating image")?; // Allocate memory let (block, _) = unsafe { @@ -339,12 +370,14 @@ where allocator.alloc(device, requirements.size, requirements.alignment) } - .map_err(|_| "Out of memory")?; + .map_err::(|e| e.into()) + .context("Error allocating memory")?; unsafe { device .bind_image_memory(&block.memory(), block.range().start, &mut image_ref) - .map_err(|_| "Couldn't bind memory to image")?; + .map_err::(|e| e.into()) + .context("Error binding memory to image")?; } Ok((block, image_ref)) diff --git a/stockton-render/src/draw/texture/loader.rs b/stockton-render/src/draw/texture/loader.rs index 34ce87d..f505de5 100644 --- a/stockton-render/src/draw/texture/loader.rs +++ b/stockton-render/src/draw/texture/loader.rs @@ -1,6 +1,11 @@ //! Manages the loading/unloading of textures -use super::{block::TexturesBlock, load::QueuedLoad, resolver::TextureResolver, LoadableImage}; +use super::{ + block::TexturesBlock, + load::{QueuedLoad, TextureLoadError}, + resolver::TextureResolver, + LoadableImage, +}; use crate::{draw::utils::find_memory_type_id, types::*}; use std::{ @@ -15,6 +20,7 @@ use std::{ time::Duration, }; +use anyhow::{Context, Result}; use arrayvec::ArrayVec; use hal::{ format::Format, memory::Properties as MemProps, prelude::*, queue::family::QueueFamilyId, @@ -23,6 +29,7 @@ use hal::{ use log::*; use rendy_memory::DynamicConfig; use stockton_levels::prelude::HasTextures; +use thiserror::Error; /// The number of command buffers to have in flight simultaneously. pub const NUM_SIMULTANEOUS_CMDS: usize = 2; @@ -84,35 +91,44 @@ pub struct TextureLoader { pub(crate) _li: PhantomData, } +#[derive(Error, Debug)] +pub enum TextureLoaderError { + #[error("Couldn't find a suitable memory type")] + NoMemoryTypes, +} + impl, I: LoadableImage> TextureLoader { - pub fn loop_forever(mut self) -> Result { + pub fn loop_forever(mut self) -> Result { debug!("TextureLoader starting main loop"); - let mut res = Ok(()); + let mut res = Ok(false); while res.is_ok() { res = self.main(); + if let Ok(true) = res { + break; + } + sleep(Duration::from_secs(0)); } match res { - Err(r) => match r { - LoopEndReason::Graceful => { - debug!("Starting to deactivate TextureLoader"); + Ok(true) => { + debug!("Starting to deactivate TextureLoader"); - Ok(self.deactivate()) - } - LoopEndReason::Error(r) => Err(r), - }, - Ok(_) => Err(""), + Ok(self.deactivate()) + } + Err(r) => Err(r.context("Error in TextureLoader loop")), + _ => unreachable!(), } } - fn main(&mut self) -> Result<(), LoopEndReason> { + fn main(&mut self) -> Result { let mut device = self.device.write().unwrap(); // Check for blocks that are finished, then send them back let mut i = 0; while i < self.commands_queued.len() { let signalled = unsafe { device.get_fence_status(&self.commands_queued[i].fence) } - .map_err(|_| LoopEndReason::Error("Device lost by TextureManager"))?; + .map_err::(|e| e.into()) + .context("Error checking fence status")?; if signalled { let (assets, mut staging_bufs, block) = self.commands_queued.remove(i).dissolve(); @@ -139,15 +155,20 @@ impl, I: LoadableImage> TextureLoader { // Attempt to load given block - if let Some(queued_load) = unsafe { self.attempt_queue_load(to_load) } { - self.commands_queued.push(queued_load); + let result = unsafe { self.attempt_queue_load(to_load) }; + match result { + Ok(queued_load) => self.commands_queued.push(queued_load), + Err(x) => match x.downcast_ref::() { + Some(TextureLoadError::NoResources) => {} + _ => return Err(x).context("Error queuing texture load"), + }, } } - LoaderRequest::End => return Err(LoopEndReason::Graceful), + LoaderRequest::End => return Ok(true), } } - Ok(()) + Ok(false) } pub fn new( @@ -160,8 +181,11 @@ impl, I: LoadableImage> TextureLoader>, texs: Arc>, resolver: R, - ) -> Result { - let device = device_lock.write().unwrap(); + ) -> Result { + let device = device_lock + .write() + .map_err(|_| LockPoisoned::Device) + .context("Error getting device lock")?; // Pool let mut pool = unsafe { @@ -169,7 +193,8 @@ impl, I: LoadableImage> TextureLoader(|e| e.into()) + .context("Error creating command pool")?; let type_mask = unsafe { use hal::image::{Kind, Tiling, Usage, ViewCapabilities}; @@ -191,7 +216,8 @@ impl, I: LoadableImage> TextureLoader(|e| e.into()) + .context("Error creating test image to get buffer settings")?; let type_mask = device.get_image_requirements(&img).type_mask; @@ -206,7 +232,7 @@ impl, I: LoadableImage> TextureLoader, I: LoadableImage> TextureLoader, I: LoadableImage> TextureLoader(|e| e.into()) + .context("Error creating fence")?, pool.allocate_one(hal::command::Level::Primary), )); }; @@ -353,11 +380,6 @@ pub struct TextureLoaderRemains { pub descriptor_allocator: ManuallyDrop, } -enum LoopEndReason { - Graceful, - Error(&'static str), -} - pub enum LoaderRequest { /// Load the given block Load(BlockRef), diff --git a/stockton-render/src/draw/texture/repo.rs b/stockton-render/src/draw/texture/repo.rs index bb68479..2316dc4 100644 --- a/stockton-render/src/draw/texture/repo.rs +++ b/stockton-render/src/draw/texture/repo.rs @@ -20,12 +20,14 @@ use std::{ thread::JoinHandle, }; +use anyhow::{Context, Result}; use hal::{ prelude::*, pso::{DescriptorSetLayoutBinding, DescriptorType, ShaderStageFlags}, Features, }; use log::debug; +use thiserror::Error; /// The number of textures in one 'block' /// The textures of the loaded file are divided into blocks of this size. @@ -33,7 +35,7 @@ use log::debug; pub const BLOCK_SIZE: usize = 8; pub struct TextureRepo<'a> { - joiner: ManuallyDrop>>, + joiner: ManuallyDrop>>, ds_layout: Arc>, req_send: Sender, resp_recv: Receiver>, @@ -42,6 +44,15 @@ pub struct TextureRepo<'a> { _a: PhantomData<&'a ()>, } +#[derive(Error, Debug)] +pub enum TextureRepoError { + #[error("No suitable queue family")] + NoQueueFamilies, + + #[error("Lock poisoned")] + LockPoisoned, +} + impl<'a> TextureRepo<'a> { pub fn new< T: 'static + HasTextures + Send + Sync, @@ -52,7 +63,7 @@ impl<'a> TextureRepo<'a> { adapter: &Adapter, texs_lock: Arc>, resolver: R, - ) -> Result { + ) -> Result { let (req_send, req_recv) = channel(); let (resp_send, resp_recv) = channel(); let family = adapter @@ -62,15 +73,18 @@ impl<'a> TextureRepo<'a> { family.queue_type().supports_transfer() && family.max_queues() >= NUM_SIMULTANEOUS_CMDS }) - .unwrap(); + .ok_or(TextureRepoError::NoQueueFamilies)?; + let gpu = unsafe { adapter .physical_device - .open(&[(family, &[1.0])], Features::empty()) - .unwrap() + .open(&[(family, &[1.0])], Features::empty())? }; - let device = device_lock.write().unwrap(); + let device = device_lock + .write() + .map_err(|_| TextureRepoError::LockPoisoned) + .context("Error getting device lock")?; let ds_lock = Arc::new(RwLock::new( unsafe { @@ -94,7 +108,8 @@ impl<'a> TextureRepo<'a> { &[], ) } - .map_err(|_| "Couldn't create descriptor set layout")?, + .map_err::(|e| e.into()) + .context("Error creating descriptor set layout")?, )); drop(device); @@ -129,7 +144,7 @@ impl<'a> TextureRepo<'a> { self.ds_layout.read().unwrap() } - pub fn queue_load(&mut self, block_id: BlockRef) -> Result<(), &'static str> { + pub fn queue_load(&mut self, block_id: BlockRef) -> Result<()> { if self.blocks.contains_key(&block_id) { return Ok(()); } @@ -137,10 +152,10 @@ impl<'a> TextureRepo<'a> { self.force_queue_load(block_id) } - pub fn force_queue_load(&mut self, block_id: BlockRef) -> Result<(), &'static str> { + pub fn force_queue_load(&mut self, block_id: BlockRef) -> Result<()> { self.req_send .send(LoaderRequest::Load(block_id)) - .map_err(|_| "Couldn't send load request")?; + .context("Error queuing texture block load")?; self.blocks.insert(block_id, None); diff --git a/stockton-render/src/draw/texture/staging_buffer.rs b/stockton-render/src/draw/texture/staging_buffer.rs index d1897ad..4adc974 100644 --- a/stockton-render/src/draw/texture/staging_buffer.rs +++ b/stockton-render/src/draw/texture/staging_buffer.rs @@ -2,6 +2,7 @@ use crate::types::*; use std::mem::ManuallyDrop; +use anyhow::{Context, Result}; use hal::{device::MapError, prelude::*, MemoryTypeId}; use rendy_memory::{Allocator, Block}; @@ -18,18 +19,21 @@ impl StagingBuffer { alloc: &mut DynamicAllocator, size: u64, _memory_type_id: MemoryTypeId, - ) -> Result { + ) -> Result { let mut buffer = unsafe { device.create_buffer(size, Self::USAGE) } - .map_err(|_| "Couldn't create staging buffer")?; + .map_err::(|e| e.into()) + .context("Error creating buffer")?; let requirements = unsafe { device.get_buffer_requirements(&buffer) }; let (memory, _) = alloc .alloc(device, requirements.size, requirements.alignment) - .map_err(|_| "Couldn't allocate staging memory")?; + .map_err::(|e| e.into()) + .context("Error allocating staging memory")?; unsafe { device.bind_buffer_memory(memory.memory(), 0, &mut buffer) } - .map_err(|_| "Couldn't bind staging memory to buffer")?; + .map_err::(|e| e.into()) + .context("Error binding staging memory to buffer")?; Ok(StagingBuffer { buf: ManuallyDrop::new(buffer), diff --git a/stockton-render/src/types.rs b/stockton-render/src/types.rs index d37d5b6..4a79602 100644 --- a/stockton-render/src/types.rs +++ b/stockton-render/src/types.rs @@ -1,5 +1,7 @@ //! Convenience module to reference types that are stored in the backend's enum +use thiserror::Error; + pub type Device = ::Device; pub type Gpu = hal::adapter::Gpu; pub type Buffer = ::Buffer; @@ -30,3 +32,45 @@ pub type DynamicAllocator = rendy_memory::DynamicAllocator; pub type DynamicBlock = rendy_memory::DynamicBlock; pub type RDescriptorSet = rendy_descriptor::DescriptorSet; + +#[derive(Error, Debug)] +pub enum LockPoisoned { + #[error("Device lock poisoned")] + Device, + + #[error("Map lock poisoned")] + Map, + + #[error("Other lock poisoned")] + Other, +} + +#[derive(Error, Debug)] +pub enum HalErrorWrapper { + #[error("Device Creation Error: {0}")] + DeviceCreationError(#[from] hal::device::CreationError), + + #[error("Buffer Creation Error: {0}")] + BufferCreationError(#[from] hal::buffer::CreationError), + + #[error("Image Creation Error: {0}")] + ImageCreationError(#[from] hal::image::CreationError), + + #[error("View Error: {0}")] + ImageViewError(#[from] hal::image::ViewError), + + #[error("Out of memory on {0}")] + OutOfMemory(#[from] hal::device::OutOfMemory), + + #[error("Device Lost: {0}")] + DeviceLost(#[from] hal::device::DeviceLost), + + #[error("Allocation Error: {0}")] + AllocationError(#[from] hal::device::AllocationError), + + #[error("Bind Error: {0}")] + BindError(#[from] hal::device::BindError), + + #[error("Map Error: {0}")] + MapError(#[from] hal::device::MapError), +} -- cgit v1.2.3