use vello::peniko::color::{AlphaColor, Srgb};
use vello::util::{RenderContext as VelloRenderContext, RenderSurface};
use vello::{wgpu, AaConfig, RenderParams};
use crate::backend::RenderBackend;
use crate::factory::WindowRenderState;
use crate::metrics::RenderMetrics;
pub struct SubmitParams<'a> {
pub render_cx: &'a VelloRenderContext,
pub surface: &'a RenderSurface<'static>,
pub base_color: AlphaColor<Srgb>,
pub msaa_samples: u8,
}
#[derive(Debug, Clone, Copy, Default)]
pub struct SubmitOutcome {
pub metrics: RenderMetrics,
pub surface_lost: bool,
}
pub fn submit_frame(state: &mut WindowRenderState, params: SubmitParams<'_>) -> SubmitOutcome {
let mut metrics = RenderMetrics {
backend: Some(state.backend),
..Default::default()
};
let width = params.surface.config.width;
let height = params.surface.config.height;
if width == 0 || height == 0 {
return SubmitOutcome { metrics, surface_lost: false };
}
let dev_id = params.surface.dev_id;
let device = ¶ms.render_cx.devices[dev_id].device;
let queue = ¶ms.render_cx.devices[dev_id].queue;
let total_t0 = std::time::Instant::now();
let r2t_t0 = std::time::Instant::now();
match state.backend {
RenderBackend::VelloGpu => {
state
.vello_renderer
.render_to_texture(
device,
queue,
&state.scene,
¶ms.surface.target_view,
&RenderParams {
base_color: params.base_color,
width,
height,
antialiasing_method: aa_for(params.msaa_samples),
},
)
.expect("vello render_to_texture failed");
}
RenderBackend::VelloCpu | RenderBackend::TinySkia => {
let (cw, ch) = state.cpu_dims;
if !state.cpu_pixels.is_empty() && cw > 0 && ch > 0 && cw == width && ch == height {
queue.write_texture(
wgpu::TexelCopyTextureInfo {
texture: ¶ms.surface.target_texture,
mip_level: 0,
origin: wgpu::Origin3d::ZERO,
aspect: wgpu::TextureAspect::All,
},
&state.cpu_pixels,
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 },
);
}
}
RenderBackend::InstancedWgpu | RenderBackend::VelloHybrid => {
}
}
metrics.render_to_texture_us = r2t_t0.elapsed().as_micros() as u64;
let present_t0 = std::time::Instant::now();
let surface_texture = match params.surface.surface.get_current_texture() {
Ok(t) => t,
Err(wgpu::SurfaceError::OutOfMemory) => {
metrics.submit_us = total_t0.elapsed().as_micros() as u64;
return SubmitOutcome { metrics, surface_lost: true };
}
Err(e) => {
eprintln!("[render-hub] surface error: {e:?}, reconfiguring");
params.surface.surface.configure(device, ¶ms.surface.config);
metrics.submit_us = total_t0.elapsed().as_micros() as u64;
return SubmitOutcome { metrics, surface_lost: false };
}
};
let surface_view = surface_texture
.texture
.create_view(&wgpu::TextureViewDescriptor::default());
match state.backend {
RenderBackend::VelloGpu | RenderBackend::VelloCpu | RenderBackend::TinySkia => {
let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor {
label: Some("uzor-render-hub:blit"),
});
params.surface.blitter.copy(
device,
&mut encoder,
¶ms.surface.target_view,
&surface_view,
);
queue.submit([encoder.finish()]);
}
RenderBackend::InstancedWgpu => {
if state.instanced_renderer.is_none() {
state.instanced_renderer = Some(
uzor_render_wgpu_instanced::InstancedRenderer::new(
device,
queue,
surface_texture.texture.format(),
),
);
}
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,
&state.instanced_commands,
Some(clear),
None,
);
}
metrics.draw_calls = state.instanced_commands.len() as u32;
}
RenderBackend::VelloHybrid => {
if let Some(ref hybrid_ctx) = state.hybrid_ctx {
if state.hybrid_renderer.is_none() {
state.hybrid_renderer = Some(vello_hybrid::Renderer::new(
device,
&vello_hybrid::RenderTargetConfig {
format: surface_texture.texture.format(),
width,
height,
},
));
}
if let Some(ref mut renderer) = state.hybrid_renderer {
let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor {
label: Some("uzor-render-hub:vello_hybrid"),
});
let _ = hybrid_ctx.render(renderer, device, queue, &mut encoder, &surface_view);
queue.submit([encoder.finish()]);
}
}
}
}
surface_texture.present();
metrics.present_us = present_t0.elapsed().as_micros() as u64;
metrics.submit_us = total_t0.elapsed().as_micros() as u64;
SubmitOutcome { metrics, surface_lost: false }
}
fn aa_for(msaa: u8) -> AaConfig {
match msaa {
0 => AaConfig::Area,
8 => AaConfig::Msaa8,
16 => AaConfig::Msaa16,
_ => AaConfig::Msaa8,
}
}