use uzor_urx_core::Scene;
use crate::factory::{SurfaceMode, WindowRenderState};
use crate::metrics::RenderMetrics;
use crate::submit::SubmitParams;
fn take_urx_scene(state: &mut WindowRenderState) -> Option<Scene> {
state.urx_ctx.as_mut().map(|c| c.take_scene())
}
pub fn submit_urx_cpu(state: &mut WindowRenderState, metrics: &mut RenderMetrics) -> bool {
let scene = match take_urx_scene(state) {
Some(s) => s,
None => return false,
};
enum SurfaceKind { Gpu, Software, #[cfg(target_arch = "wasm32")] Canvas2d }
let (width, height, kind) = match &state.surface {
SurfaceMode::Gpu { surface, .. } => (surface.config.width, surface.config.height, SurfaceKind::Gpu),
#[cfg(not(target_arch = "wasm32"))]
SurfaceMode::Software { width, height, .. } => (*width, *height, SurfaceKind::Software),
#[cfg(target_arch = "wasm32")]
SurfaceMode::Canvas2d { .. } => return false,
};
if width == 0 || height == 0 { return false; }
ensure_urx_cpu_resources(state, width, height);
let r2t_t0 = std::time::Instant::now();
let render_ok = {
let backend = match state.urx_cpu_backend.as_ref() { Some(b) => b, None => return false };
let pixmap = match state.urx_cpu_pixmap.as_mut() { Some(p) => p, None => return false };
pixmap.fill([0, 0, 0, 0]);
match backend.render(&scene, pixmap) {
Ok(_) => true,
Err(e) => { eprintln!("[render-hub] urx-cpu render error: {:?}", e); false }
}
};
if !render_ok { return false; }
metrics.render_to_texture_us = r2t_t0.elapsed().as_micros() as u64;
match kind {
SurfaceKind::Gpu => {
let (pix_ptr, pix_len, cw, ch) = {
let pixmap = state.urx_cpu_pixmap.as_ref().expect("pixmap inited above");
let pix = pixmap.pixels();
(pix.as_ptr(), pix.len(), pixmap.width(), pixmap.height())
};
if let SurfaceMode::Gpu { gpu_pool, surface, dev_id } = &mut state.surface {
let device = &gpu_pool.devices[*dev_id].device;
let queue = &gpu_pool.devices[*dev_id].queue;
let pix: &[u8] = unsafe { std::slice::from_raw_parts(pix_ptr, pix_len) };
if !pix.is_empty() && cw == width && ch == height {
queue.write_texture(
wgpu::TexelCopyTextureInfo {
texture: &surface.target_texture,
mip_level: 0,
origin: wgpu::Origin3d::ZERO,
aspect: wgpu::TextureAspect::All,
},
pix,
wgpu::TexelCopyBufferLayout {
offset: 0,
bytes_per_row: Some(4 * cw),
rows_per_image: Some(ch),
},
wgpu::Extent3d { width: cw, height: ch, depth_or_array_layers: 1 },
);
}
let present_t0 = std::time::Instant::now();
let lost = crate::submit::blit_and_present_urx(surface, device, queue);
metrics.present_us = present_t0.elapsed().as_micros() as u64;
return lost;
}
false
}
#[cfg(not(target_arch = "wasm32"))]
SurfaceKind::Software => {
let (pix_ptr, pix_len, cw, ch) = {
let pixmap = state.urx_cpu_pixmap.as_ref().expect("pixmap inited above");
(pixmap.pixels().as_ptr(), pixmap.pixels().len(), pixmap.width(), pixmap.height())
};
if let SurfaceMode::Software { presenter, .. } = &mut state.surface {
let pix: &[u8] = unsafe { std::slice::from_raw_parts(pix_ptr, pix_len) };
presenter.present(pix, cw, ch);
}
false
}
#[cfg(target_arch = "wasm32")]
SurfaceKind::Canvas2d => false,
}
}
fn ensure_urx_cpu_resources(state: &mut WindowRenderState, width: u32, height: u32) {
if state.urx_cpu_backend.is_none() {
state.urx_cpu_backend = Some(uzor_urx_cpu::CpuBackend::new());
}
let need_new = match &state.urx_cpu_pixmap {
None => true,
Some(p) => p.width() != width || p.height() != height,
};
if need_new {
state.urx_cpu_pixmap = Some(uzor_urx_cpu::Pixmap::new(width, height));
}
}
pub fn submit_urx_wgpu(
state: &mut WindowRenderState,
params: &SubmitParams,
metrics: &mut RenderMetrics,
) -> bool {
let scene = match take_urx_scene(state) {
Some(s) => s,
None => return false,
};
let SurfaceMode::Gpu { ref gpu_pool, ref mut surface, dev_id } = state.surface else {
eprintln!("[render-hub] urx_wgpu requires SurfaceMode::Gpu");
return false;
};
let width = surface.config.width;
let height = surface.config.height;
if width == 0 || height == 0 { return false; }
let device = &gpu_pool.devices[dev_id].device;
let queue = &gpu_pool.devices[dev_id].queue;
let surface_texture = match surface.surface.get_current_texture() {
wgpu::CurrentSurfaceTexture::Success(t) | wgpu::CurrentSurfaceTexture::Suboptimal(t) => t,
wgpu::CurrentSurfaceTexture::Timeout | wgpu::CurrentSurfaceTexture::Occluded => return false,
wgpu::CurrentSurfaceTexture::Outdated | wgpu::CurrentSurfaceTexture::Lost => {
surface.surface.configure(device, &surface.config);
return false;
}
wgpu::CurrentSurfaceTexture::Validation => return false,
};
let surface_view = surface_texture.texture.create_view(&wgpu::TextureViewDescriptor::default());
let format = surface_texture.texture.format();
if state.instanced_renderer.is_none() {
state.instanced_renderer =
Some(uzor_render_wgpu_instanced::InstancedRenderer::new(device, queue, format));
}
if state.instanced_ctx.is_none() {
state.instanced_ctx = Some(uzor_render_wgpu_instanced::InstancedRenderContext::new(
width as f32, height as f32, 0.0, 0.0,
));
}
let r2t_t0 = std::time::Instant::now();
if let Some(ctx) = state.instanced_ctx.as_mut() {
ctx.clear();
uzor_urx_wgpu::adapt_scene_into(&scene, ctx);
}
let cmds: Vec<uzor_render_wgpu_instanced::DrawCmd> = state.instanced_ctx.as_mut()
.map(|c| std::mem::take(&mut c.draw_commands))
.unwrap_or_default();
let clear = wgpu::Color {
r: params.base_color.components[0] as f64,
g: params.base_color.components[1] as f64,
b: params.base_color.components[2] as f64,
a: params.base_color.components[3] as f64,
};
if let Some(ref mut inst) = state.instanced_renderer {
inst.render(device, queue, &surface_view, width, height, &cmds, Some(clear), None);
}
metrics.render_to_texture_us = r2t_t0.elapsed().as_micros() as u64;
if let Some(ctx) = state.instanced_ctx.as_mut() {
let mut taken = cmds;
taken.clear();
ctx.draw_commands = taken;
}
let present_t0 = std::time::Instant::now();
surface_texture.present();
metrics.present_us = present_t0.elapsed().as_micros() as u64;
false
}
pub fn submit_urx_hybrid(
state: &mut WindowRenderState,
params: &SubmitParams,
metrics: &mut RenderMetrics,
) -> bool {
let scene = match take_urx_scene(state) {
Some(s) => s,
None => return false,
};
let SurfaceMode::Gpu { ref gpu_pool, ref mut surface, dev_id } = state.surface else {
eprintln!("[render-hub] urx_hybrid requires SurfaceMode::Gpu");
return false;
};
let width = surface.config.width;
let height = surface.config.height;
if width == 0 || height == 0 { return false; }
let device = &gpu_pool.devices[dev_id].device;
let queue = &gpu_pool.devices[dev_id].queue;
if state.urx_cpu_backend.is_none() {
state.urx_cpu_backend = Some(uzor_urx_cpu::CpuBackend::new());
}
if state.urx_hybrid_backend.is_none() {
state.urx_hybrid_backend = Some(uzor_urx_hybrid::HybridBackend::new());
}
let need_new_pixmap = match &state.urx_cpu_pixmap {
None => true,
Some(p) => p.width() != width || p.height() != height,
};
if need_new_pixmap {
state.urx_cpu_pixmap = Some(uzor_urx_cpu::Pixmap::new(width, height));
}
let r2t_t0 = std::time::Instant::now();
let render_ok = {
let backend = state.urx_cpu_backend.as_ref().expect("inited above");
let pixmap = state.urx_cpu_pixmap.as_mut().expect("inited above");
pixmap.fill([0, 0, 0, 0]);
backend.render(&scene, pixmap).is_ok()
};
if !render_ok { return false; }
let region_id = uzor_urx_core::region::RegionId(0);
let pixmap_clone = state.urx_cpu_pixmap.as_ref().expect("inited above").clone();
if let Some(ref mut hb) = state.urx_hybrid_backend {
hb.upsert_region_pixmap(device, queue, region_id, &pixmap_clone);
}
metrics.render_to_texture_us = r2t_t0.elapsed().as_micros() as u64;
let surface_texture = match surface.surface.get_current_texture() {
wgpu::CurrentSurfaceTexture::Success(t) | wgpu::CurrentSurfaceTexture::Suboptimal(t) => t,
wgpu::CurrentSurfaceTexture::Timeout | wgpu::CurrentSurfaceTexture::Occluded => return false,
wgpu::CurrentSurfaceTexture::Outdated | wgpu::CurrentSurfaceTexture::Lost => {
surface.surface.configure(device, &surface.config);
return false;
}
wgpu::CurrentSurfaceTexture::Validation => return false,
};
let surface_view = surface_texture.texture.create_view(&wgpu::TextureViewDescriptor::default());
let format = surface_texture.texture.format();
let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor {
label: Some("urx-hybrid-encoder"),
});
let _ = params;
let present_t0 = std::time::Instant::now();
let instances: [(uzor_urx_core::region::RegionId, uzor_urx_hybrid::QuadInstance); 1] = [
(region_id, uzor_urx_hybrid::QuadInstance::new(
0.0, 0.0, width as f32, height as f32,
)),
];
if let Some(ref mut hb) = state.urx_hybrid_backend {
hb.composite(
device, queue, &mut encoder, &surface_view, format,
width, height, &instances,
);
}
queue.submit(std::iter::once(encoder.finish()));
surface_texture.present();
metrics.present_us = present_t0.elapsed().as_micros() as u64;
false
}
pub fn submit_urx_wgpu_full(
state: &mut WindowRenderState,
params: &SubmitParams,
metrics: &mut RenderMetrics,
) -> bool {
let scene = match take_urx_scene(state) {
Some(s) => s,
None => return false,
};
let SurfaceMode::Gpu { ref gpu_pool, ref mut surface, dev_id } = state.surface else {
eprintln!("[render-hub] urx_wgpu_full requires SurfaceMode::Gpu");
return false;
};
let width = surface.config.width;
let height = surface.config.height;
if width == 0 || height == 0 { return false; }
let device = gpu_pool.devices[dev_id].device.clone();
let queue = gpu_pool.devices[dev_id].queue.clone();
if state.urx_wgpu_full_backend.is_none() {
let surface_texture_format = surface.config.format;
state.urx_wgpu_full_backend = Some(uzor_urx_wgpu_full::WgpuFullBackend::new(
device.clone(), queue.clone(), surface_texture_format,
));
}
if let Some(ref mut backend) = state.urx_wgpu_full_backend {
backend.resize(width, height);
}
let surface_texture = match surface.surface.get_current_texture() {
wgpu::CurrentSurfaceTexture::Success(t) | wgpu::CurrentSurfaceTexture::Suboptimal(t) => t,
wgpu::CurrentSurfaceTexture::Timeout | wgpu::CurrentSurfaceTexture::Occluded => return false,
wgpu::CurrentSurfaceTexture::Outdated | wgpu::CurrentSurfaceTexture::Lost => {
surface.surface.configure(&device, &surface.config);
return false;
}
wgpu::CurrentSurfaceTexture::Validation => return false,
};
let surface_view = surface_texture.texture.create_view(&wgpu::TextureViewDescriptor::default());
let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor {
label: Some("urx-wgpu-full-encoder"),
});
let _ = params;
let r2t_t0 = std::time::Instant::now();
if let Some(ref mut backend) = state.urx_wgpu_full_backend {
if let Err(e) = backend.submit(&scene, &mut encoder, &surface_view) {
eprintln!("[render-hub] urx_wgpu_full submit error: {:?}", e);
}
}
queue.submit(std::iter::once(encoder.finish()));
metrics.render_to_texture_us = r2t_t0.elapsed().as_micros() as u64;
let present_t0 = std::time::Instant::now();
surface_texture.present();
metrics.present_us = present_t0.elapsed().as_micros() as u64;
false
}