From 1fa1d038aed7c861e0bb06a13a2ba083e22efae8 Mon Sep 17 00:00:00 2001 From: sagudev <16504129+sagudev@users.noreply.github.com> Date: Fri, 31 Oct 2025 08:11:17 +0100 Subject: [PATCH 1/3] Remove unwraps in wgpu_context Signed-off-by: sagudev <16504129+sagudev@users.noreply.github.com> --- crates/anyrender_vello/src/image_renderer.rs | 4 ++- crates/anyrender_vello/src/window_renderer.rs | 4 +-- .../src/window_renderer.rs | 4 +-- crates/wgpu_context/src/buffer_renderer.rs | 22 ++++++++------- crates/wgpu_context/src/surface_renderer.rs | 28 ++++++++----------- 5 files changed, 30 insertions(+), 32 deletions(-) diff --git a/crates/anyrender_vello/src/image_renderer.rs b/crates/anyrender_vello/src/image_renderer.rs index 240e2c3..a5fe21d 100644 --- a/crates/anyrender_vello/src/image_renderer.rs +++ b/crates/anyrender_vello/src/image_renderer.rs @@ -96,7 +96,9 @@ impl ImageRenderer for VelloImageRenderer { ) .expect("Got non-Send/Sync error from rendering"); - self.buffer_renderer.copy_texture_to_buffer(cpu_buffer); + self.buffer_renderer + .copy_texture_to_buffer(cpu_buffer) + .unwrap(); // Empty the Vello scene (memory optimisation) self.scene.reset(); diff --git a/crates/anyrender_vello/src/window_renderer.rs b/crates/anyrender_vello/src/window_renderer.rs index 886aae5..40c5841 100644 --- a/crates/anyrender_vello/src/window_renderer.rs +++ b/crates/anyrender_vello/src/window_renderer.rs @@ -213,7 +213,7 @@ impl WindowRenderer for VelloWindowRenderer { render_surface.device(), render_surface.queue(), &self.scene, - &render_surface.target_texture_view(), + &render_surface.target_texture_view().unwrap(), &RenderParams { base_color: self.config.base_color, width: render_surface.config.width, @@ -224,7 +224,7 @@ impl WindowRenderer for VelloWindowRenderer { .expect("failed to render to texture"); timer.record_time("render"); - render_surface.maybe_blit_and_present(); + render_surface.maybe_blit_and_present().unwrap(); timer.record_time("present"); render_surface.device().poll(wgpu::PollType::Wait).unwrap(); diff --git a/crates/anyrender_vello_hybrid/src/window_renderer.rs b/crates/anyrender_vello_hybrid/src/window_renderer.rs index 4455f72..73d0491 100644 --- a/crates/anyrender_vello_hybrid/src/window_renderer.rs +++ b/crates/anyrender_vello_hybrid/src/window_renderer.rs @@ -220,7 +220,7 @@ impl WindowRenderer for VelloHybridWindowRenderer { }); timer.record_time("cmd"); - let surface_texture = render_surface.current_surface_texture(); + let surface_texture = render_surface.current_surface_texture().unwrap(); let texture_view = surface_texture .texture .create_view(&TextureViewDescriptor::default()); @@ -245,7 +245,7 @@ impl WindowRenderer for VelloHybridWindowRenderer { drop(texture_view); drop(surface_texture); - render_surface.maybe_blit_and_present(); + render_surface.maybe_blit_and_present().unwrap(); timer.record_time("present"); render_surface.device().poll(wgpu::PollType::Wait).unwrap(); diff --git a/crates/wgpu_context/src/buffer_renderer.rs b/crates/wgpu_context/src/buffer_renderer.rs index 13dd082..24be68d 100644 --- a/crates/wgpu_context/src/buffer_renderer.rs +++ b/crates/wgpu_context/src/buffer_renderer.rs @@ -118,13 +118,19 @@ impl BufferRenderer { self.texture_view.clone() } - pub fn copy_texture_to_vec(&self, cpu_buffer: &mut Vec) { + pub fn copy_texture_to_vec( + &self, + cpu_buffer: &mut Vec, + ) -> Result<(), Box> { cpu_buffer.clear(); cpu_buffer.reserve((self.config.width * self.config.height * 4) as usize); - self.copy_texture_to_buffer(&mut *cpu_buffer); + self.copy_texture_to_buffer(&mut *cpu_buffer) } - pub fn copy_texture_to_buffer(&self, cpu_buffer: &mut [u8]) { + pub fn copy_texture_to_buffer( + &self, + cpu_buffer: &mut [u8], + ) -> Result<(), Box> { let mut encoder = self .device() .create_command_encoder(&CommandEncoderDescriptor { @@ -153,13 +159,8 @@ impl BufferRenderer { let (sender, receiver) = futures_intrusive::channel::shared::oneshot_channel(); buf_slice.map_async(wgpu::MapMode::Read, move |v| sender.send(v).unwrap()); - if let Ok(recv_result) = - block_on_wgpu(self.device(), receiver.receive()).inspect_err(|err| { - panic!("channel inaccessible: {:#}", err); - }) - { - let _ = recv_result.unwrap(); - } + block_on_wgpu(self.device(), receiver.receive())? + .ok_or("No value available in channel")??; let data = buf_slice.get_mapped_range(); @@ -175,5 +176,6 @@ impl BufferRenderer { // Unmap buffer drop(data); self.gpu_buffer.unmap(); + Ok(()) } } diff --git a/crates/wgpu_context/src/surface_renderer.rs b/crates/wgpu_context/src/surface_renderer.rs index 4fb46a6..9d9f51a 100644 --- a/crates/wgpu_context/src/surface_renderer.rs +++ b/crates/wgpu_context/src/surface_renderer.rs @@ -1,4 +1,5 @@ use crate::{DeviceHandle, WgpuContextError, util::create_texture}; +use wgpu::SurfaceError; use wgpu::{ CommandEncoderDescriptor, CompositeAlphaMode, Device, PresentMode, Queue, Surface, SurfaceConfiguration, SurfaceTexture, TextureFormat, TextureUsages, TextureView, @@ -187,38 +188,31 @@ impl<'s> SurfaceRenderer<'s> { .configure(&self.device_handle.device, &self.config); } - pub fn current_surface_texture(&self) -> SurfaceTexture { - self.surface - .get_current_texture() - .expect("failed to get surface texture") + pub fn current_surface_texture(&self) -> Result { + self.surface.get_current_texture() } - pub fn target_texture_view(&self) -> TextureView { + pub fn target_texture_view(&self) -> Result { match &self.intermediate_texture { - Some(intermediate_texture) => intermediate_texture.texture_view.clone(), + Some(intermediate_texture) => Ok(intermediate_texture.texture_view.clone()), None => { - let surface_texture = self - .surface - .get_current_texture() - .expect("failed to get surface texture"); - surface_texture + let surface_texture = self.surface.get_current_texture()?; + Ok(surface_texture .texture - .create_view(&TextureViewDescriptor::default()) + .create_view(&TextureViewDescriptor::default())) } } } - pub fn maybe_blit_and_present(&self) { - let surface_texture = self - .surface - .get_current_texture() - .expect("failed to get surface texture"); + pub fn maybe_blit_and_present(&self) -> Result<(), SurfaceError> { + let surface_texture = self.surface.get_current_texture()?; if let Some(its) = &self.intermediate_texture { self.blit_from_intermediate_texture_to_surface(&surface_texture, its); } surface_texture.present(); + Ok(()) } /// Blit from the intermediate texture to the surface texture From 0581433d0d4d8c956e5ff31903aa41ab91b7d015 Mon Sep 17 00:00:00 2001 From: sagudev <16504129+sagudev@users.noreply.github.com> Date: Fri, 31 Oct 2025 09:16:40 +0100 Subject: [PATCH 2/3] Make anyrender fallible Signed-off-by: sagudev <16504129+sagudev@users.noreply.github.com> --- crates/anyrender/src/lib.rs | 32 +++++++---- crates/anyrender/src/null_backend.rs | 30 ++++++++--- crates/anyrender_skia/src/image_renderer.rs | 20 ++++--- crates/anyrender_skia/src/window_renderer.rs | 21 ++++++-- crates/anyrender_vello/src/image_renderer.rs | 50 ++++++++--------- crates/anyrender_vello/src/window_renderer.rs | 54 ++++++++++--------- .../anyrender_vello_cpu/src/image_renderer.rs | 18 ++++--- .../src/window_renderer.rs | 52 ++++++++++-------- crates/pixels_window_renderer/src/lib.rs | 21 ++++++-- crates/softbuffer_window_renderer/src/lib.rs | 30 +++++++---- examples/bunnymark/src/main.rs | 5 +- examples/winit/src/main.rs | 7 ++- 12 files changed, 217 insertions(+), 123 deletions(-) diff --git a/crates/anyrender/src/lib.rs b/crates/anyrender/src/lib.rs index 0060ce3..3d8fbef 100644 --- a/crates/anyrender/src/lib.rs +++ b/crates/anyrender/src/lib.rs @@ -43,11 +43,19 @@ pub trait WindowRenderer { type ScenePainter<'a>: PaintScene where Self: 'a; - fn resume(&mut self, window: Arc, width: u32, height: u32); + fn resume( + &mut self, + window: Arc, + width: u32, + height: u32, + ) -> Result<(), Box>; fn suspend(&mut self); fn is_active(&self) -> bool; fn set_size(&mut self, width: u32, height: u32); - fn render)>(&mut self, draw_fn: F); + fn render)>( + &mut self, + draw_fn: F, + ) -> Result<(), Box>; } /// Abstraction for rendering a scene to an image buffer @@ -55,15 +63,21 @@ pub trait ImageRenderer { type ScenePainter<'a>: PaintScene where Self: 'a; - fn new(width: u32, height: u32) -> Self; + fn new(width: u32, height: u32) -> Result> + where + Self: Sized; fn resize(&mut self, width: u32, height: u32); fn reset(&mut self); fn render_to_vec)>( &mut self, draw_fn: F, vec: &mut Vec, - ); - fn render)>(&mut self, draw_fn: F, buffer: &mut [u8]); + ) -> Result<(), Box>; + fn render)>( + &mut self, + draw_fn: F, + buffer: &mut [u8], + ) -> Result<(), Box>; } /// Draw a scene to a buffer using an `ImageRenderer` @@ -71,12 +85,12 @@ pub fn render_to_buffer)>( draw_fn: F, width: u32, height: u32, -) -> Vec { +) -> Result, Box> { let mut buf = Vec::with_capacity((width * height * 4) as usize); - let mut renderer = R::new(width, height); - renderer.render_to_vec(draw_fn, &mut buf); + let mut renderer = R::new(width, height)?; + renderer.render_to_vec(draw_fn, &mut buf)?; - buf + Ok(buf) } /// Abstraction for drawing a 2D scene diff --git a/crates/anyrender/src/null_backend.rs b/crates/anyrender/src/null_backend.rs index 3af5dcf..3f97876 100644 --- a/crates/anyrender/src/null_backend.rs +++ b/crates/anyrender/src/null_backend.rs @@ -20,8 +20,14 @@ impl WindowRenderer for NullWindowRenderer { where Self: 'a; - fn resume(&mut self, _window: Arc, _width: u32, _height: u32) { + fn resume( + &mut self, + _window: Arc, + _width: u32, + _height: u32, + ) -> Result<(), Box> { self.is_active = true; + Ok(()) } fn suspend(&mut self) { @@ -34,7 +40,12 @@ impl WindowRenderer for NullWindowRenderer { fn set_size(&mut self, _width: u32, _height: u32) {} - fn render)>(&mut self, _draw_fn: F) {} + fn render)>( + &mut self, + _draw_fn: F, + ) -> Result<(), Box> { + Ok(()) + } } #[derive(Copy, Clone, Default)] @@ -52,8 +63,8 @@ impl ImageRenderer for NullImageRenderer { where Self: 'a; - fn new(_width: u32, _height: u32) -> Self { - Self + fn new(_width: u32, _height: u32) -> Result> { + Ok(Self) } fn resize(&mut self, _width: u32, _height: u32) {} @@ -64,10 +75,17 @@ impl ImageRenderer for NullImageRenderer { &mut self, _draw_fn: F, _vec: &mut Vec, - ) { + ) -> Result<(), Box> { + Ok(()) } - fn render)>(&mut self, _draw_fn: F, _buffer: &mut [u8]) {} + fn render)>( + &mut self, + _draw_fn: F, + _buffer: &mut [u8], + ) -> Result<(), Box> { + Ok(()) + } } #[derive(Copy, Clone, Default)] diff --git a/crates/anyrender_skia/src/image_renderer.rs b/crates/anyrender_skia/src/image_renderer.rs index 10f8de9..a8346ca 100644 --- a/crates/anyrender_skia/src/image_renderer.rs +++ b/crates/anyrender_skia/src/image_renderer.rs @@ -16,12 +16,12 @@ impl ImageRenderer for SkiaImageRenderer { where Self: 'a; - fn new(width: u32, height: u32) -> Self { + fn new(width: u32, height: u32) -> Result> { graphics::set_font_cache_count_limit(100); graphics::set_typeface_cache_count_limit(100); graphics::set_resource_cache_total_bytes_limit(10485760); - Self { + Ok(Self { image_info: ImageInfo::new( (width as i32, height as i32), ColorType::RGBA8888, @@ -30,7 +30,7 @@ impl ImageRenderer for SkiaImageRenderer { ), surface_props: SurfaceProps::default(), scene_cache: SkiaSceneCache::default(), - } + }) } fn resize(&mut self, width: u32, height: u32) { @@ -48,7 +48,7 @@ impl ImageRenderer for SkiaImageRenderer { &mut self, draw_fn: F, buffer: &mut Vec, - ) { + ) -> Result<(), Box> { debug_timer!(timer, feature = "log_frame_times"); let mut surface = surfaces::wrap_pixels( @@ -57,7 +57,7 @@ impl ImageRenderer for SkiaImageRenderer { None, Some(&self.surface_props), ) - .unwrap(); + .ok_or("Invalid surface parameters")?; surface.canvas().clear(Color::WHITE); @@ -71,9 +71,14 @@ impl ImageRenderer for SkiaImageRenderer { timer.record_time("cache next gen"); timer.print_times("skia_raster: "); + Ok(()) } - fn render)>(&mut self, draw_fn: F, buffer: &mut [u8]) { + fn render)>( + &mut self, + draw_fn: F, + buffer: &mut [u8], + ) -> Result<(), Box> { debug_timer!(timer, feature = "log_frame_times"); let mut surface = surfaces::wrap_pixels( @@ -82,7 +87,7 @@ impl ImageRenderer for SkiaImageRenderer { None, Some(&self.surface_props), ) - .unwrap(); + .ok_or("Invalid surface parameters")?; surface.canvas().clear(Color::WHITE); @@ -96,5 +101,6 @@ impl ImageRenderer for SkiaImageRenderer { timer.record_time("cache next gen"); timer.print_times("skia_raster: "); + Ok(()) } } diff --git a/crates/anyrender_skia/src/window_renderer.rs b/crates/anyrender_skia/src/window_renderer.rs index c6e3df4..af47358 100644 --- a/crates/anyrender_skia/src/window_renderer.rs +++ b/crates/anyrender_skia/src/window_renderer.rs @@ -49,7 +49,12 @@ impl WindowRenderer for SkiaWindowRenderer { where Self: 'a; - fn resume(&mut self, window: Arc, width: u32, height: u32) { + fn resume( + &mut self, + window: Arc, + width: u32, + height: u32, + ) -> Result<(), Box> { graphics::set_font_cache_count_limit(100); graphics::set_typeface_cache_count_limit(100); graphics::set_resource_cache_total_bytes_limit(10485760); @@ -62,7 +67,9 @@ impl WindowRenderer for SkiaWindowRenderer { self.render_state = RenderState::Active(Box::new(ActiveRenderState { backend: Box::new(backend), scene_cache: SkiaSceneCache::default(), - })) + })); + + Ok(()) } fn suspend(&mut self) { @@ -79,16 +86,19 @@ impl WindowRenderer for SkiaWindowRenderer { } } - fn render)>(&mut self, draw_fn: F) { + fn render)>( + &mut self, + draw_fn: F, + ) -> Result<(), Box> { let RenderState::Active(state) = &mut self.render_state else { - return; + return Ok(()); }; debug_timer!(timer, feature = "log_frame_times"); let mut surface = match state.backend.prepare() { Some(it) => it, - None => return, + None => return Ok(()), }; surface.canvas().restore_to_count(1); @@ -107,6 +117,7 @@ impl WindowRenderer for SkiaWindowRenderer { timer.record_time("cache next gen"); timer.print_times("skia: "); + Ok(()) } } diff --git a/crates/anyrender_vello/src/image_renderer.rs b/crates/anyrender_vello/src/image_renderer.rs index a5fe21d..e561860 100644 --- a/crates/anyrender_vello/src/image_renderer.rs +++ b/crates/anyrender_vello/src/image_renderer.rs @@ -18,7 +18,7 @@ impl ImageRenderer for VelloImageRenderer { where Self: 'a; - fn new(width: u32, height: u32) -> Self { + fn new(width: u32, height: u32) -> Result> { // Create WGPUContext let mut context = WGPUContext::new(); @@ -28,8 +28,7 @@ impl ImageRenderer for VelloImageRenderer { width, height, usage: TextureUsages::STORAGE_BINDING, - })) - .expect("No compatible device found"); + }))?; // Create vello::Renderer let vello_renderer = VelloRenderer::new( @@ -40,14 +39,13 @@ impl ImageRenderer for VelloImageRenderer { antialiasing_support: vello::AaSupport::area_only(), pipeline_cache: None, }, - ) - .expect("Got non-Send/Sync error from creating renderer"); + )?; - Self { + Ok(Self { buffer_renderer, vello_renderer, scene: VelloScene::new(), - } + }) } fn resize(&mut self, width: u32, height: u32) { @@ -62,18 +60,18 @@ impl ImageRenderer for VelloImageRenderer { &mut self, draw_fn: F, cpu_buffer: &mut Vec, - ) { + ) -> Result<(), Box> { let size = self.buffer_renderer.size(); cpu_buffer.clear(); cpu_buffer.reserve((size.width * size.height * 4) as usize); - self.render(draw_fn, cpu_buffer); + self.render(draw_fn, cpu_buffer) } fn render)>( &mut self, draw_fn: F, cpu_buffer: &mut [u8], - ) { + ) -> Result<(), Box> { draw_fn(&mut VelloScenePainter { inner: &mut self.scene, renderer: Some(&mut self.vello_renderer), @@ -81,26 +79,24 @@ impl ImageRenderer for VelloImageRenderer { }); let size = self.buffer_renderer.size(); - self.vello_renderer - .render_to_texture( - self.buffer_renderer.device(), - self.buffer_renderer.queue(), - &self.scene, - &self.buffer_renderer.target_texture_view(), - &vello::RenderParams { - base_color: vello::peniko::Color::TRANSPARENT, - width: size.width, - height: size.height, - antialiasing_method: vello::AaConfig::Area, - }, - ) - .expect("Got non-Send/Sync error from rendering"); + self.vello_renderer.render_to_texture( + self.buffer_renderer.device(), + self.buffer_renderer.queue(), + &self.scene, + &self.buffer_renderer.target_texture_view(), + &vello::RenderParams { + base_color: vello::peniko::Color::TRANSPARENT, + width: size.width, + height: size.height, + antialiasing_method: vello::AaConfig::Area, + }, + )?; - self.buffer_renderer - .copy_texture_to_buffer(cpu_buffer) - .unwrap(); + self.buffer_renderer.copy_texture_to_buffer(cpu_buffer)?; // Empty the Vello scene (memory optimisation) self.scene.reset(); + + Ok(()) } } diff --git a/crates/anyrender_vello/src/window_renderer.rs b/crates/anyrender_vello/src/window_renderer.rs index 40c5841..a3e0125 100644 --- a/crates/anyrender_vello/src/window_renderer.rs +++ b/crates/anyrender_vello/src/window_renderer.rs @@ -127,7 +127,12 @@ impl WindowRenderer for VelloWindowRenderer { matches!(self.render_state, RenderState::Active(_)) } - fn resume(&mut self, window_handle: Arc, width: u32, height: u32) { + fn resume( + &mut self, + window_handle: Arc, + width: u32, + height: u32, + ) -> Result<(), Box> { // Create wgpu_context::SurfaceRenderer let render_surface = pollster::block_on(self.wgpu_context.create_surface( window_handle.clone(), @@ -144,8 +149,7 @@ impl WindowRenderer for VelloWindowRenderer { Some(TextureConfiguration { usage: TextureUsages::STORAGE_BINDING | TextureUsages::TEXTURE_BINDING, }), - )) - .expect("Error creating surface"); + ))?; // Create vello::Renderer let renderer = VelloRenderer::new( @@ -157,8 +161,7 @@ impl WindowRenderer for VelloWindowRenderer { // TODO: add pipeline cache pipeline_cache: None, }, - ) - .unwrap(); + )?; // Resume custom paint sources let device_handle = &render_surface.device_handle; @@ -172,6 +175,7 @@ impl WindowRenderer for VelloWindowRenderer { renderer, render_surface, }); + Ok(()) } fn suspend(&mut self) { @@ -190,9 +194,12 @@ impl WindowRenderer for VelloWindowRenderer { }; } - fn render)>(&mut self, draw_fn: F) { + fn render)>( + &mut self, + draw_fn: F, + ) -> Result<(), Box> { let RenderState::Active(state) = &mut self.render_state else { - return; + return Ok(()); }; let render_surface = &state.render_surface; @@ -207,27 +214,24 @@ impl WindowRenderer for VelloWindowRenderer { }); timer.record_time("cmd"); - state - .renderer - .render_to_texture( - render_surface.device(), - render_surface.queue(), - &self.scene, - &render_surface.target_texture_view().unwrap(), - &RenderParams { - base_color: self.config.base_color, - width: render_surface.config.width, - height: render_surface.config.height, - antialiasing_method: self.config.antialiasing_method, - }, - ) - .expect("failed to render to texture"); + state.renderer.render_to_texture( + render_surface.device(), + render_surface.queue(), + &self.scene, + &render_surface.target_texture_view()?, + &RenderParams { + base_color: self.config.base_color, + width: render_surface.config.width, + height: render_surface.config.height, + antialiasing_method: self.config.antialiasing_method, + }, + )?; timer.record_time("render"); - render_surface.maybe_blit_and_present().unwrap(); + render_surface.maybe_blit_and_present()?; timer.record_time("present"); - render_surface.device().poll(wgpu::PollType::Wait).unwrap(); + render_surface.device().poll(wgpu::PollType::Wait)?; timer.record_time("wait"); timer.print_times("vello: "); @@ -237,5 +241,7 @@ impl WindowRenderer for VelloWindowRenderer { // Empty the Vello scene (memory optimisation) self.scene.reset(); + + Ok(()) } } diff --git a/crates/anyrender_vello_cpu/src/image_renderer.rs b/crates/anyrender_vello_cpu/src/image_renderer.rs index bd62dad..618652c 100644 --- a/crates/anyrender_vello_cpu/src/image_renderer.rs +++ b/crates/anyrender_vello_cpu/src/image_renderer.rs @@ -10,10 +10,10 @@ pub struct VelloCpuImageRenderer { impl ImageRenderer for VelloCpuImageRenderer { type ScenePainter<'a> = VelloCpuScenePainter; - fn new(width: u32, height: u32) -> Self { - Self { + fn new(width: u32, height: u32) -> Result> { + Ok(Self { scene: VelloCpuScenePainter(RenderContext::new(width as u16, height as u16)), - } + }) } fn resize(&mut self, width: u32, height: u32) { @@ -24,7 +24,11 @@ impl ImageRenderer for VelloCpuImageRenderer { self.scene.0.reset(); } - fn render)>(&mut self, draw_fn: F, buffer: &mut [u8]) { + fn render)>( + &mut self, + draw_fn: F, + buffer: &mut [u8], + ) -> Result<(), Box> { debug_timer!(timer, feature = "log_frame_times"); draw_fn(&mut self.scene); @@ -42,16 +46,18 @@ impl ImageRenderer for VelloCpuImageRenderer { timer.record_time("render"); timer.print_times("vello_cpu: "); + + Ok(()) } fn render_to_vec)>( &mut self, draw_fn: F, buffer: &mut Vec, - ) { + ) -> Result<(), Box> { let width = self.scene.0.width(); let height = self.scene.0.height(); buffer.resize(width as usize * height as usize * 4, 0); - self.render(draw_fn, &mut *buffer); + self.render(draw_fn, &mut *buffer) } } diff --git a/crates/anyrender_vello_hybrid/src/window_renderer.rs b/crates/anyrender_vello_hybrid/src/window_renderer.rs index 73d0491..381cae1 100644 --- a/crates/anyrender_vello_hybrid/src/window_renderer.rs +++ b/crates/anyrender_vello_hybrid/src/window_renderer.rs @@ -118,7 +118,12 @@ impl WindowRenderer for VelloHybridWindowRenderer { matches!(self.render_state, RenderState::Active(_)) } - fn resume(&mut self, window_handle: Arc, width: u32, height: u32) { + fn resume( + &mut self, + window_handle: Arc, + width: u32, + height: u32, + ) -> Result<(), Box> { // Create wgpu_context::SurfaceRenderer let render_surface = pollster::block_on(self.wgpu_context.create_surface( window_handle.clone(), @@ -138,8 +143,7 @@ impl WindowRenderer for VelloHybridWindowRenderer { // Some(TextureConfiguration { // usage: TextureUsages::STORAGE_BINDING | TextureUsages::TEXTURE_BINDING, // }), - )) - .expect("Error creating surface"); + ))?; // Create vello::Renderer let renderer = VelloHybridRenderer::new( @@ -164,6 +168,8 @@ impl WindowRenderer for VelloHybridWindowRenderer { renderer, render_surface, }); + + Ok(()) } fn suspend(&mut self) { @@ -189,9 +195,12 @@ impl WindowRenderer for VelloHybridWindowRenderer { } } - fn render)>(&mut self, draw_fn: F) { + fn render)>( + &mut self, + draw_fn: F, + ) -> Result<(), Box> { let RenderState::Active(state) = &mut self.render_state else { - return; + return Ok(()); }; let render_surface = &state.render_surface; @@ -220,35 +229,32 @@ impl WindowRenderer for VelloHybridWindowRenderer { }); timer.record_time("cmd"); - let surface_texture = render_surface.current_surface_texture().unwrap(); + let surface_texture = render_surface.current_surface_texture()?; let texture_view = surface_texture .texture .create_view(&TextureViewDescriptor::default()); - state - .renderer - .render( - &self.scene, - render_surface.device(), - render_surface.queue(), - &mut encoder, - &RenderSize { - width: render_surface.config.width, - height: render_surface.config.height, - }, - &texture_view, - ) - .expect("failed to render to texture"); + state.renderer.render( + &self.scene, + render_surface.device(), + render_surface.queue(), + &mut encoder, + &RenderSize { + width: render_surface.config.width, + height: render_surface.config.height, + }, + &texture_view, + )?; render_surface.queue().submit([encoder.finish()]); timer.record_time("render"); drop(texture_view); drop(surface_texture); - render_surface.maybe_blit_and_present().unwrap(); + render_surface.maybe_blit_and_present()?; timer.record_time("present"); - render_surface.device().poll(wgpu::PollType::Wait).unwrap(); + render_surface.device().poll(wgpu::PollType::Wait)?; timer.record_time("wait"); timer.print_times("vello_hybrid: "); @@ -258,5 +264,7 @@ impl WindowRenderer for VelloHybridWindowRenderer { // Empty the Vello scene (memory optimisation) self.scene.reset(); + + Ok(()) } } diff --git a/crates/pixels_window_renderer/src/lib.rs b/crates/pixels_window_renderer/src/lib.rs index e36986f..86e499a 100644 --- a/crates/pixels_window_renderer/src/lib.rs +++ b/crates/pixels_window_renderer/src/lib.rs @@ -30,7 +30,7 @@ pub struct PixelsWindowRenderer { impl PixelsWindowRenderer { #[allow(clippy::new_without_default)] pub fn new() -> Self { - Self::with_renderer(Renderer::new(0, 0)) + Self::with_renderer(Renderer::new(0, 0).unwrap()) } pub fn with_renderer(renderer: R) -> PixelsWindowRenderer { @@ -52,7 +52,12 @@ impl WindowRenderer for PixelsWindowRenderer matches!(self.render_state, RenderState::Active(_)) } - fn resume(&mut self, window_handle: Arc, width: u32, height: u32) { + fn resume( + &mut self, + window_handle: Arc, + width: u32, + height: u32, + ) -> Result<(), Box> { let surface = SurfaceTexture::new(width, height, window_handle.clone()); let mut pixels = Pixels::new(width, height, surface).unwrap(); pixels.enable_vsync(true); @@ -66,6 +71,7 @@ impl WindowRenderer for PixelsWindowRenderer self.window_handle = Some(window_handle); self.set_size(width, height); + Ok(()) } fn suspend(&mut self) { @@ -86,15 +92,18 @@ impl WindowRenderer for PixelsWindowRenderer }; } - fn render)>(&mut self, draw_fn: F) { + fn render)>( + &mut self, + draw_fn: F, + ) -> Result<(), Box> { let RenderState::Active(state) = &mut self.render_state else { - return; + return Ok(()); }; debug_timer!(timer, feature = "log_frame_times"); // Paint - self.renderer.render(draw_fn, state.pixels.frame_mut()); + self.renderer.render(draw_fn, state.pixels.frame_mut())?; timer.record_time("render"); state.pixels.render().unwrap(); @@ -103,5 +112,7 @@ impl WindowRenderer for PixelsWindowRenderer // Reset the renderer ready for the next render self.renderer.reset(); + + Ok(()) } } diff --git a/crates/softbuffer_window_renderer/src/lib.rs b/crates/softbuffer_window_renderer/src/lib.rs index af33643..20f20d7 100644 --- a/crates/softbuffer_window_renderer/src/lib.rs +++ b/crates/softbuffer_window_renderer/src/lib.rs @@ -30,7 +30,7 @@ pub struct SoftbufferWindowRenderer { impl SoftbufferWindowRenderer { #[allow(clippy::new_without_default)] pub fn new() -> Self { - Self::with_renderer(Renderer::new(0, 0)) + Self::with_renderer(Renderer::new(0, 0).unwrap()) } pub fn with_renderer(renderer: R) -> SoftbufferWindowRenderer { @@ -52,9 +52,14 @@ impl WindowRenderer for SoftbufferWindowRenderer, width: u32, height: u32) { - let context = Context::new(window_handle.clone()).unwrap(); - let surface = Surface::new(&context, window_handle.clone()).unwrap(); + fn resume( + &mut self, + window_handle: Arc, + width: u32, + height: u32, + ) -> Result<(), Box> { + let context = Context::new(window_handle.clone())?; + let surface = Surface::new(&context, window_handle.clone())?; self.render_state = RenderState::Active(ActiveRenderState { _context: context, surface, @@ -62,6 +67,8 @@ impl WindowRenderer for SoftbufferWindowRenderer WindowRenderer for SoftbufferWindowRenderer)>(&mut self, draw_fn: F) { + fn render)>( + &mut self, + draw_fn: F, + ) -> Result<(), Box> { let RenderState::Active(state) = &mut self.render_state else { - return; + return Ok(()); }; debug_timer!(timer, feature = "log_frame_times"); let Ok(mut surface_buffer) = state.surface.buffer_mut() else { - return; + return Ok(()); }; timer.record_time("buffer_mut"); // Paint let mut vec = Vec::new(); - self.renderer.render_to_vec(draw_fn, &mut vec); + self.renderer.render_to_vec(draw_fn, &mut vec)?; timer.record_time("render"); let out = surface_buffer.as_mut(); @@ -115,11 +125,13 @@ impl WindowRenderer for SoftbufferWindowRenderer { diff --git a/examples/winit/src/main.rs b/examples/winit/src/main.rs index b651707..9270860 100644 --- a/examples/winit/src/main.rs +++ b/examples/winit/src/main.rs @@ -162,7 +162,9 @@ impl App { Arc::new(event_loop.create_window(attr).unwrap()) }); - renderer.resume(window.clone(), self.width, self.height); + renderer + .resume(window.clone(), self.width, self.height) + .unwrap(); let renderer = renderer.into(); self.render_state = RenderState::Active { window, renderer }; self.request_redraw(); @@ -213,7 +215,8 @@ impl ApplicationHandler for App { r.render(|p| App::draw_scene(p, Color::from_rgb8(0, 0, 255))) } Renderer::Null(r) => r.render(|p| App::draw_scene(p, Color::from_rgb8(0, 0, 0))), - }, + } + .unwrap(), WindowEvent::KeyboardInput { event: KeyEvent { From e1eb4ccb7d6f289af8eb7f0058f50b99b1090c2c Mon Sep 17 00:00:00 2001 From: sagudev <16504129+sagudev@users.noreply.github.com> Date: Fri, 31 Oct 2025 09:31:49 +0100 Subject: [PATCH 3/3] Remove some panics in skia Signed-off-by: sagudev <16504129+sagudev@users.noreply.github.com> --- crates/anyrender_skia/src/opengl.rs | 28 ++++++++------------ crates/anyrender_skia/src/window_renderer.rs | 2 +- 2 files changed, 12 insertions(+), 18 deletions(-) diff --git a/crates/anyrender_skia/src/opengl.rs b/crates/anyrender_skia/src/opengl.rs index 3846c60..b675d15 100644 --- a/crates/anyrender_skia/src/opengl.rs +++ b/crates/anyrender_skia/src/opengl.rs @@ -31,7 +31,7 @@ impl OpenGLBackend { window: Arc, width: u32, height: u32, - ) -> OpenGLBackend { + ) -> Result> { let raw_display_handle = window.display_handle().unwrap().as_raw(); let raw_window_handle = window.window_handle().unwrap().as_raw(); @@ -44,15 +44,13 @@ impl OpenGLBackend { DisplayApiPreference::Wgl(Some(raw_window_handle.clone())), #[cfg(not(any(target_os = "windows", target_os = "macos")))] DisplayApiPreference::Egl, - ) - .unwrap() + )? }; let gl_config_template = ConfigTemplateBuilder::new().with_transparency(true).build(); let gl_config = unsafe { gl_display - .find_configs(gl_config_template) - .unwrap() + .find_configs(gl_config_template)? .reduce(|accum, config| { let transparency_check = config.supports_transparency().unwrap_or(false) & !accum.supports_transparency().unwrap_or(false); @@ -63,7 +61,7 @@ impl OpenGLBackend { accum } }) - .unwrap() + .ok_or("No config available")? }; let gl_context_attrs = ContextAttributesBuilder::new().build(Some(raw_window_handle)); @@ -73,20 +71,16 @@ impl OpenGLBackend { NonZeroU32::new(height).expect("height should be a positive value"), ); - let gl_not_current_context = unsafe { - gl_display - .create_context(&gl_config, &gl_context_attrs) - .unwrap() - }; + let gl_not_current_context = + unsafe { gl_display.create_context(&gl_config, &gl_context_attrs)? }; let gl_surface = unsafe { gl_config .display() - .create_window_surface(&gl_config, &gl_surface_attrs) - .unwrap() + .create_window_surface(&gl_config, &gl_surface_attrs)? }; - let gl_context = gl_not_current_context.make_current(&gl_surface).unwrap(); + let gl_context = gl_not_current_context.make_current(&gl_surface)?; gl::load_with(|s| { gl_config @@ -113,13 +107,13 @@ impl OpenGLBackend { } skia_safe::gpu::gl::FramebufferInfo { - fboid: fboid.try_into().unwrap(), + fboid: fboid.try_into()?, format: skia_safe::gpu::gl::Format::RGBA8.into(), ..Default::default() } }; - OpenGLBackend { + Ok(OpenGLBackend { surface: Some(Self::create_surface( width, height, @@ -132,7 +126,7 @@ impl OpenGLBackend { gl_surface, gl_context, fb_info, - } + }) } fn create_surface( diff --git a/crates/anyrender_skia/src/window_renderer.rs b/crates/anyrender_skia/src/window_renderer.rs index af47358..44bc582 100644 --- a/crates/anyrender_skia/src/window_renderer.rs +++ b/crates/anyrender_skia/src/window_renderer.rs @@ -62,7 +62,7 @@ impl WindowRenderer for SkiaWindowRenderer { #[cfg(target_os = "macos")] let backend = crate::metal::MetalBackend::new(window, width, height); #[cfg(not(target_os = "macos"))] - let backend = crate::opengl::OpenGLBackend::new(window, width, height); + let backend = crate::opengl::OpenGLBackend::new(window, width, height)?; self.render_state = RenderState::Active(Box::new(ActiveRenderState { backend: Box::new(backend),