aboutsummaryrefslogtreecommitdiff
path: root/stockton-render/src
diff options
context:
space:
mode:
authortcmal <me@aria.rip>2024-08-25 17:44:23 +0100
committertcmal <me@aria.rip>2024-08-25 17:44:23 +0100
commitc52a05e6d3977efce6bd4479aa312dc90e0452e5 (patch)
treec383c910c1467b406e332651906e081c599d6951 /stockton-render/src
parentd5b9ccd3d4a6e5b35a0411688bfb7113f5c7d631 (diff)
feat(render): proper error handling
Diffstat (limited to 'stockton-render/src')
-rw-r--r--stockton-render/src/draw/buffer.rs57
-rw-r--r--stockton-render/src/draw/context.rs247
-rw-r--r--stockton-render/src/draw/depth_buffer.rs46
-rw-r--r--stockton-render/src/draw/draw_buffers.rs14
-rw-r--r--stockton-render/src/draw/pipeline.rs22
-rw-r--r--stockton-render/src/draw/queue_negotiator.rs11
-rw-r--r--stockton-render/src/draw/render.rs27
-rw-r--r--stockton-render/src/draw/target.rs139
-rw-r--r--stockton-render/src/draw/texture/loader.rs16
-rw-r--r--stockton-render/src/draw/texture/repo.rs7
-rw-r--r--stockton-render/src/draw/ui/pipeline.rs22
-rw-r--r--stockton-render/src/draw/ui/render.rs11
-rwxr-xr-xstockton-render/src/draw/ui/texture.rs16
-rw-r--r--stockton-render/src/error.rs77
-rw-r--r--stockton-render/src/lib.rs40
-rw-r--r--stockton-render/src/window.rs8
16 files changed, 371 insertions, 389 deletions
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<usize> {
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<Self, CreationError> {
+ pub fn new(device: &mut DeviceT, adapter: &Adapter, usage: Usage, size: u64) -> Result<Self> {
// Convert size to bytes
let size_bytes = size * size_of::<T>() 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::Once<_>, 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(&copy_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<VulkanSystem>> {
/// Buffers used for drawing the UI
ui_draw_buffers: ManuallyDrop<DrawBuffers<'a, UiPoint>>,
- /// Memory allocator used for any sort of textures / maps
- /// Guaranteed suitable for 2D RGBA images with `Optimal` tiling and `Usage::Sampled`
- texture_allocator: ManuallyDrop<DynamicAllocator>,
-
/// View projection matrix
pub(crate) vp_matrix: Mat4,
@@ -88,15 +87,15 @@ pub struct RenderingContext<'a, M: 'static + MinBspFeatures<VulkanSystem>> {
impl<'a, M: 'static + MinBspFeatures<VulkanSystem>> RenderingContext<'a, M> {
/// Create a new RenderingContext for the given window.
- pub fn new(window: &Window, map: M) -> Result<Self, error::CreationError> {
+ pub fn new(window: &Window, map: M) -> Result<Self> {
let map = Arc::new(RwLock::new(map));
// Create surface
let (instance, surface, mut adapters) = unsafe {
- let instance = back::Instance::create("stockton", 1)
- .map_err(|_| error::CreationError::WindowError)?;
+ let instance =
+ back::Instance::create("stockton", 1).context("Error creating vulkan instance")?;
let surface = instance
.create_surface(window)
- .map_err(|_| error::CreationError::WindowError)?;
+ .context("Error creating surface")?;
let adapters = instance.enumerate_adapters();
(instance, surface, adapters)
@@ -108,21 +107,22 @@ impl<'a, M: 'static + MinBspFeatures<VulkanSystem>> RenderingContext<'a, M> {
let mut draw_queue_negotiator = QueueNegotiator::find(&adapter, |family| {
surface.supports_queue_family(family) && family.queue_type().supports_graphics()
})
- .unwrap();
+ .context("Error creating draw queue negotiator")?;
let mut tex_queue_negotiator =
- QueueNegotiator::find(&adapter, TextureRepo::queue_family_filter).unwrap();
- // Device & Queue group
- let (device_lock, mut queue_groups) = {
- debug!(
- "Using draw queue family {:?}",
- draw_queue_negotiator.family_id()
- );
- debug!(
- "Using tex queue family {:?}",
- tex_queue_negotiator.family_id()
- );
+ QueueNegotiator::find(&adapter, TextureRepo::queue_family_filter)
+ .context("Error creating texture queue negotiator")?;
+ debug!(
+ "Using draw queue family {:?}",
+ draw_queue_negotiator.family_id()
+ );
+ debug!(
+ "Using tex queue family {:?}",
+ tex_queue_negotiator.family_id()
+ );
+ // Device & Queue groups
+ let (device_lock, mut queue_groups) = {
let gpu = unsafe {
adapter
.physical_device
@@ -133,24 +133,22 @@ impl<'a, M: 'static + MinBspFeatures<VulkanSystem>> RenderingContext<'a, M> {
],
hal::Features::empty(),
)
- .unwrap()
+ .context("Error opening logical device")?
};
(Arc::new(RwLock::new(gpu.device)), gpu.queue_groups)
};
- let mut device = device_lock.write().unwrap();
-
- let device_props = adapter.physical_device.properties();
+ let mut device = device_lock
+ .write()
+ .map_err(|_| LockPoisoned::Device)
+ .context("Error getting device lock")?;
// Figure out what our swapchain will look like
let swapchain_properties = SwapchainProperties::find_best(&adapter, &surface)
- .map_err(|_| error::CreationError::BadSurface)?;
+ .context("Error getting properties for swapchain")?;
- debug!(
- "Detected following swapchain properties: {:?}",
- swapchain_properties
- );
+ debug!("Detected swapchain properties: {:?}", swapchain_properties);
// Command pool
let mut cmd_pool = unsafe {
@@ -159,62 +157,17 @@ impl<'a, M: 'static + MinBspFeatures<VulkanSystem>> RenderingContext<'a, M> {
CommandPoolCreateFlags::RESET_INDIVIDUAL,
)
}
- .map_err(|_| error::CreationError::OutOfMemoryError)?;
+ .context("Error creating draw command pool")?;
// Vertex and index buffers
- let draw_buffers = DrawBuffers::new(&mut device, &adapter)?;
+ let draw_buffers =
+ DrawBuffers::new(&mut device, &adapter).context("Error creating 3D draw buffers")?;
// UI Vertex and index buffers
- let ui_draw_buffers = DrawBuffers::new(&mut device, &adapter)?;
-
- // Memory allocators
- let texture_allocator = unsafe {
- use hal::{
- format::Format,
- image::{Kind, Tiling, Usage, ViewCapabilities},
- memory::Properties,
- };
-
- // We create an empty image with the same format as used for textures
- // this is to get the type_mask required, which will stay the same for
- // all colour images of the same tiling. (certain memory flags excluded).
-
- // Size and alignment don't necessarily stay the same, so we're forced to
- // guess at the alignment for our allocator.
-
- // TODO: Way to tune these options
-
- let img = device
- .create_image(
- Kind::D2(16, 16, 1, 1),
- 1,
- Format::Rgba8Srgb,
- Tiling::Optimal,
- Usage::SAMPLED,
- SparseFlags::empty(),
- ViewCapabilities::empty(),
- )
- .map_err(|_| error::CreationError::OutOfMemoryError)?;
-
- let type_mask = device.get_image_requirements(&img).type_mask;
-
- device.destroy_image(img);
-
- let props = Properties::DEVICE_LOCAL;
-
- DynamicAllocator::new(
- find_memory_type_id(&adapter, type_mask, props)
- .ok_or(error::CreationError::OutOfMemoryError)?,
- props,
- DynamicConfig {
- block_size_granularity: 4 * 32 * 32, // 32x32 image
- max_chunk_size: u64::pow(2, 63),
- min_device_allocation: 4 * 32 * 32,
- },
- device_props.limits.non_coherent_atom_size as u64,
- )
- };
+ let ui_draw_buffers =
+ DrawBuffers::new(&mut device, &adapter).context("Error creating UI draw buffers")?;
+ // We have to unlock device for creating texture repos
drop(device);
// Texture repos
@@ -222,28 +175,34 @@ impl<'a, M: 'static + MinBspFeatures<VulkanSystem>> RenderingContext<'a, M> {
let tex_repo = TextureRepo::new(
device_lock.clone(),
tex_queue_negotiator.family_id(),
- tex_queue_negotiator.get_queue(&mut queue_groups).unwrap(),
+ tex_queue_negotiator
+ .get_queue(&mut queue_groups)
+ .ok_or(EnvironmentError::NoQueues)
+ .context("Error getting 3D texture loader queue")?,
&adapter,
map.clone(),
BasicFsResolver::new(std::path::Path::new(".")),
)
- .unwrap(); // TODO
+ .context("Error creating 3D Texture repo")?; // TODO
debug!("Creating UI Texture Repo");
let ui_tex_repo = TextureRepo::new(
device_lock.clone(),
tex_queue_negotiator.family_id(),
- tex_queue_negotiator.get_queue(&mut queue_groups).unwrap(),
+ tex_queue_negotiator
+ .get_queue(&mut queue_groups)
+ .ok_or(EnvironmentError::NoQueues)
+ .context("Error getting UI texture loader queue")?,
&adapter,
Arc::new(RwLock::new(UiTextures)),
BasicFsResolver::new(std::path::Path::new(".")),
)
- .unwrap(); // TODO
+ .context("Error creating UI texture repo")?; // TODO
- let mut device = device_lock.write().unwrap();
+ let mut device = device_lock.write().map_err(|_| LockPoisoned::Device)?;
- let ds_layout_lock = tex_repo.get_ds_layout();
- let ui_ds_layout_lock = ui_tex_repo.get_ds_layout();
+ let ds_layout_lock = tex_repo.get_ds_layout()?;
+ let ui_ds_layout_lock = ui_tex_repo.get_ds_layout()?;
// Graphics pipeline
let pipeline = CompletePipeline::new(
@@ -271,7 +230,7 @@ impl<'a, M: 'static + MinBspFeatures<VulkanSystem>> RenderingContext<'a, M> {
&mut cmd_pool,
swapchain_properties,
)
- .map_err(error::CreationError::TargetChainCreationError)?;
+ .context("Error creating target chain")?;
drop(device);
drop(ds_layout_lock);
@@ -284,7 +243,10 @@ impl<'a, M: 'static + MinBspFeatures<VulkanSystem>> RenderingContext<'a, M> {
device: device_lock,
adapter,
- queue: draw_queue_negotiator.get_queue(&mut queue_groups).unwrap(),
+ queue: draw_queue_negotiator
+ .get_queue(&mut queue_groups)
+ .ok_or(EnvironmentError::NoQueues)
+ .context("Error getting draw queue")?,
target_chain: ManuallyDrop::new(target_chain),
cmd_pool: ManuallyDrop::new(cmd_pool),
@@ -298,8 +260,6 @@ impl<'a, M: 'static + MinBspFeatures<VulkanSystem>> RenderingContext<'a, M> {
draw_buffers: ManuallyDrop::new(draw_buffers),
ui_draw_buffers: ManuallyDrop::new(ui_draw_buffers),
- texture_allocator: ManuallyDrop::new(texture_allocator),
-
vp_matrix: Mat4::identity(),
pixels_per_point: window.scale_factor() as f32,
@@ -309,23 +269,29 @@ impl<'a, M: 'static + MinBspFeatures<VulkanSystem>> RenderingContext<'a, M> {
/// If this function fails the whole context is probably dead
/// # Safety
/// The context must not be used while this is being called
- pub unsafe fn handle_surface_change(&mut self) -> Result<(), error::CreationError> {
- let mut device = self.device.write().unwrap();
+ pub unsafe fn handle_surface_change(&mut self) -> Result<()> {
+ let mut device = self
+ .device
+ .write()
+ .map_err(|_| LockPoisoned::Device)
+ .context("Error getting device lock")?;
- device.wait_idle().unwrap();
+ device
+ .wait_idle()
+ .context("Error waiting for device to become idle")?;
let surface = ManuallyDrop::into_inner(read(&self.target_chain))
.deactivate_with_recyling(&mut device, &mut self.cmd_pool);
let properties = SwapchainProperties::find_best(&self.adapter, &surface)
- .map_err(|_| error::CreationError::BadSurface)?;
+ .context("Error finding best swapchain properties")?;
use core::ptr::read;
// Graphics pipeline
// TODO: Recycle
- let ds_layout_handle = self.tex_repo.get_ds_layout();
- let ui_ds_layout_handle = self.tex_repo.get_ds_layout();
+ let ds_layout_handle = self.tex_repo.get_ds_layout()?;
+ let ui_ds_layout_handle = self.tex_repo.get_ds_layout()?;
ManuallyDrop::into_inner(read(&self.pipeline)).deactivate(&mut device);
self.pipeline = ManuallyDrop::new({
@@ -334,7 +300,8 @@ impl<'a, M: 'static + MinBspFeatures<VulkanSystem>> RenderingContext<'a, M> {
properties.extent,
&properties,
once(&*ds_layout_handle),
- )?
+ )
+ .context("Error creating 3D Pipeline")?
});
// 2D Graphics pipeline
@@ -349,7 +316,8 @@ impl<'a, M: 'static + MinBspFeatures<VulkanSystem>> RenderingContext<'a, M> {
properties.extent,
&properties,
once(&*ui_ds_layout_handle),
- )?
+ )
+ .context("Error creating UI Pipeline")?
});
self.target_chain = ManuallyDrop::new(
@@ -362,78 +330,89 @@ impl<'a, M: 'static + MinBspFeatures<VulkanSystem>> RenderingContext<'a, M> {
&mut self.cmd_pool,
properties,
)
- .map_err(error::CreationError::TargetChainCreationError)?,
+ .context("Error creating target chain")?,
);
Ok(())
}
/// Draw all vertices in the buffer
- pub fn draw_vertices(&mut self, ui: &mut UiState, faces: &[u32]) -> Result<(), &'static str> {
- let mut device = self.device.write().unwrap();
- let mut queue = self.queue.write().unwrap();
+ pub fn draw_vertices(&mut self, ui: &mut UiState, faces: &[u32]) -> Result<()> {
+ let mut device = self
+ .device
+ .write()
+ .map_err(|_| LockPoisoned::Device)
+ .context("Error getting device lock")?;
+ let mut queue = self
+ .queue
+ .write()
+ .map_err(|_| LockPoisoned::Map)
+ .context("Error getting map lock")?;
// Ensure UI texture(s) are loaded
- ensure_textures_ui(
- &mut self.ui_tex_repo,
- ui,
- &mut device,
- &mut self.adapter,
- &mut self.texture_allocator,
- &mut queue,
- &mut self.cmd_pool,
- );
+ ensure_textures_ui(&mut self.ui_tex_repo, ui)?;
// Get any textures that just finished loading
self.tex_repo.process_responses();
// 3D Pass
- let (cmd_buffer, img) = self.target_chain.prep_next_target(
- &mut device,
- &mut self.draw_buffers,
- &self.pipeline,
- &self.vp_matrix,
- )?;
+ let (cmd_buffer, img) = self
+ .target_chain
+ .prep_next_target(
+ &mut device,
+ &mut self.draw_buffers,
+ &self.pipeline,
+ &self.vp_matrix,
+ )
+ .context("Error preparing next target")?;
+
do_render(
cmd_buffer,
&mut self.draw_buffers,
&mut self.tex_repo,
&self.pipeline.pipeline_layout,
- &*self.map.read().unwrap(),
+ &*self
+ .map
+ .read()
+ .map_err(|_| LockPoisoned::Map)
+ .context("Error getting map read lock")?,
faces,
- );
+ )?;
// 2D Pass
- let cmd_buffer =
- self.target_chain
- .target_2d_pass(&mut self.ui_draw_buffers, &img, &self.ui_pipeline)?;
+ let cmd_buffer = self
+ .target_chain
+ .target_2d_pass(&mut self.ui_draw_buffers, &img, &self.ui_pipeline)
+ .context("Error switching to 2D pass")?;
+
do_render_ui(
cmd_buffer,
&self.ui_pipeline.pipeline_layout,
&mut self.ui_draw_buffers,
&mut self.ui_tex_repo,
ui,
- );
+ )?;
// Update our buffers before we actually start drawing
self.draw_buffers
.vertex_buffer
- .commit(&device, &mut queue, &mut self.cmd_pool);
+ .commit(&device, &mut queue, &mut self.cmd_pool)?;
self.draw_buffers
.index_buffer
- .commit(&device, &mut queue, &mut self.cmd_pool);
+ .commit(&device, &mut queue, &mut self.cmd_pool)?;
self.ui_draw_buffers
.vertex_buffer
- .commit(&device, &mut queue, &mut self.cmd_pool);
+ .commit(&device, &mut queue, &mut self.cmd_pool)?;
self.ui_draw_buffers
.index_buffer
- .commit(&device, &mut queue, &mut self.cmd_pool);
+ .commit(&device, &mut queue, &mut self.cmd_pool)?;
// Send commands off to GPU
self.target_chain
- .finish_and_submit_target(img, &mut queue)?;
+ .finish_and_submit_target(img, &mut queue)
+ .context("Error finishing and submitting target")?;
Ok(())
}
@@ -456,8 +435,6 @@ impl<'a, M: MinBspFeatures<VulkanSystem>> core::ops::Drop for RenderingContext<'
ManuallyDrop::into_inner(read(&self.draw_buffers)).deactivate(&mut device);
ManuallyDrop::into_inner(read(&self.ui_draw_buffers)).deactivate(&mut device);
- ManuallyDrop::into_inner(read(&self.texture_allocator)).dispose();
-
ManuallyDrop::into_inner(read(&self.target_chain)).deactivate(
&mut self.instance,
&mut device,
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<MemoryT>,
}
+#[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<DedicatedLoadedImage, &'static str> {
+ ) -> Result<DedicatedLoadedImage> {
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<DedicatedLoadedImage, &'static str> {
+ ) -> Result<DedicatedLoadedImage> {
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<DrawBuffers<'a, T>, 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<DrawBuffers<'a, T>> {
+ 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<Self, error::CreationError> {
+ ) -> Result<Self> {
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<M: MinBspFeatures<VulkanSystem>>(
@@ -39,7 +42,7 @@ pub fn do_render<M: MinBspFeatures<VulkanSystem>>(
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<M: MinBspFeatures<VulkanSystem>>(
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<M: MinBspFeatures<VulkanSystem>>(
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<M: MinBspFeatures<VulkanSystem>>(
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<M: MinBspFeatures<VulkanSystem>>(
// 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<M: MinBspFeatures<VulkanSystem>>(
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<SwapchainProperties, NoSupportedValuesError> {
+ ) -> Result<SwapchainProperties, EnvironmentError> {
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<TargetChain, TargetChainCreationError> {
+ ) -> Result<TargetChain> {
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<TargetResources> =
Vec::with_capacity(swap_config.image_count as usize);
let mut sync_objects: Vec<SyncObjects> =
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<UvPoint>,
pipeline: &CompletePipeline,
vp: &Mat4,
- ) -> Result<
- (
- &'a mut crate::types::CommandBufferT,
- <SurfaceT as PresentationSurface<back::Backend>>::SwapchainImage,
- ),
- &'static str,
- > {
+ ) -> Result<(
+ &'a mut crate::types::CommandBufferT,
+ <SurfaceT as PresentationSurface<back::Backend>>::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<UiPoint>,
img: &<SurfaceT as PresentationSurface<back::Backend>>::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: <SurfaceT as PresentationSurface<back::Backend>>::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<TargetResources, TargetResourcesCreationError> {
+ ) -> Result<TargetResources> {
// 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<Self, TargetResourcesCreationError> {
+ pub fn new(device: &mut DeviceT) -> Result<Self> {
// 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<T: HasTextures, R: TextureResolver<I>, I: LoadableImage> TextureLoader<T, R
debug!("Load finished for texture block {:?}", block.id);
// Destroy staging buffers
- while staging_bufs.len() > 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<T: HasTextures, R: TextureResolver<I>, I: LoadableImage> TextureLoader<T, R
self.descriptor_allocator
.allocate(
&device,
- &*self.ds_layout.read().unwrap(),
+ &*self
+ .ds_layout
+ .read()
+ .map_err(|_| LockPoisoned::Other)
+ .context("Error reading descriptor set layout")?,
DescriptorRanges::from_bindings(&[
DescriptorSetLayoutBinding {
binding: 0,
@@ -668,8 +673,7 @@ impl<T: HasTextures, R: TextureResolver<I>, I: LoadableImage> TextureLoader<T, R
device.destroy_fence(assets.0);
// Command buffer will be freed when we reset the command pool
// Destroy staging buffers
- while staging_bufs.len() > 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<DescriptorSetLayoutT> {
- self.ds_layout.read().unwrap()
+ pub fn get_ds_layout(&self) -> Result<RwLockReadGuard<DescriptorSetLayoutT>> {
+ 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<Self, error::CreationError> {
+ ) -> Result<Self> {
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<UiPoint>,
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::<Vec<String>>()
+ .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<VulkanSystem>> {
impl<'a, M: 'static + MinBspFeatures<VulkanSystem>> Renderer<'a, M> {
/// Create a new Renderer.
- pub fn new(window: &Window, file: M) -> (Self, Sender<WindowEvent>) {
+ pub fn new(window: &Window, file: M) -> Result<(Self, Sender<WindowEvent>)> {
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<T: 'static + MinBspFeatures<VulkanSystem>>(
) {
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 => {