| mod context; |
|
|
| use anyhow::Result; |
| pub use context::Context; |
| use dyn_any::StaticType; |
| use glam::UVec2; |
| use graphene_application_io::{ApplicationIo, EditorApi, SurfaceHandle}; |
| use graphene_core::{Color, Ctx}; |
| pub use graphene_svg_renderer::RenderContext; |
| use std::sync::Arc; |
| use vello::{AaConfig, AaSupport, RenderParams, Renderer, RendererOptions, Scene}; |
| use wgpu::{Origin3d, SurfaceConfiguration, TextureAspect}; |
|
|
| #[derive(dyn_any::DynAny)] |
| pub struct WgpuExecutor { |
| pub context: Context, |
| vello_renderer: futures::lock::Mutex<Renderer>, |
| } |
|
|
| impl std::fmt::Debug for WgpuExecutor { |
| fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { |
| f.debug_struct("WgpuExecutor").field("context", &self.context).finish() |
| } |
| } |
|
|
| impl<'a, T: ApplicationIo<Executor = WgpuExecutor>> From<&'a EditorApi<T>> for &'a WgpuExecutor { |
| fn from(editor_api: &'a EditorApi<T>) -> Self { |
| editor_api.application_io.as_ref().unwrap().gpu_executor().unwrap() |
| } |
| } |
|
|
| pub type WgpuSurface = Arc<SurfaceHandle<Surface>>; |
| pub type WgpuWindow = Arc<SurfaceHandle<WindowHandle>>; |
|
|
| impl graphene_application_io::Size for Surface { |
| fn size(&self) -> UVec2 { |
| self.resolution |
| } |
| } |
|
|
| pub struct Surface { |
| pub inner: wgpu::Surface<'static>, |
| resolution: UVec2, |
| } |
| #[cfg(target_arch = "wasm32")] |
| pub type Window = web_sys::HtmlCanvasElement; |
| #[cfg(not(target_arch = "wasm32"))] |
| pub type Window = Arc<winit::window::Window>; |
|
|
| unsafe impl StaticType for Surface { |
| type Static = Surface; |
| } |
|
|
| impl WgpuExecutor { |
| pub async fn render_vello_scene(&self, scene: &Scene, surface: &WgpuSurface, width: u32, height: u32, context: &RenderContext, background: Color) -> Result<()> { |
| let surface = &surface.surface.inner; |
| let surface_caps = surface.get_capabilities(&self.context.adapter); |
| surface.configure( |
| &self.context.device, |
| &SurfaceConfiguration { |
| usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::STORAGE_BINDING, |
| format: wgpu::TextureFormat::Rgba8Unorm, |
| width, |
| height, |
| present_mode: surface_caps.present_modes[0], |
| alpha_mode: wgpu::CompositeAlphaMode::Opaque, |
| view_formats: vec![], |
| desired_maximum_frame_latency: 2, |
| }, |
| ); |
| let surface_texture = surface.get_current_texture()?; |
|
|
| let [r, g, b, _] = background.to_rgba8_srgb(); |
| let render_params = RenderParams { |
| |
| |
| base_color: vello::peniko::Color::from_rgba8(r, g, b, 0xff), |
| width, |
| height, |
| antialiasing_method: AaConfig::Msaa16, |
| }; |
|
|
| { |
| let mut renderer = self.vello_renderer.lock().await; |
| for (id, texture) in context.resource_overrides.iter() { |
| let texture_view = wgpu::ImageCopyTextureBase { |
| texture: texture.clone(), |
| mip_level: 0, |
| origin: Origin3d::ZERO, |
| aspect: TextureAspect::All, |
| }; |
| renderer.override_image( |
| &vello::peniko::Image::new(vello::peniko::Blob::from_raw_parts(Arc::new(vec![]), *id), vello::peniko::Format::Rgba8, 0, 0), |
| Some(texture_view), |
| ); |
| } |
| renderer.render_to_surface(&self.context.device, &self.context.queue, scene, &surface_texture, &render_params).unwrap(); |
| } |
|
|
| surface_texture.present(); |
|
|
| Ok(()) |
| } |
|
|
| #[cfg(target_arch = "wasm32")] |
| pub fn create_surface(&self, canvas: graphene_application_io::WasmSurfaceHandle) -> Result<SurfaceHandle<Surface>> { |
| let surface = self.context.instance.create_surface(wgpu::SurfaceTarget::Canvas(canvas.surface))?; |
|
|
| Ok(SurfaceHandle { |
| window_id: canvas.window_id, |
| surface: Surface { |
| inner: surface, |
| resolution: UVec2::ZERO, |
| }, |
| }) |
| } |
| #[cfg(not(target_arch = "wasm32"))] |
| pub fn create_surface(&self, window: SurfaceHandle<Window>) -> Result<SurfaceHandle<Surface>> { |
| let size = window.surface.inner_size(); |
| let resolution = UVec2::new(size.width, size.height); |
| let surface = self.context.instance.create_surface(wgpu::SurfaceTarget::Window(Box::new(window.surface)))?; |
|
|
| Ok(SurfaceHandle { |
| window_id: window.window_id, |
| surface: Surface { inner: surface, resolution }, |
| }) |
| } |
| } |
|
|
| impl WgpuExecutor { |
| pub async fn new() -> Option<Self> { |
| let context = Context::new().await?; |
|
|
| let vello_renderer = Renderer::new( |
| &context.device, |
| RendererOptions { |
| surface_format: Some(wgpu::TextureFormat::Rgba8Unorm), |
| use_cpu: false, |
| antialiasing_support: AaSupport::all(), |
| num_init_threads: std::num::NonZeroUsize::new(1), |
| }, |
| ) |
| .map_err(|e| anyhow::anyhow!("Failed to create Vello renderer: {:?}", e)) |
| .ok()?; |
|
|
| Some(Self { |
| context, |
| vello_renderer: vello_renderer.into(), |
| }) |
| } |
| } |
|
|
| pub type WindowHandle = Arc<SurfaceHandle<Window>>; |
|
|
| #[node_macro::node(skip_impl)] |
| fn create_gpu_surface<'a: 'n, Io: ApplicationIo<Executor = WgpuExecutor, Surface = Window> + 'a + Send + Sync>(_: impl Ctx + 'a, editor_api: &'a EditorApi<Io>) -> Option<WgpuSurface> { |
| let canvas = editor_api.application_io.as_ref()?.window()?; |
| let executor = editor_api.application_io.as_ref()?.gpu_executor()?; |
| Some(Arc::new(executor.create_surface(canvas).ok()?)) |
| } |
|
|