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::layout::window::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) instanced_ctx: Option<InstancedRenderContext>,
pub(crate) vello_cpu_ctx: Option<VelloCpuRenderContext>,
pub(crate) tiny_skia_ctx: Option<TinySkiaCpuRenderContext>,
pub(crate) urx_ctx: Option<uzor_render_urx::UrxRenderContext>,
pub(crate) urx_cpu_backend: Option<uzor_urx_cpu::CpuBackend>,
pub(crate) urx_cpu_pixmap: Option<uzor_urx_cpu::Pixmap>,
#[allow(dead_code)]
pub(crate) urx_wgpu_backend: Option<uzor_urx_wgpu::UrxWgpuBackend>,
pub(crate) urx_hybrid_backend: Option<uzor_urx_hybrid::HybridBackend>,
pub(crate) urx_wgpu_full_backend: Option<uzor_urx_wgpu_full::WgpuFullBackend>,
pub(crate) urx_engine: Option<uzor_urx_engine::UrxEngine>,
pub(crate) urx_renderer_3d: Option<uzor_urx_3d::Renderer3D>,
pub(crate) urx_scene_3d: Option<uzor_urx_3d::Scene3D>,
pub(crate) urx_physics: Option<uzor_urx_physics::PhysicsWorld>,
pub(crate) urx_particles: Option<uzor_urx_3d::ParticleSystem>,
#[cfg(target_arch = "wasm32")]
pub(crate) canvas2d_ctx: Option<Canvas2dRenderContext>,
pub(crate) scene: Scene,
pub(crate) vello_hybrid_ctx: VelloHybridRenderContext,
pub(crate) active: RenderBackend,
pub(crate) active_urx: Option<uzor::UrxBackend>,
}
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,
instanced_ctx: None,
vello_cpu_ctx: None,
tiny_skia_ctx: None,
urx_ctx: None,
urx_cpu_backend: None,
urx_cpu_pixmap: None,
urx_wgpu_backend: None,
urx_hybrid_backend: None,
urx_wgpu_full_backend: None,
urx_engine: None,
urx_renderer_3d: None,
urx_scene_3d: None,
urx_physics: None,
urx_particles: None,
active_urx: 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_skeleton(
gpu_pool: GpuDevicePool,
surface: RenderSurface<'static>,
dev_id: usize,
) -> Self {
Self {
surface: SurfaceMode::Gpu { gpu_pool, surface, dev_id },
vello_gpu_renderer: None,
vello_hybrid_renderer: None,
instanced_renderer: None,
instanced_ctx: None,
vello_cpu_ctx: None,
tiny_skia_ctx: None,
urx_ctx: None,
urx_cpu_backend: None,
urx_cpu_pixmap: None,
urx_wgpu_backend: None,
urx_hybrid_backend: None,
urx_wgpu_full_backend: None,
urx_engine: None,
urx_renderer_3d: None,
urx_scene_3d: None,
urx_physics: None,
urx_particles: None,
active_urx: None,
#[cfg(target_arch = "wasm32")]
canvas2d_ctx: None,
scene: Scene::new(),
vello_hybrid_ctx: VelloHybridRenderContext::new(1.0),
active: RenderBackend::VelloGpu,
}
}
pub fn attach_vello_renderer(&mut self, renderer: VelloRenderer) {
self.vello_gpu_renderer = Some(renderer);
}
pub fn surface_mut_for_skeleton(&mut self) -> &mut SurfaceMode {
&mut self.surface
}
pub fn has_vello_gpu_renderer(&self) -> bool {
self.vello_gpu_renderer.is_some()
}
pub fn surface_config_size(&self) -> (u32, u32) {
match &self.surface {
SurfaceMode::Gpu { surface, .. } => (surface.config.width, surface.config.height),
SurfaceMode::Software { width, height, .. } => (*width, *height),
}
}
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,
instanced_ctx: None,
vello_cpu_ctx: None,
tiny_skia_ctx: None,
urx_ctx: None,
urx_cpu_backend: None,
urx_cpu_pixmap: None,
urx_wgpu_backend: None,
urx_hybrid_backend: None,
urx_wgpu_full_backend: None,
urx_engine: None,
urx_renderer_3d: None,
urx_scene_3d: None,
urx_physics: None,
urx_particles: None,
active_urx: 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,
instanced_ctx: None,
vello_cpu_ctx: None,
tiny_skia_ctx: Some(TinySkiaCpuRenderContext::new(width, height, 1.0)),
urx_ctx: None,
urx_cpu_backend: None,
urx_cpu_pixmap: None,
urx_wgpu_backend: None,
urx_hybrid_backend: None,
urx_wgpu_full_backend: None,
urx_engine: None,
urx_renderer_3d: None,
urx_scene_3d: None,
urx_physics: None,
urx_particles: None,
active_urx: None,
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,
instanced_ctx: None,
vello_cpu_ctx: Some(VelloCpuRenderContext::new(dpr)),
tiny_skia_ctx: None,
urx_ctx: None,
urx_cpu_backend: None,
urx_cpu_pixmap: None,
urx_wgpu_backend: None,
urx_hybrid_backend: None,
urx_wgpu_full_backend: None,
urx_engine: None,
urx_renderer_3d: None,
urx_scene_3d: None,
urx_physics: None,
urx_particles: None,
active_urx: None,
scene: Scene::new(),
vello_hybrid_ctx: VelloHybridRenderContext::new(dpr),
active: RenderBackend::VelloCpu,
}
}
}
#[cfg(not(target_arch = "wasm32"))]
fn recreate_target_with_cpu_usage(
surface: &mut RenderSurface<'static>,
device: &wgpu::Device,
width: u32,
height: u32,
) {
let size = wgpu::Extent3d {
width: width.max(1),
height: height.max(1),
depth_or_array_layers: 1,
};
let new_texture = device.create_texture(&wgpu::TextureDescriptor {
label: Some("target_texture_cpu_swapchain"),
size,
mip_level_count: 1,
sample_count: 1,
dimension: wgpu::TextureDimension::D2,
format: wgpu::TextureFormat::Rgba8Unorm,
usage: wgpu::TextureUsages::STORAGE_BINDING
| wgpu::TextureUsages::TEXTURE_BINDING
| wgpu::TextureUsages::COPY_SRC
| wgpu::TextureUsages::COPY_DST
| wgpu::TextureUsages::RENDER_ATTACHMENT,
view_formats: &[],
});
let new_view = new_texture.create_view(&wgpu::TextureViewDescriptor::default());
surface.target_texture = new_texture;
surface.target_view = new_view;
}
impl WindowRenderState {
pub fn new_tiny_skia_gpu(
gpu_pool: GpuDevicePool,
mut surface: RenderSurface<'static>,
dev_id: usize,
) -> Self {
let (w, h) = (surface.config.width.max(1), surface.config.height.max(1));
recreate_target_with_cpu_usage(&mut surface, &gpu_pool.devices[dev_id].device, w, h);
Self {
surface: SurfaceMode::Gpu { gpu_pool, surface, dev_id },
vello_gpu_renderer: None,
vello_hybrid_renderer: None,
instanced_renderer: None,
instanced_ctx: None,
vello_cpu_ctx: None,
tiny_skia_ctx: Some(TinySkiaCpuRenderContext::new(w, h, 1.0)),
urx_ctx: None,
urx_cpu_backend: None,
urx_cpu_pixmap: None,
urx_wgpu_backend: None,
urx_hybrid_backend: None,
urx_wgpu_full_backend: None,
urx_engine: None,
urx_renderer_3d: None,
urx_scene_3d: None,
urx_physics: None,
urx_particles: None,
active_urx: None,
#[cfg(target_arch = "wasm32")]
canvas2d_ctx: None,
scene: Scene::new(),
vello_hybrid_ctx: VelloHybridRenderContext::new(1.0),
active: RenderBackend::TinySkia,
}
}
pub fn new_vello_cpu_gpu(
gpu_pool: GpuDevicePool,
mut surface: RenderSurface<'static>,
dev_id: usize,
dpr: f64,
) -> Self {
let (cw, ch) = (surface.config.width.max(1), surface.config.height.max(1));
recreate_target_with_cpu_usage(&mut surface, &gpu_pool.devices[dev_id].device, cw, ch);
Self {
surface: SurfaceMode::Gpu { gpu_pool, surface, dev_id },
vello_gpu_renderer: None,
vello_hybrid_renderer: None,
instanced_renderer: None,
instanced_ctx: None,
vello_cpu_ctx: Some(VelloCpuRenderContext::new(dpr)),
tiny_skia_ctx: None,
urx_ctx: None,
urx_cpu_backend: None,
urx_cpu_pixmap: None,
urx_wgpu_backend: None,
urx_hybrid_backend: None,
urx_wgpu_full_backend: None,
urx_engine: None,
urx_renderer_3d: None,
urx_scene_3d: None,
urx_physics: None,
urx_particles: None,
active_urx: None,
#[cfg(target_arch = "wasm32")]
canvas2d_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,
instanced_ctx: None,
vello_cpu_ctx: None,
tiny_skia_ctx: None,
urx_ctx: None,
urx_cpu_backend: None,
urx_cpu_pixmap: None,
urx_wgpu_backend: None,
urx_hybrid_backend: None,
urx_wgpu_full_backend: None,
urx_engine: None,
urx_renderer_3d: None,
urx_scene_3d: None,
urx_physics: None,
urx_particles: None,
active_urx: 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
}
#[cfg(not(target_arch = "wasm32"))]
pub fn gpu_handles(&self) -> Option<(&wgpu::Device, &wgpu::Queue, &vello::util::RenderSurface<'static>)> {
match &self.surface {
SurfaceMode::Gpu { gpu_pool, surface, dev_id } => {
let dh = gpu_pool.devices.get(*dev_id)?;
Some((&dh.device, &dh.queue, surface))
}
#[cfg(not(target_arch = "wasm32"))]
SurfaceMode::Software { .. } => None,
#[cfg(target_arch = "wasm32")]
SurfaceMode::Canvas2d { .. } => None,
}
}
#[cfg(not(target_arch = "wasm32"))]
pub fn gpu_handles_mut(
&mut self,
) -> Option<(&wgpu::Device, &wgpu::Queue, &mut vello::util::RenderSurface<'static>)> {
match &mut self.surface {
SurfaceMode::Gpu { gpu_pool, surface, dev_id } => {
let dh = gpu_pool.devices.get(*dev_id)?;
Some((&dh.device, &dh.queue, surface))
}
#[cfg(not(target_arch = "wasm32"))]
SurfaceMode::Software { .. } => None,
#[cfg(target_arch = "wasm32")]
SurfaceMode::Canvas2d { .. } => None,
}
}
pub fn set_active(&mut self, backend: RenderBackend) {
self.active = backend;
self.ensure_backend_slot(backend);
}
#[cfg(not(target_arch = "wasm32"))]
pub fn ensure_backend_slot(&mut self, backend: RenderBackend) {
match backend {
RenderBackend::VelloGpu => {
if self.vello_gpu_renderer.is_none() {
if let Some((device, _, _)) = self.gpu_handles() {
match vello::Renderer::new(
device,
vello::RendererOptions {
use_cpu: false,
antialiasing_support: vello::AaSupport::all(),
num_init_threads: std::num::NonZeroUsize::new(1),
pipeline_cache: None,
},
) {
Ok(r) => self.vello_gpu_renderer = Some(r),
Err(e) => eprintln!("[render-hub] VelloGpu renderer init failed: {e}"),
}
}
}
}
RenderBackend::VelloCpu => {
if self.vello_cpu_ctx.is_none() {
self.vello_cpu_ctx = Some(VelloCpuRenderContext::new(1.0));
}
}
RenderBackend::TinySkia => {
if self.tiny_skia_ctx.is_none() {
let (w, h) = self.gpu_handles()
.map(|(_, _, s)| (s.config.width.max(1), s.config.height.max(1)))
.unwrap_or((1, 1));
self.tiny_skia_ctx = Some(TinySkiaCpuRenderContext::new(w, h, 1.0));
}
}
RenderBackend::VelloHybrid | RenderBackend::InstancedWgpu => {}
#[cfg(target_arch = "wasm32")]
RenderBackend::Canvas2d => {}
#[cfg(not(target_arch = "wasm32"))]
RenderBackend::Canvas2d => {}
RenderBackend::UrxCpu
| RenderBackend::UrxWgpu
| RenderBackend::UrxHybrid
| RenderBackend::UrxWgpuFull => {
if self.urx_ctx.is_none() {
self.urx_ctx = Some(uzor_render_urx::UrxRenderContext::new(1.0));
}
}
}
}
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_scene_render_context<R>(
&mut self,
scene: &mut Scene,
f: impl FnOnce(&mut dyn uzor::render::RenderContext) -> R,
) -> Option<R> {
match self.active {
RenderBackend::VelloGpu => {
let mut ctx = VelloGpuRenderContext::new(scene, 0.0, 0.0);
Some(f(&mut ctx))
}
RenderBackend::VelloHybrid => {
None
}
_ => None,
}
}
pub fn append_region_scene(&mut self, region_scene: &Scene) {
if matches!(self.active, RenderBackend::VelloGpu | RenderBackend::VelloHybrid) {
self.scene.append(region_scene, 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 => {
let mut ctx = VelloGpuRenderContext::new(&mut self.scene, 0.0, 0.0);
Some(f(&mut ctx))
}
RenderBackend::VelloHybrid => {
Some(f(&mut self.vello_hybrid_ctx))
}
RenderBackend::VelloCpu => {
let (w, h) = self.gpu_handles()
.map(|(_, _, s)| (s.config.width.max(1), s.config.height.max(1)))
.unwrap_or((1, 1));
self.vello_cpu_ctx.as_mut().map(|c| {
c.begin_frame(w, h);
f(c)
})
}
RenderBackend::TinySkia => {
let (w, h) = self.gpu_handles()
.map(|(_, _, s)| (s.config.width.max(1), s.config.height.max(1)))
.unwrap_or((1, 1));
self.tiny_skia_ctx.as_mut().map(|c| {
if c.width() != w || c.height() != h {
c.resize(w, h);
}
f(c)
})
}
RenderBackend::InstancedWgpu => {
let (w, h) = self.gpu_handles()
.map(|(_, _, s)| (s.config.width.max(1), s.config.height.max(1)))
.unwrap_or((1, 1));
if self.instanced_ctx.is_none() {
self.instanced_ctx = Some(InstancedRenderContext::new(
w as f32, h as f32, 0.0, 0.0,
));
}
self.instanced_ctx.as_mut().map(|c| {
c.clear();
let _ = (w, h); f(c)
})
}
#[cfg(target_arch = "wasm32")]
RenderBackend::Canvas2d => {
self.canvas2d_ctx.as_mut().map(|c| f(c))
}
#[cfg(not(target_arch = "wasm32"))]
RenderBackend::Canvas2d => None,
RenderBackend::UrxCpu
| RenderBackend::UrxWgpu
| RenderBackend::UrxHybrid
| RenderBackend::UrxWgpuFull => {
let (w, h) = self.gpu_handles()
.map(|(_, _, s)| (s.config.width.max(1), s.config.height.max(1)))
.unwrap_or_else(|| match &self.surface {
#[cfg(not(target_arch = "wasm32"))]
SurfaceMode::Software { width, height, .. } => (*width, *height),
_ => (1, 1),
});
if self.urx_ctx.is_none() {
self.urx_ctx = Some(uzor_render_urx::UrxRenderContext::new(1.0));
}
self.urx_ctx.as_mut().map(|c| {
c.begin_frame(w, h);
f(c)
})
}
}
}
pub fn wait_gpu_idle(&self) {
if let SurfaceMode::Gpu { gpu_pool, dev_id, .. } = &self.surface {
let device = &gpu_pool.devices[*dev_id].device;
let _ = device.poll(wgpu::PollType::Wait { submission_index: None, timeout: 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, dev_id } => {
gpu_pool.resize_surface(surface, width, height);
if matches!(self.active, RenderBackend::VelloHybrid) {
self.vello_hybrid_renderer = None;
}
let device = &gpu_pool.devices[*dev_id].device;
recreate_target_with_cpu_usage(surface, device, width, height);
}
#[cfg(not(target_arch = "wasm32"))]
SurfaceMode::Software { presenter, width: w, height: h } => {
presenter.resize(width, height);
*w = width;
*h = height;
if let Some(ref mut ts) = self.tiny_skia_ctx {
ts.resize(width, height);
}
}
#[cfg(target_arch = "wasm32")]
SurfaceMode::Canvas2d { .. } => {
}
}
}
pub fn begin_frame(&mut self) {
match self.active {
RenderBackend::VelloGpu => self.scene.reset(),
RenderBackend::VelloHybrid => {
if let SurfaceMode::Gpu { ref surface, .. } = self.surface {
self.vello_hybrid_ctx
.begin_frame(surface.config.width, surface.config.height);
}
}
RenderBackend::VelloCpu
| RenderBackend::TinySkia
| RenderBackend::InstancedWgpu => {
}
RenderBackend::Canvas2d => {
}
RenderBackend::UrxCpu
| RenderBackend::UrxWgpu
| RenderBackend::UrxHybrid
| RenderBackend::UrxWgpuFull => {
}
}
}
pub fn set_active_urx(&mut self, backend: Option<uzor::UrxBackend>) {
self.active_urx = backend;
}
pub fn active_urx(&self) -> Option<uzor::UrxBackend> {
self.active_urx
}
pub fn with_urx_engine<R>(
&mut self,
f: impl FnOnce(&mut crate::urx_engine_handle::UrxEngineHandle<'_>) -> R,
) -> Option<R> {
let backend = self.active_urx?;
let (width, height) = match &self.surface {
SurfaceMode::Gpu { surface, .. } => (surface.config.width, surface.config.height),
#[cfg(not(target_arch = "wasm32"))]
SurfaceMode::Software { width, height, .. } => (*width, *height),
#[cfg(target_arch = "wasm32")]
SurfaceMode::Canvas2d { .. } => (0, 0),
};
if width == 0 || height == 0 {
return None;
}
let dpr = 1.0_f64; if self.urx_ctx.is_none() {
self.urx_ctx = Some(uzor_render_urx::UrxRenderContext::new(dpr));
}
let ctx = self.urx_ctx.as_mut()?;
ctx.begin_frame(width, height);
let gpu_available = matches!(self.surface, SurfaceMode::Gpu { .. });
let resolve_backend = |b: uzor::UrxBackend| -> uzor_urx_engine::Backend {
match b {
uzor::UrxBackend::Cpu => uzor_urx_engine::Backend::Cpu,
uzor::UrxBackend::Wgpu => uzor_urx_engine::Backend::Wgpu,
uzor::UrxBackend::Hybrid => uzor_urx_engine::Backend::Hybrid,
uzor::UrxBackend::WgpuFull => uzor_urx_engine::Backend::FullGpu,
uzor::UrxBackend::Auto => {
let hint = uzor_urx_engine::WorkloadHint {
region_count: 1, total_pixels: (width as u64) * (height as u64),
high_hz: false,
retained: true, gpu_available,
unified_memory: false,
heavy_compute: false,
};
uzor_urx_engine::Backend::auto(hint)
}
}
};
if self.urx_engine.is_none() {
let core_backend = resolve_backend(backend);
self.urx_engine = Some(uzor_urx_engine::UrxEngine::new(core_backend, width, height));
}
if let Some(eng) = self.urx_engine.as_mut() {
let (ew, eh) = (eng.width(), eng.height());
if ew != width || eh != height {
let core_backend = resolve_backend(backend);
*eng = uzor_urx_engine::UrxEngine::new(core_backend, width, height);
}
}
let engine = self.urx_engine.as_mut()?;
let mut handle = crate::urx_engine_handle::UrxEngineHandle {
render_ctx: ctx as &mut dyn uzor::render::RenderContext,
engine,
width,
height,
dpr,
frame_idx: 0, };
Some(f(&mut handle))
}
pub fn with_renderer_3d<R>(
&mut self,
f: impl FnOnce(&mut uzor_urx_3d::Renderer3D, &mut uzor_urx_3d::Scene3D) -> R,
) -> Option<R> {
let (width, height) = match &self.surface {
SurfaceMode::Gpu { surface, .. } => (surface.config.width, surface.config.height),
_ => return None,
};
if width == 0 || height == 0 { return None; }
let (device, queue, surface_format) = match &self.surface {
SurfaceMode::Gpu { gpu_pool, surface, dev_id } => (
gpu_pool.devices[*dev_id].device.clone(),
gpu_pool.devices[*dev_id].queue.clone(),
surface.config.format,
),
_ => return None,
};
if self.urx_renderer_3d.is_none() {
self.urx_renderer_3d = Some(uzor_urx_3d::Renderer3D::new(
&device,
&queue,
surface_format,
(width, height),
1024,
));
}
if self.urx_scene_3d.is_none() {
self.urx_scene_3d = Some(uzor_urx_3d::Scene3D::new());
}
let r3d = self.urx_renderer_3d.as_mut()?;
let scene_3d = self.urx_scene_3d.as_mut()?;
Some(f(r3d, scene_3d))
}
pub fn paint_skeleton(&mut self, spec: uzor_urx_core::SkeletonSpec, now_us: u64) -> bool {
let (width, height) = match &self.surface {
SurfaceMode::Gpu { surface, .. } => (surface.config.width, surface.config.height),
#[cfg(not(target_arch = "wasm32"))]
SurfaceMode::Software { width, height, .. } => (*width, *height),
#[cfg(target_arch = "wasm32")]
SurfaceMode::Canvas2d { .. } => return false,
};
if width == 0 || height == 0 { return false; }
let mut frame = uzor_urx_core::SkeletonFrame::new(width, height, spec);
frame.render(now_us);
match &mut self.surface {
SurfaceMode::Gpu { gpu_pool, surface, dev_id } => {
let device = &gpu_pool.devices[*dev_id].device;
let queue = &gpu_pool.devices[*dev_id].queue;
queue.write_texture(
wgpu::TexelCopyTextureInfo {
texture: &surface.target_texture,
mip_level: 0,
origin: wgpu::Origin3d::ZERO,
aspect: wgpu::TextureAspect::All,
},
&frame.pixels,
wgpu::TexelCopyBufferLayout {
offset: 0,
bytes_per_row: Some(4 * width),
rows_per_image: Some(height),
},
wgpu::Extent3d { width, height, depth_or_array_layers: 1 },
);
let _ = crate::submit::blit_and_present_urx(surface, device, queue);
true
}
#[cfg(not(target_arch = "wasm32"))]
SurfaceMode::Software { presenter, .. } => {
presenter.present(&frame.pixels, width, height);
true
}
#[cfg(target_arch = "wasm32")]
SurfaceMode::Canvas2d { .. } => false,
}
}
pub fn with_physics_world<R>(
&mut self,
f: impl FnOnce(&mut uzor_urx_physics::PhysicsWorld) -> R,
) -> R {
if self.urx_physics.is_none() {
self.urx_physics = Some(uzor_urx_physics::PhysicsWorld::new());
}
let physics = self.urx_physics.as_mut().expect("just inited");
f(physics)
}
pub fn init_particles(&mut self, config: uzor_urx_3d::EmitterConfig) {
self.urx_particles = Some(uzor_urx_3d::ParticleSystem::new(config));
}
pub fn with_particles<R>(
&mut self,
f: impl FnOnce(&mut uzor_urx_3d::ParticleSystem) -> R,
) -> Option<R> {
self.urx_particles.as_mut().map(f)
}
}