diff options
author | tcmal <me@aria.rip> | 2024-08-25 17:44:22 +0100 |
---|---|---|
committer | tcmal <me@aria.rip> | 2024-08-25 17:44:22 +0100 |
commit | 10b3d4ac59e826b31d2114999e31893390acfb9c (patch) | |
tree | 0dd47e2dda5c346cadf15c8e21a53d3a39c00238 | |
parent | 5da588c6223e4c30cc5ec349f318aa5203cb0ce7 (diff) |
feat(render): WIP switch to anyhow for errors
-rw-r--r-- | stockton-render/Cargo.toml | 2 | ||||
-rw-r--r-- | stockton-render/src/draw/texture/load.rs | 65 | ||||
-rw-r--r-- | stockton-render/src/draw/texture/loader.rs | 80 | ||||
-rw-r--r-- | stockton-render/src/draw/texture/repo.rs | 35 | ||||
-rw-r--r-- | stockton-render/src/draw/texture/staging_buffer.rs | 12 | ||||
-rw-r--r-- | 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<B: Block<back::Backend>> { pub fence: Fence, @@ -57,12 +71,21 @@ impl<'a, T: HasTextures, R: TextureResolver<I>, I: LoadableImage> TextureLoader< pub(crate) unsafe fn attempt_queue_load( &mut self, block_ref: usize, - ) -> Option<QueuedLoad<DynamicBlock>> { - let mut device = self.device.write().unwrap(); + ) -> Result<QueuedLoad<DynamicBlock>> { + 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>, I: LoadableImage> TextureLoader< 1, &mut v, ) - .unwrap(); + .map_err::<HalErrorWrapper, _>(|e| e.into()) + .context("Error creating descriptor set")?; v.pop().unwrap() }; @@ -110,9 +134,12 @@ impl<'a, T: HasTextures, R: TextureResolver<I>, 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>, 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::<HalErrorWrapper, _>(|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>, 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>, I: LoadableImage> TextureLoader< Swizzle::NO, Self::RESOURCES, ) - .expect("Error creating image view"); + .map_err::<HalErrorWrapper, _>(|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>, I: LoadableImage> TextureLoader< // Create sampler let sampler = device .create_sampler(&SamplerDesc::new(Filter::Nearest, WrapMode::Tile)) - .expect("Failed to create sampler"); + .map_err::<HalErrorWrapper, _>(|e| e.into()) + .context("Error creating sampler")?; // Write to descriptor set { @@ -283,7 +313,7 @@ impl<'a, T: HasTextures, R: TextureResolver<I>, I: LoadableImage> TextureLoader< Some(&fence), ); - Some(QueuedLoad { + Ok(QueuedLoad { staging_bufs, fence, buf, @@ -313,7 +343,7 @@ fn create_image_view<T, I>( format: Format, usage: ImgUsage, img: &I, -) -> Result<(T::Block, Image), &'static str> +) -> Result<(T::Block, Image)> where T: Allocator<back::Backend>, I: LoadableImage, @@ -331,7 +361,8 @@ where ViewCapabilities::empty(), ) } - .map_err(|_| "Couldn't create image")?; + .map_err::<HalErrorWrapper, _>(|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::<HalErrorWrapper, _>(|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::<HalErrorWrapper, _>(|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<T, R, I> { pub(crate) _li: PhantomData<I>, } +#[derive(Error, Debug)] +pub enum TextureLoaderError { + #[error("Couldn't find a suitable memory type")] + NoMemoryTypes, +} + impl<T: HasTextures, R: TextureResolver<I>, I: LoadableImage> TextureLoader<T, R, I> { - pub fn loop_forever(mut self) -> Result<TextureLoaderRemains, &'static str> { + pub fn loop_forever(mut self) -> Result<TextureLoaderRemains> { 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<bool> { 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::<HalErrorWrapper, _>(|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<T: HasTextures, R: TextureResolver<I>, I: LoadableImage> TextureLoader<T, R match to_load { LoaderRequest::Load(to_load) => { // 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::<TextureLoadError>() { + 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<T: HasTextures, R: TextureResolver<I>, I: LoadableImage> TextureLoader<T, R return_channel: Sender<TexturesBlock<DynamicBlock>>, texs: Arc<RwLock<T>>, resolver: R, - ) -> Result<Self, &'static str> { - let device = device_lock.write().unwrap(); + ) -> Result<Self> { + let device = device_lock + .write() + .map_err(|_| LockPoisoned::Device) + .context("Error getting device lock")?; // Pool let mut pool = unsafe { @@ -169,7 +193,8 @@ impl<T: HasTextures, R: TextureResolver<I>, I: LoadableImage> TextureLoader<T, R device.create_command_pool(family, CommandPoolCreateFlags::RESET_INDIVIDUAL) } - .map_err(|_| "Couldn't create command pool")?; + .map_err::<HalErrorWrapper, _>(|e| e.into()) + .context("Error creating command pool")?; let type_mask = unsafe { use hal::image::{Kind, Tiling, Usage, ViewCapabilities}; @@ -191,7 +216,8 @@ impl<T: HasTextures, R: TextureResolver<I>, I: LoadableImage> TextureLoader<T, R Usage::SAMPLED, ViewCapabilities::empty(), ) - .map_err(|_| "Couldn't make image to get memory requirements")?; + .map_err::<HalErrorWrapper, _>(|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<T: HasTextures, R: TextureResolver<I>, I: LoadableImage> TextureLoader<T, R DynamicAllocator::new( find_memory_type_id(&adapter, type_mask, props) - .ok_or("Couldn't find memory type supporting image")?, + .ok_or(TextureLoaderError::NoMemoryTypes)?, props, DynamicConfig { block_size_granularity: 4 * 32 * 32, // 32x32 image @@ -219,7 +245,7 @@ impl<T: HasTextures, R: TextureResolver<I>, I: LoadableImage> TextureLoader<T, R let (staging_memory_type, staging_allocator) = { let props = MemProps::CPU_VISIBLE | MemProps::COHERENT; let t = find_memory_type_id(&adapter, type_mask, props) - .ok_or("Couldn't find memory type supporting image")?; + .ok_or(TextureLoaderError::NoMemoryTypes)?; ( t, DynamicAllocator::new( @@ -242,7 +268,8 @@ impl<T: HasTextures, R: TextureResolver<I>, I: LoadableImage> TextureLoader<T, R data.push_back(( device .create_fence(false) - .map_err(|_| "Couldn't create fence")?, + .map_err::<HalErrorWrapper, _>(|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<DescriptorAllocator>, } -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<JoinHandle<Result<TextureLoaderRemains, &'static str>>>, + joiner: ManuallyDrop<JoinHandle<Result<TextureLoaderRemains>>>, ds_layout: Arc<RwLock<DescriptorSetLayout>>, req_send: Sender<LoaderRequest>, resp_recv: Receiver<TexturesBlock<DynamicBlock>>, @@ -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<RwLock<T>>, resolver: R, - ) -> Result<Self, &'static str> { + ) -> Result<Self> { 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::<HalErrorWrapper, _>(|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<StagingBuffer, &'static str> { + ) -> Result<StagingBuffer> { let mut buffer = unsafe { device.create_buffer(size, Self::USAGE) } - .map_err(|_| "Couldn't create staging buffer")?; + .map_err::<HalErrorWrapper, _>(|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::<HalErrorWrapper, _>(|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::<HalErrorWrapper, _>(|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 = <back::Backend as hal::Backend>::Device; pub type Gpu = hal::adapter::Gpu<back::Backend>; pub type Buffer = <back::Backend as hal::Backend>::Buffer; @@ -30,3 +32,45 @@ pub type DynamicAllocator = rendy_memory::DynamicAllocator<back::Backend>; pub type DynamicBlock = rendy_memory::DynamicBlock<back::Backend>; pub type RDescriptorSet = rendy_descriptor::DescriptorSet<back::Backend>; + +#[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), +} |