aboutsummaryrefslogtreecommitdiff
path: root/stockton-render
diff options
context:
space:
mode:
authortcmal <me@aria.rip>2024-08-25 17:44:22 +0100
committertcmal <me@aria.rip>2024-08-25 17:44:22 +0100
commit10b3d4ac59e826b31d2114999e31893390acfb9c (patch)
tree0dd47e2dda5c346cadf15c8e21a53d3a39c00238 /stockton-render
parent5da588c6223e4c30cc5ec349f318aa5203cb0ce7 (diff)
feat(render): WIP switch to anyhow for errors
Diffstat (limited to 'stockton-render')
-rw-r--r--stockton-render/Cargo.toml2
-rw-r--r--stockton-render/src/draw/texture/load.rs65
-rw-r--r--stockton-render/src/draw/texture/loader.rs80
-rw-r--r--stockton-render/src/draw/texture/repo.rs35
-rw-r--r--stockton-render/src/draw/texture/staging_buffer.rs12
-rw-r--r--stockton-render/src/types.rs44
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),
+}