Skip to content

Commit

Permalink
Better error types (#516)
Browse files Browse the repository at this point in the history
Using custom `thiserror` errors instead of `Box<dyn Error>`, closes #285

This removed the use of `Result` as a return type for many functions,
including `RenderContext::new()`. This "could" fail when no backend
feature is activated for wgpu, but because we don't vendor them, this
should never happen. (also there is no failable version of this
function)

I also had to make a `RendererError` which I totally hate, but the
problem here is that without the `wgpu-profiler` feature this function
never fails, so should I remove the `Result` and just unwrap for the
`GpuProfiler`?

There are still many unwraps, which may(?) still be handled correctly
with errors, but for most of them I'm not sure. They are in many
functions which had `Result` return types but still, `unwrap` was
called, so was it intention?

---------

Co-authored-by: Kaur Kuut <[email protected]>
  • Loading branch information
DasLixou and xStrom authored May 30, 2024
1 parent 2f25262 commit 3842b3d
Show file tree
Hide file tree
Showing 6 changed files with 140 additions and 108 deletions.
77 changes: 39 additions & 38 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ raw-window-handle = { workspace = true }
static_assertions = { workspace = true }
futures-intrusive = { workspace = true }
wgpu-profiler = { workspace = true, optional = true }
thiserror = { workspace = true }

[workspace.lints]
clippy.doc_markdown = "warn"
Expand All @@ -80,6 +81,7 @@ futures-intrusive = "0.5.0"
raw-window-handle = "0.6.1"
smallvec = "1.13.2"
static_assertions = "1.1.0"
thiserror = "1.0.57"

# NOTE: Make sure to keep this in sync with the version badge in README.md
# as well as examples/simple/Cargo.toml
Expand Down
62 changes: 47 additions & 15 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ pub mod util;

pub use render::Render;
pub use scene::{DrawGlyphs, Scene};
use thiserror::Error;
#[cfg(feature = "wgpu")]
pub use util::block_on_wgpu;

Expand All @@ -126,12 +127,6 @@ use wgpu::{Device, PipelineCompilationOptions, Queue, SurfaceTexture, TextureFor
#[cfg(all(feature = "wgpu", feature = "wgpu-profiler"))]
use wgpu_profiler::{GpuProfiler, GpuProfilerSettings};

/// Catch-all error type.
pub type Error = Box<dyn std::error::Error>;

/// Specialization of `Result` for our catch-all error type.
pub type Result<T> = std::result::Result<T, Error>;

/// Represents the antialiasing method to use during a render pass.
#[derive(Copy, Clone, PartialEq, Eq)]
pub enum AaConfig {
Expand Down Expand Up @@ -183,6 +178,47 @@ impl FromIterator<AaConfig> for AaSupport {
}
}

/// Errors that can occur in Vello.
#[derive(Error, Debug)]
#[non_exhaustive]
pub enum Error {
/// There is no available device with the features required by Vello.
#[cfg(feature = "wgpu")]
#[error("Couldn't find suitable device")]
NoCompatibleDevice,
/// Failed to create surface.
/// See [`wgpu::CreateSurfaceError`] for more information.
#[cfg(feature = "wgpu")]
#[error("Couldn't create wgpu surface")]
WgpuCreateSurfaceError(#[from] wgpu::CreateSurfaceError),
/// Surface doesn't support the required texture formats.
/// Make sure that you have a surface which provides one of
/// [`TextureFormat::Rgba8Unorm`] or [`TextureFormat::Bgra8Unorm`] as texture formats.
#[cfg(feature = "wgpu")]
#[error("Couldn't find `Rgba8Unorm` or `Bgra8Unorm` texture formats for surface")]
UnsupportedSurfaceFormat,

/// Used a buffer inside a recording while it was not available.
/// Check if you have created it and not freed before its last usage.
#[cfg(feature = "wgpu")]
#[error("Buffer '{0}' is not available but used for {1}")]
UnavailableBufferUsed(&'static str, &'static str),
/// Failed to async map a buffer.
/// See [`wgpu::BufferAsyncError`] for more information.
#[cfg(feature = "wgpu")]
#[error("Failed to async map a buffer")]
BufferAsyncError(#[from] wgpu::BufferAsyncError),

/// Failed to create [`GpuProfiler`].
/// See [`wgpu_profiler::CreationError`] for more information.
#[cfg(feature = "wgpu-profiler")]
#[error("Couldn't create wgpu profiler")]
ProfilerCreationError(#[from] wgpu_profiler::CreationError),
}

#[allow(dead_code)] // this can be unused when wgpu feature is not used
pub(crate) type Result<T, E = Error> = std::result::Result<T, E>;

/// Renders a scene into a texture or surface.
#[cfg(feature = "wgpu")]
pub struct Renderer {
Expand Down Expand Up @@ -257,7 +293,7 @@ impl Renderer {
#[cfg(not(target_arch = "wasm32"))]
engine.use_parallel_initialisation();
}
let shaders = shaders::full_shaders(device, &mut engine, &options)?;
let shaders = shaders::full_shaders(device, &mut engine, &options);
#[cfg(not(target_arch = "wasm32"))]
engine.build_shaders_if_needed(device, options.num_init_threads);
let blit = options
Expand Down Expand Up @@ -399,14 +435,14 @@ impl Renderer {

/// Reload the shaders. This should only be used during `vello` development
#[cfg(feature = "hot_reload")]
pub async fn reload_shaders(&mut self, device: &Device) -> Result<()> {
pub async fn reload_shaders(&mut self, device: &Device) -> Result<(), wgpu::Error> {
device.push_error_scope(wgpu::ErrorFilter::Validation);
let mut engine = WgpuEngine::new(self.options.use_cpu);
// We choose not to initialise these shaders in parallel, to ensure the error scope works correctly
let shaders = shaders::full_shaders(device, &mut engine, &self.options)?;
let shaders = shaders::full_shaders(device, &mut engine, &self.options);
let error = device.pop_error_scope().await;
if let Some(error) = error {
return Err(error.into());
return Err(error);
}
self.engine = engine;
self.shaders = shaders;
Expand Down Expand Up @@ -460,11 +496,7 @@ impl Renderer {
let buf_slice = bump_buf.slice(..);
let (sender, receiver) = futures_intrusive::channel::shared::oneshot_channel();
buf_slice.map_async(wgpu::MapMode::Read, move |v| sender.send(v).unwrap());
if let Some(recv_result) = receiver.receive().await {
recv_result?;
} else {
return Err("channel was closed".into());
}
receiver.receive().await.expect("channel was closed")?;
let mapped = buf_slice.get_mapped_range();
bump = Some(bytemuck::pod_read_unaligned(&mapped));
}
Expand Down
Loading

0 comments on commit 3842b3d

Please sign in to comment.