diff --git a/Cargo.lock b/Cargo.lock index bcc13d8..f98e445 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -130,6 +130,24 @@ dependencies = [ "vello_cpu", ] +[[package]] +name = "anyrender_vello_hybrid" +version = "0.1.0" +dependencies = [ + "anyrender", + "debug_timer", + "futures-intrusive", + "kurbo 0.12.0", + "peniko", + "pollster 0.4.0", + "raw-window-handle", + "rustc-hash", + "vello_common", + "vello_hybrid", + "wgpu 26.0.1", + "wgpu_context", +] + [[package]] name = "arrayref" version = "0.3.9" @@ -257,6 +275,7 @@ dependencies = [ "anyrender", "anyrender_vello", "anyrender_vello_cpu", + "anyrender_vello_hybrid", "fastrand", "image", "kurbo 0.12.0", @@ -735,9 +754,9 @@ dependencies = [ [[package]] name = "fearless_simd" -version = "0.2.0" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb8f822f06d1b452a9207f11ce3292f7ee6c23d5d2e7ddfd07990a9107804850" +checksum = "8fb2907d1f08b2b316b9223ced5b0e89d87028ba8deae9764741dba8ff7f3903" dependencies = [ "bytemuck", ] @@ -2688,24 +2707,25 @@ dependencies = [ [[package]] name = "vello_common" -version = "0.0.3" +version = "0.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a368a0edbbe19080297a7bbd9e24860ba3a224226b189d6bf14cf8187459921" +checksum = "a235ba928b3109ad9e7696270edb09445a52ae1c7c08e6d31a19b1cdd6cbc24a" dependencies = [ "bytemuck", "fearless_simd", "hashbrown 0.15.5", "log", "peniko", + "png 0.17.16", "skrifa", "smallvec", ] [[package]] name = "vello_cpu" -version = "0.0.3" +version = "0.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "401cfde7308b9193bdfe53649648b10020664c556f2e17724c39366cc220b9d1" +checksum = "b0bd1fcf9c1814f17a491e07113623d44e3ec1125a9f3401f5e047d6d326da21" dependencies = [ "bytemuck", "crossbeam-channel", @@ -2728,6 +2748,22 @@ dependencies = [ "smallvec", ] +[[package]] +name = "vello_hybrid" +version = "0.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25707b2b4f5fe1f4f1b764ce78154629dab8c7f9cee18b4264569c6a01f15b53" +dependencies = [ + "bytemuck", + "guillotiere", + "hashbrown 0.15.5", + "log", + "thiserror 2.0.17", + "vello_common", + "vello_sparse_shaders", + "wgpu 26.0.1", +] + [[package]] name = "vello_shaders" version = "0.6.0" @@ -2741,6 +2777,12 @@ dependencies = [ "vello_encoding", ] +[[package]] +name = "vello_sparse_shaders" +version = "0.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a49fba8e6c201b4764f2574661eb18e7b98e0b5efff91cda2782205abfbdc2e2" + [[package]] name = "version_check" version = "0.9.5" @@ -3598,6 +3640,7 @@ dependencies = [ "anyrender", "anyrender_vello", "anyrender_vello_cpu", + "anyrender_vello_hybrid", "kurbo 0.12.0", "peniko", "wgpu 26.0.1", diff --git a/Cargo.toml b/Cargo.toml index 099486a..aadf349 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,6 +3,7 @@ members = [ "crates/anyrender", "crates/anyrender_vello", "crates/anyrender_vello_cpu", + "crates/anyrender_vello_hybrid", "crates/anyrender_svg", "crates/wgpu_context", "crates/pixels_window_renderer", @@ -26,6 +27,7 @@ rust-version = "1.86.0" anyrender = { version = "0.6.0", path = "./crates/anyrender" } anyrender_vello = { version = "0.6.0", path = "./crates/anyrender_vello" } anyrender_vello_cpu = { version = "0.7.0", path = "./crates/anyrender_vello_cpu" } +anyrender_vello_hybrid = { version = "0.1.0", path = "./crates/anyrender_vello_hybrid" } anyrender_svg = { version = "0.6.0", path = "./crates/anyrender_svg" } wgpu_context = { version = "0.1.0", path = "./crates/wgpu_context" } pixels_window_renderer = { version = "0.1.0", path = "./crates/pixels_window_renderer" } @@ -37,7 +39,9 @@ linebender_resource_handle = "0.1" peniko = "0.5.0" kurbo = "0.12" vello = { version = "0.6", features = [ "wgpu" ] } -vello_cpu = { version = "0.0.3", default-features = false, features = ["std", "text"]} +vello_cpu = { version = "0.0.4", default-features = false, features = ["std", "text"] } +vello_hybrid = { version = "0.0.4" } +vello_common = { version = "0.0.4" } # Rendering wgpu = "26" diff --git a/crates/anyrender_vello_hybrid/Cargo.toml b/crates/anyrender_vello_hybrid/Cargo.toml new file mode 100644 index 0000000..be122af --- /dev/null +++ b/crates/anyrender_vello_hybrid/Cargo.toml @@ -0,0 +1,27 @@ +[package] +name = "anyrender_vello_hybrid" +description = "vello_hybrid backend for anyrender" +version = "0.1.0" +documentation = "https://docs.rs/anyrender_vello_hybrid" +homepage.workspace = true +repository.workspace = true +license.workspace = true +edition.workspace = true +publish = false + +[features] +log_frame_times = ["debug_timer/enable"] + +[dependencies] +anyrender = { workspace = true } +debug_timer = { workspace = true } +kurbo = { workspace = true } +peniko = { workspace = true } +vello_hybrid = { workspace = true } +vello_common = { workspace = true } +raw-window-handle = { workspace = true } +wgpu = { workspace = true } +futures-intrusive = { workspace = true } +pollster = { workspace = true } +rustc-hash = { workspace = true } +wgpu_context = { workspace = true } \ No newline at end of file diff --git a/crates/anyrender_vello_hybrid/src/lib.rs b/crates/anyrender_vello_hybrid/src/lib.rs new file mode 100644 index 0000000..60939fd --- /dev/null +++ b/crates/anyrender_vello_hybrid/src/lib.rs @@ -0,0 +1,8 @@ +//! An Anyrender backend using the vello_cpu crate +#![cfg_attr(docsrs, feature(doc_cfg))] + +mod scene; +mod window_renderer; + +pub use scene::VelloHybridScenePainter; +pub use window_renderer::*; diff --git a/crates/anyrender_vello_hybrid/src/scene.rs b/crates/anyrender_vello_hybrid/src/scene.rs new file mode 100644 index 0000000..8d1b387 --- /dev/null +++ b/crates/anyrender_vello_hybrid/src/scene.rs @@ -0,0 +1,211 @@ +use anyrender::{NormalizedCoord, Paint, PaintRef, PaintScene}; +use kurbo::{Affine, Rect, Shape, Stroke}; +use peniko::{BlendMode, Color, Fill, FontData, ImageBrush, ImageData, StyleRef}; +use rustc_hash::FxHashMap; +use vello_common::paint::{ImageId, ImageSource, PaintType}; +use vello_hybrid::Renderer; +use wgpu::{CommandEncoder, Device, Queue}; + +const DEFAULT_TOLERANCE: f64 = 0.1; + +fn anyrender_paint_to_vello_hybrid_paint<'a>( + paint: PaintRef<'a>, + mut image_manager: &mut Option<&mut ImageManager<'_>>, +) -> PaintType { + match paint { + Paint::Solid(alpha_color) => PaintType::Solid(alpha_color), + Paint::Gradient(gradient) => PaintType::Gradient(gradient.clone()), + + Paint::Image(image_brush) => { + if let Some(image_manager) = &mut image_manager { + let image_id = image_manager.upload_image(image_brush.image); + PaintType::Image(ImageBrush { + image: ImageSource::OpaqueId(image_id), + sampler: image_brush.sampler, + }) + } else { + PaintType::Solid(peniko::color::palette::css::TRANSPARENT) + } + } + + // TODO: custom paint + Paint::Custom(_) => PaintType::Solid(peniko::color::palette::css::TRANSPARENT), + } +} + +pub(crate) struct ImageManager<'a> { + pub(crate) renderer: &'a mut Renderer, + pub(crate) device: &'a Device, + pub(crate) queue: &'a Queue, + pub(crate) encoder: &'a mut CommandEncoder, + pub(crate) cache: &'a mut FxHashMap, +} + +impl ImageManager<'_> { + pub(crate) fn upload_image(&mut self, image: &ImageData) -> ImageId { + let peniko_id = image.data.id(); + + // Try to get ImageId from cache first + if let Some(atlas_id) = self.cache.get(&peniko_id) { + return *atlas_id; + }; + + // Convert ImageData to Pixmap + let ImageSource::Pixmap(pixmap) = ImageSource::from_peniko_image_data(image) else { + unreachable!(); // ImageSource::from_peniko_image_data always return a Pixmap + }; + + // Upload Pixamp + let atlas_id = self + .renderer + .upload_image(self.device, self.queue, self.encoder, &pixmap); + + // Store ImageId in cache + self.cache.insert(peniko_id, atlas_id); + + // Return ImageId + atlas_id + } +} + +pub struct VelloHybridScenePainter<'s> { + pub(crate) scene: &'s mut vello_hybrid::Scene, + pub(crate) image_manager: Option>, +} + +impl VelloHybridScenePainter<'_> { + pub fn new<'s>(scene: &'s mut vello_hybrid::Scene) -> VelloHybridScenePainter<'s> { + VelloHybridScenePainter { + scene, + image_manager: None, + } + } +} + +impl PaintScene for VelloHybridScenePainter<'_> { + fn reset(&mut self) { + self.scene.reset(); + } + + fn push_layer( + &mut self, + blend: impl Into, + alpha: f32, + transform: Affine, + clip: &impl Shape, + ) { + self.scene.set_transform(transform); + self.scene.push_layer( + Some(&clip.into_path(DEFAULT_TOLERANCE)), + Some(blend.into()), + Some(alpha), + None, + ); + } + + fn pop_layer(&mut self) { + self.scene.pop_layer(); + } + + fn stroke<'a>( + &mut self, + style: &Stroke, + transform: Affine, + paint: impl Into>, + brush_transform: Option, + shape: &impl Shape, + ) { + self.scene.set_transform(transform); + self.scene.set_stroke(style.clone()); + let paint = + anyrender_paint_to_vello_hybrid_paint(paint.into(), &mut self.image_manager.as_mut()); + self.scene.set_paint(paint); + self.scene + .set_paint_transform(brush_transform.unwrap_or(Affine::IDENTITY)); + self.scene.stroke_path(&shape.into_path(DEFAULT_TOLERANCE)); + } + + fn fill<'a>( + &mut self, + style: Fill, + transform: Affine, + paint: impl Into>, + brush_transform: Option, + shape: &impl Shape, + ) { + self.scene.set_transform(transform); + self.scene.set_fill_rule(style); + let paint = + anyrender_paint_to_vello_hybrid_paint(paint.into(), &mut self.image_manager.as_mut()); + self.scene.set_paint(paint); + self.scene + .set_paint_transform(brush_transform.unwrap_or(Affine::IDENTITY)); + self.scene.fill_path(&shape.into_path(DEFAULT_TOLERANCE)); + } + + fn draw_glyphs<'a, 's: 'a>( + &'a mut self, + font: &'a FontData, + font_size: f32, + hint: bool, + normalized_coords: &'a [NormalizedCoord], + style: impl Into>, + paint: impl Into>, + _brush_alpha: f32, + transform: Affine, + glyph_transform: Option, + glyphs: impl Iterator, + ) { + let paint = + anyrender_paint_to_vello_hybrid_paint(paint.into(), &mut self.image_manager.as_mut()); + self.scene.set_paint(paint); + self.scene.set_transform(transform); + + fn into_vello_hybrid_glyph(g: anyrender::Glyph) -> vello_common::glyph::Glyph { + vello_common::glyph::Glyph { + id: g.id, + x: g.x, + y: g.y, + } + } + + let style: StyleRef<'a> = style.into(); + match style { + StyleRef::Fill(fill) => { + self.scene.set_fill_rule(fill); + self.scene + .glyph_run(font) + .font_size(font_size) + .hint(hint) + .normalized_coords(normalized_coords) + .glyph_transform(glyph_transform.unwrap_or_default()) + .fill_glyphs(glyphs.map(into_vello_hybrid_glyph)); + } + StyleRef::Stroke(stroke) => { + self.scene.set_stroke(stroke.clone()); + self.scene + .glyph_run(font) + .font_size(font_size) + .hint(hint) + .normalized_coords(normalized_coords) + .glyph_transform(glyph_transform.unwrap_or_default()) + .stroke_glyphs(glyphs.map(into_vello_hybrid_glyph)); + } + } + } + fn draw_box_shadow( + &mut self, + _transform: Affine, + _rect: Rect, + _color: Color, + _radius: f64, + _std_dev: f64, + ) { + // FIXME: implement once supported in vello_hybrid + // + // self.scene.set_transform(transform); + // self.scene.set_paint(PaintType::Solid(color)); + // self.scene + // .fill_blurred_rounded_rect(&rect, radius as f32, std_dev as f32); + } +} diff --git a/crates/anyrender_vello_hybrid/src/window_renderer.rs b/crates/anyrender_vello_hybrid/src/window_renderer.rs new file mode 100644 index 0000000..4455f72 --- /dev/null +++ b/crates/anyrender_vello_hybrid/src/window_renderer.rs @@ -0,0 +1,262 @@ +use anyrender::{WindowHandle, WindowRenderer}; +use debug_timer::debug_timer; +use rustc_hash::FxHashMap; +use std::sync::{ + Arc, + // atomic::{AtomicU64}, +}; +use vello_common::paint::ImageId; +use vello_hybrid::{ + RenderSettings, RenderSize, RenderTargetConfig, Renderer as VelloHybridRenderer, + Scene as VelloHybridScene, +}; +use wgpu::{ + CommandEncoderDescriptor, Features, Limits, PresentMode, TextureFormat, TextureViewDescriptor, +}; +use wgpu_context::{DeviceHandle, SurfaceRenderer, SurfaceRendererConfiguration, WGPUContext}; + +use crate::{VelloHybridScenePainter, scene::ImageManager}; +// use crate::CustomPaintSource; + +// static PAINT_SOURCE_ID: AtomicU64 = AtomicU64::new(0); + +// Simple struct to hold the state of the renderer +struct ActiveRenderState { + renderer: VelloHybridRenderer, + render_surface: SurfaceRenderer<'static>, +} + +#[allow(clippy::large_enum_variant)] +enum RenderState { + Active(ActiveRenderState), + Suspended, +} + +impl RenderState { + fn current_device_handle(&self) -> Option<&DeviceHandle> { + let RenderState::Active(state) = self else { + return None; + }; + Some(&state.render_surface.device_handle) + } +} + +#[derive(Clone, Default)] +pub struct VelloHybridRendererOptions { + pub features: Option, + pub limits: Option, + pub render_settings: RenderSettings, +} + +pub struct VelloHybridWindowRenderer { + // The fields MUST be in this order, so that the surface is dropped before the window + // Window is cached even when suspended so that it can be reused when the app is resumed after being suspended + render_state: RenderState, + window_handle: Option>, + + // Vello + wgpu_context: WGPUContext, + scene: VelloHybridScene, + config: VelloHybridRendererOptions, + // custom_paint_sources: FxHashMap>, + cached_images: FxHashMap, +} +impl VelloHybridWindowRenderer { + #[allow(clippy::new_without_default)] + pub fn new() -> Self { + Self::with_options(VelloHybridRendererOptions::default()) + } + + pub fn with_options(config: VelloHybridRendererOptions) -> Self { + let features = config.features.unwrap_or_default() + | Features::CLEAR_TEXTURE + | Features::PIPELINE_CACHE; + let render_settings = config.render_settings; + Self { + wgpu_context: WGPUContext::with_features_and_limits( + Some(features), + config.limits.clone(), + ), + config, + render_state: RenderState::Suspended, + window_handle: None, + scene: VelloHybridScene::new_with(0, 0, render_settings), + // custom_paint_sources: FxHashMap::default(), + cached_images: FxHashMap::default(), + } + } + + pub fn current_device_handle(&self) -> Option<&DeviceHandle> { + self.render_state.current_device_handle() + } + + // pub fn register_custom_paint_source(&mut self, mut source: Box) -> u64 { + // if let Some(device_handle) = self.render_state.current_device_handle() { + // source.resume(device_handle); + // } + // let id = PAINT_SOURCE_ID.fetch_add(1, atomic::Ordering::SeqCst); + // self.custom_paint_sources.insert(id, source); + + // id + // } + + // pub fn unregister_custom_paint_source(&mut self, id: u64) { + // if let Some(mut source) = self.custom_paint_sources.remove(&id) { + // source.suspend(); + // drop(source); + // } + // } +} + +impl WindowRenderer for VelloHybridWindowRenderer { + type ScenePainter<'a> + = VelloHybridScenePainter<'a> + where + Self: 'a; + + fn is_active(&self) -> bool { + matches!(self.render_state, RenderState::Active(_)) + } + + fn resume(&mut self, window_handle: Arc, width: u32, height: u32) { + // Create wgpu_context::SurfaceRenderer + let render_surface = pollster::block_on(self.wgpu_context.create_surface( + window_handle.clone(), + SurfaceRendererConfiguration { + usage: wgpu::TextureUsages::RENDER_ATTACHMENT, + formats: vec![ + /*TextureFormat::Rgba8Unorm, */ TextureFormat::Bgra8Unorm, + ], + width, + height, + present_mode: PresentMode::AutoVsync, + desired_maximum_frame_latency: 2, + alpha_mode: wgpu::CompositeAlphaMode::Auto, + view_formats: vec![], + }, + None, + // Some(TextureConfiguration { + // usage: TextureUsages::STORAGE_BINDING | TextureUsages::TEXTURE_BINDING, + // }), + )) + .expect("Error creating surface"); + + // Create vello::Renderer + let renderer = VelloHybridRenderer::new( + render_surface.device(), + &RenderTargetConfig { + // TODO: Make configurable? + format: TextureFormat::Bgra8Unorm, + width, + height, + }, + ); + + // Resume custom paint sources + // let device_handle = &render_surface.device_handle; + // for source in self.custom_paint_sources.values_mut() { + // source.resume(device_handle) + // } + + // Set state to Active + self.window_handle = Some(window_handle); + self.render_state = RenderState::Active(ActiveRenderState { + renderer, + render_surface, + }); + } + + fn suspend(&mut self) { + // Suspend custom paint sources + // for source in self.custom_paint_sources.values_mut() { + // source.suspend() + // } + + // Set state to Suspended + self.render_state = RenderState::Suspended; + } + + fn set_size(&mut self, width: u32, height: u32) { + if width as u16 != self.scene.width() || height as u16 != self.scene.height() { + self.scene = VelloHybridScene::new_with( + width as u16, + height as u16, + self.config.render_settings, + ); + if let RenderState::Active(state) = &mut self.render_state { + state.render_surface.resize(width, height); + }; + } + } + + fn render)>(&mut self, draw_fn: F) { + let RenderState::Active(state) = &mut self.render_state else { + return; + }; + + let render_surface = &state.render_surface; + + debug_timer!(timer, feature = "log_frame_times"); + + let mut encoder = + render_surface + .device() + .create_command_encoder(&CommandEncoderDescriptor { + label: Some("Render scene"), + }); + + let image_manager = ImageManager { + renderer: &mut state.renderer, + device: render_surface.device(), + queue: render_surface.queue(), + encoder: &mut encoder, + cache: &mut self.cached_images, + }; + + // Regenerate the vello scene + draw_fn(&mut VelloHybridScenePainter { + scene: &mut self.scene, + image_manager: Some(image_manager), + }); + timer.record_time("cmd"); + + 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"); + render_surface.queue().submit([encoder.finish()]); + timer.record_time("render"); + + drop(texture_view); + drop(surface_texture); + + render_surface.maybe_blit_and_present(); + timer.record_time("present"); + + render_surface.device().poll(wgpu::PollType::Wait).unwrap(); + + timer.record_time("wait"); + timer.print_times("vello_hybrid: "); + + // static COUNTER: AtomicU64 = AtomicU64::new(0); + // println!("FRAME {}", COUNTER.fetch_add(1, atomic::Ordering::Relaxed)); + + // Empty the Vello scene (memory optimisation) + self.scene.reset(); + } +} diff --git a/crates/wgpu_context/src/surface_renderer.rs b/crates/wgpu_context/src/surface_renderer.rs index 19ce0a2..4fb46a6 100644 --- a/crates/wgpu_context/src/surface_renderer.rs +++ b/crates/wgpu_context/src/surface_renderer.rs @@ -187,6 +187,12 @@ 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 target_texture_view(&self) -> TextureView { match &self.intermediate_texture { Some(intermediate_texture) => intermediate_texture.texture_view.clone(), diff --git a/examples/bunnymark/Cargo.toml b/examples/bunnymark/Cargo.toml index 1b703f5..5cc693f 100644 --- a/examples/bunnymark/Cargo.toml +++ b/examples/bunnymark/Cargo.toml @@ -13,5 +13,6 @@ wgpu = { workspace = true } image = { workspace = true, features = ["png"] } anyrender = { workspace = true } anyrender_vello = { workspace = true, features = ["log_frame_times"] } +anyrender_vello_hybrid = { workspace = true, features = ["log_frame_times"] } anyrender_vello_cpu = { workspace = true, features = ["pixels_window_renderer", "multithreading", "log_frame_times"] } fastrand = "2.3" diff --git a/examples/bunnymark/src/main.rs b/examples/bunnymark/src/main.rs index 4fff85f..2d80e84 100644 --- a/examples/bunnymark/src/main.rs +++ b/examples/bunnymark/src/main.rs @@ -1,6 +1,7 @@ use anyrender::{PaintScene, WindowRenderer}; use anyrender_vello::VelloWindowRenderer; use anyrender_vello_cpu::VelloCpuWindowRenderer; +use anyrender_vello_hybrid::VelloHybridWindowRenderer; use bunny::BunnyManager; use kurbo::{Affine, Circle, Point, Rect, Stroke}; use peniko::{Color, Fill}; @@ -27,6 +28,7 @@ struct App { enum Renderer { Gpu(Box), + Hybrid(Box), Cpu(Box), } @@ -34,6 +36,7 @@ impl Renderer { fn is_active(&self) -> bool { match self { Renderer::Gpu(r) => r.is_active(), + Renderer::Hybrid(r) => r.is_active(), Renderer::Cpu(r) => r.is_active(), } } @@ -41,6 +44,7 @@ impl Renderer { fn set_size(&mut self, w: u32, h: u32) { match self { Renderer::Gpu(r) => r.set_size(w, h), + Renderer::Hybrid(r) => r.set_size(w, h), Renderer::Cpu(r) => r.set_size(w, h), } } @@ -151,8 +155,8 @@ impl ApplicationHandler for App { } fn resumed(&mut self, event_loop: &ActiveEventLoop) { - self.set_backend(VelloCpuWindowRenderer::new(), event_loop, |r| { - Renderer::Cpu(Box::new(r)) + self.set_backend(VelloHybridWindowRenderer::new(), event_loop, |r| { + Renderer::Hybrid(Box::new(r)) }); } @@ -194,6 +198,7 @@ impl ApplicationHandler for App { .update(self.logical_width as f64, self.logical_height as f64); let renderer_name = match renderer { Renderer::Gpu(_) => "vello", + Renderer::Hybrid(_) => "vello_hybrid", Renderer::Cpu(_) => "vello_cpu", }; print!( @@ -212,6 +217,16 @@ impl ApplicationHandler for App { Color::from_rgb8(255, 0, 0), ); }), + Renderer::Hybrid(r) => r.render(|scene_painter| { + App::draw_scene( + scene_painter, + self.logical_width, + self.logical_height, + self.scale_factor, + &self.bunny_manager, + Color::from_rgb8(255, 0, 0), + ); + }), Renderer::Cpu(r) => r.render(|scene_painter| { App::draw_scene( scene_painter, @@ -242,6 +257,11 @@ impl ApplicationHandler for App { if logical_key == Key::Named(NamedKey::Space) { match renderer { Renderer::Cpu(_) => { + self.set_backend(VelloHybridWindowRenderer::new(), event_loop, |r| { + Renderer::Hybrid(Box::new(r)) + }); + } + Renderer::Hybrid(_) => { self.set_backend(VelloWindowRenderer::new(), event_loop, |r| { Renderer::Gpu(Box::new(r)) }); diff --git a/examples/winit/Cargo.toml b/examples/winit/Cargo.toml index f65b701..ab881c5 100644 --- a/examples/winit/Cargo.toml +++ b/examples/winit/Cargo.toml @@ -12,6 +12,7 @@ peniko = { workspace = true } wgpu = { workspace = true } anyrender = { workspace = true } anyrender_vello = { workspace = true } +anyrender_vello_hybrid = { workspace = true } anyrender_vello_cpu = { workspace = true, features = [ "pixels_window_renderer", "softbuffer_window_renderer", diff --git a/examples/winit/src/main.rs b/examples/winit/src/main.rs index db45f0b..e3a603c 100644 --- a/examples/winit/src/main.rs +++ b/examples/winit/src/main.rs @@ -1,6 +1,7 @@ use anyrender::{NullWindowRenderer, PaintScene, WindowRenderer}; use anyrender_vello::VelloWindowRenderer; use anyrender_vello_cpu::{PixelsWindowRenderer, SoftbufferWindowRenderer, VelloCpuImageRenderer}; +use anyrender_vello_hybrid::VelloHybridWindowRenderer; use kurbo::{Affine, Circle, Point, Rect, Stroke}; use peniko::{Color, Fill}; use std::sync::Arc; @@ -21,13 +22,15 @@ struct App { type VelloCpuSBWindowRenderer = SoftbufferWindowRenderer; type VelloCpuWindowRenderer = PixelsWindowRenderer; -// type InitialBackend = NullWindowRenderer; -// type InitialBackend = VelloCpuWindowRenderer; -type InitialBackend = VelloCpuSBWindowRenderer; // type InitialBackend = VelloWindowRenderer; +type InitialBackend = VelloHybridWindowRenderer; +// type InitialBackend = VelloCpuWindowRenderer; +// type InitialBackend = VelloCpuSBWindowRenderer; +// type InitialBackend = NullWindowRenderer; enum Renderer { Gpu(Box), + Hybrid(Box), Cpu(Box), CpuSoftbuffer(Box), Null(NullWindowRenderer), @@ -37,6 +40,11 @@ impl From for Renderer { Self::Gpu(Box::new(renderer)) } } +impl From for Renderer { + fn from(renderer: VelloHybridWindowRenderer) -> Self { + Self::Hybrid(Box::new(renderer)) + } +} impl From for Renderer { fn from(renderer: VelloCpuWindowRenderer) -> Self { Self::Cpu(Box::new(renderer)) @@ -57,6 +65,7 @@ impl Renderer { fn is_active(&self) -> bool { match self { Renderer::Gpu(r) => r.is_active(), + Renderer::Hybrid(r) => r.is_active(), Renderer::Cpu(r) => r.is_active(), Renderer::CpuSoftbuffer(r) => r.is_active(), Renderer::Null(r) => r.is_active(), @@ -66,6 +75,7 @@ impl Renderer { fn set_size(&mut self, w: u32, h: u32) { match self { Renderer::Gpu(r) => r.set_size(w, h), + Renderer::Hybrid(r) => r.set_size(w, h), Renderer::Cpu(r) => r.set_size(w, h), Renderer::CpuSoftbuffer(r) => r.set_size(w, h), Renderer::Null(r) => r.set_size(w, h), @@ -184,6 +194,7 @@ impl ApplicationHandler for App { } WindowEvent::RedrawRequested => match renderer { Renderer::Gpu(r) => r.render(|p| App::draw_scene(p, Color::from_rgb8(255, 0, 0))), + Renderer::Hybrid(r) => r.render(|p| App::draw_scene(p, Color::from_rgb8(0, 0, 0))), Renderer::Cpu(r) => r.render(|p| App::draw_scene(p, Color::from_rgb8(0, 255, 0))), Renderer::CpuSoftbuffer(r) => { r.render(|p| App::draw_scene(p, Color::from_rgb8(0, 0, 255))) @@ -200,6 +211,9 @@ impl ApplicationHandler for App { .. } => match renderer { Renderer::Cpu(_) | Renderer::CpuSoftbuffer(_) => { + self.set_backend(VelloHybridWindowRenderer::new(), event_loop); + } + Renderer::Hybrid(_) => { self.set_backend(VelloWindowRenderer::new(), event_loop); } Renderer::Gpu(_) => {