use uzor_render_tiny_skia::TinySkiaCpuRenderContext;
use uzor_render_vello_cpu::VelloCpuRenderContext;
use uzor_render_vello_gpu::VelloGpuRenderContext;
use uzor_render_vello_hybrid::VelloHybridRenderContext;
use uzor_render_wgpu_instanced::{InstancedRenderContext, InstancedRenderer};
#[cfg(not(target_arch = "wasm32"))]
use uzor_window_hub::lifecycle::SoftwarePresenter;
use vello::util::{RenderContext as VelloRenderContext, RenderSurface};
use vello::{Renderer as VelloRenderer, Scene};
use crate::backend::RenderBackend;
#[cfg(target_arch = "wasm32")]
use uzor_render_canvas2d::Canvas2dRenderContext;
pub type GpuDevicePool = VelloRenderContext;
pub enum SurfaceMode {
Gpu {
gpu_pool: GpuDevicePool,
surface: RenderSurface<'static>,
dev_id: usize,
},
#[cfg(not(target_arch = "wasm32"))]
Software {
presenter: Box<dyn SoftwarePresenter>,
width: u32,
height: u32,
},
#[cfg(target_arch = "wasm32")]
Canvas2d {
canvas: web_sys::HtmlCanvasElement,
},
}
pub enum BackendContext<'a> {
VelloGpu(VelloGpuRenderContext<'a>),
VelloHybrid(VelloHybridRenderContext),
Instanced(InstancedRenderContext),
VelloCpu(VelloCpuRenderContext),
TinySkia(TinySkiaCpuRenderContext),
}
impl<'a> BackendContext<'a> {
pub fn vello_gpu(scene: &'a mut Scene, offset_x: f64, offset_y: f64) -> Self {
Self::VelloGpu(VelloGpuRenderContext::new(scene, offset_x, offset_y))
}
pub fn vello_hybrid(dpr: f64) -> Self {
Self::VelloHybrid(VelloHybridRenderContext::new(dpr))
}
pub fn instanced(screen_w: f32, screen_h: f32, offset_x: f32, offset_y: f32) -> Self {
Self::Instanced(InstancedRenderContext::new(screen_w, screen_h, offset_x, offset_y))
}
pub fn vello_cpu(dpr: f64) -> Self {
Self::VelloCpu(VelloCpuRenderContext::new(dpr))
}
pub fn tiny_skia(width: u32, height: u32, dpr: f64) -> Self {
Self::TinySkia(TinySkiaCpuRenderContext::new(width, height, dpr))
}
}
pub struct WindowRenderState {
pub(crate) surface: SurfaceMode,
pub(crate) vello_gpu_renderer: Option<VelloRenderer>,
pub(crate) vello_hybrid_renderer: Option<vello_hybrid::Renderer>,
pub(crate) instanced_renderer: Option<InstancedRenderer>,
pub(crate) vello_cpu_ctx: Option<VelloCpuRenderContext>,
pub(crate) tiny_skia_ctx: Option<TinySkiaCpuRenderContext>,
#[cfg(target_arch = "wasm32")]
pub(crate) canvas2d_ctx: Option<Canvas2dRenderContext>,
pub(crate) scene: Scene,
pub(crate) vello_hybrid_ctx: VelloHybridRenderContext,
pub(crate) active: RenderBackend,
}
impl WindowRenderState {
pub fn new_gpu(
gpu_pool: GpuDevicePool,
surface: RenderSurface<'static>,
renderer: VelloRenderer,
dev_id: usize,
) -> Self {
Self {
surface: SurfaceMode::Gpu { gpu_pool, surface, dev_id },
vello_gpu_renderer: Some(renderer),
vello_hybrid_renderer: None,
instanced_renderer: None,
vello_cpu_ctx: None,
tiny_skia_ctx: None,
#[cfg(target_arch = "wasm32")]
canvas2d_ctx: None,
scene: Scene::new(),
vello_hybrid_ctx: VelloHybridRenderContext::new(1.0),
active: RenderBackend::VelloGpu,
}
}
pub fn new_gpu_no_vello(
gpu_pool: GpuDevicePool,
surface: RenderSurface<'static>,
dev_id: usize,
active: RenderBackend,
dpr: f64,
) -> Self {
Self {
surface: SurfaceMode::Gpu { gpu_pool, surface, dev_id },
vello_gpu_renderer: None,
vello_hybrid_renderer: None,
instanced_renderer: None,
vello_cpu_ctx: None,
tiny_skia_ctx: None,
#[cfg(target_arch = "wasm32")]
canvas2d_ctx: None,
scene: Scene::new(),
vello_hybrid_ctx: VelloHybridRenderContext::new(dpr),
active,
}
}
#[cfg(not(target_arch = "wasm32"))]
pub fn new_cpu(width: u32, height: u32, presenter: Box<dyn SoftwarePresenter>) -> Self {
Self {
surface: SurfaceMode::Software { presenter, width, height },
vello_gpu_renderer: None,
vello_hybrid_renderer: None,
instanced_renderer: None,
vello_cpu_ctx: None,
tiny_skia_ctx: Some(TinySkiaCpuRenderContext::new(width, height, 1.0)),
scene: Scene::new(),
vello_hybrid_ctx: VelloHybridRenderContext::new(1.0),
active: RenderBackend::TinySkia,
}
}
#[cfg(not(target_arch = "wasm32"))]
pub fn new_vello_cpu(dpr: f64, presenter: Box<dyn SoftwarePresenter>) -> Self {
Self {
surface: SurfaceMode::Software { presenter, width: 0, height: 0 },
vello_gpu_renderer: None,
vello_hybrid_renderer: None,
instanced_renderer: None,
vello_cpu_ctx: Some(VelloCpuRenderContext::new(dpr)),
tiny_skia_ctx: None,
scene: Scene::new(),
vello_hybrid_ctx: VelloHybridRenderContext::new(dpr),
active: RenderBackend::VelloCpu,
}
}
pub fn new_vello_hybrid(
gpu_pool: GpuDevicePool,
surface: RenderSurface<'static>,
dev_id: usize,
dpr: f64,
) -> Self {
Self::new_gpu_no_vello(gpu_pool, surface, dev_id, RenderBackend::VelloHybrid, dpr)
}
pub fn new_wgpu_instanced(
gpu_pool: GpuDevicePool,
surface: RenderSurface<'static>,
dev_id: usize,
) -> Self {
Self::new_gpu_no_vello(gpu_pool, surface, dev_id, RenderBackend::InstancedWgpu, 1.0)
}
#[cfg(target_arch = "wasm32")]
pub fn new_canvas2d(
canvas: web_sys::HtmlCanvasElement,
ctx: Canvas2dRenderContext,
) -> Self {
Self {
surface: SurfaceMode::Canvas2d { canvas },
vello_gpu_renderer: None,
vello_hybrid_renderer: None,
instanced_renderer: None,
vello_cpu_ctx: None,
tiny_skia_ctx: None,
canvas2d_ctx: Some(ctx),
scene: Scene::new(),
vello_hybrid_ctx: VelloHybridRenderContext::new(1.0),
active: RenderBackend::Canvas2d,
}
}
pub fn backend(&self) -> RenderBackend {
self.active
}
pub fn set_active(&mut self, backend: RenderBackend) {
self.active = backend;
}
pub fn scene_mut(&mut self) -> Option<&mut Scene> {
match self.active {
RenderBackend::VelloGpu | RenderBackend::VelloHybrid => Some(&mut self.scene),
_ => None,
}
}
pub fn scene(&self) -> Option<&Scene> {
match self.active {
RenderBackend::VelloGpu | RenderBackend::VelloHybrid => Some(&self.scene),
_ => None,
}
}
pub fn cpu_ctx_mut(&mut self) -> Option<&mut TinySkiaCpuRenderContext> {
self.tiny_skia_ctx.as_mut()
}
pub fn cpu_ctx(&self) -> Option<&TinySkiaCpuRenderContext> {
self.tiny_skia_ctx.as_ref()
}
pub fn vello_cpu_ctx_mut(&mut self) -> Option<&mut VelloCpuRenderContext> {
self.vello_cpu_ctx.as_mut()
}
pub fn vello_cpu_ctx(&self) -> Option<&VelloCpuRenderContext> {
self.vello_cpu_ctx.as_ref()
}
pub fn vello_hybrid_ctx_mut(&mut self) -> Option<&mut VelloHybridRenderContext> {
if matches!(self.active, RenderBackend::VelloHybrid) {
Some(&mut self.vello_hybrid_ctx)
} else {
None
}
}
#[cfg(target_arch = "wasm32")]
pub fn canvas2d_ctx_mut(&mut self) -> Option<&mut Canvas2dRenderContext> {
if matches!(self.active, RenderBackend::Canvas2d) {
self.canvas2d_ctx.as_mut()
} else {
None
}
}
#[cfg(target_arch = "wasm32")]
pub fn canvas2d_ctx(&self) -> Option<&Canvas2dRenderContext> {
if matches!(self.active, RenderBackend::Canvas2d) {
self.canvas2d_ctx.as_ref()
} else {
None
}
}
pub fn with_render_context<R>(
&mut self,
f: impl FnOnce(&mut dyn uzor::render::RenderContext) -> R,
) -> Option<R> {
match self.active {
RenderBackend::VelloGpu | RenderBackend::VelloHybrid => {
let mut ctx = VelloGpuRenderContext::new(&mut self.scene, 0.0, 0.0);
Some(f(&mut ctx))
}
RenderBackend::VelloCpu => {
self.vello_cpu_ctx.as_mut().map(|c| f(c))
}
RenderBackend::TinySkia => {
self.tiny_skia_ctx.as_mut().map(|c| f(c))
}
RenderBackend::InstancedWgpu => None,
#[cfg(target_arch = "wasm32")]
RenderBackend::Canvas2d => {
self.canvas2d_ctx.as_mut().map(|c| f(c))
}
#[cfg(not(target_arch = "wasm32"))]
RenderBackend::Canvas2d => None,
}
}
pub fn resize_surface(&mut self, width: u32, height: u32) {
if width == 0 || height == 0 {
return;
}
match &mut self.surface {
SurfaceMode::Gpu { gpu_pool, surface, .. } => {
gpu_pool.resize_surface(surface, width, height);
}
#[cfg(not(target_arch = "wasm32"))]
SurfaceMode::Software { presenter, width: w, height: h } => {
presenter.resize(width, height);
*w = width;
*h = height;
}
#[cfg(target_arch = "wasm32")]
SurfaceMode::Canvas2d { .. } => {
}
}
}
pub fn begin_frame(&mut self) {
match self.active {
RenderBackend::VelloGpu => self.scene.reset(),
RenderBackend::VelloHybrid => {
}
RenderBackend::VelloCpu
| RenderBackend::TinySkia
| RenderBackend::InstancedWgpu => {
}
RenderBackend::Canvas2d => {
}
}
}
}