use std::any::Any;
use crate::color::Color;
use crate::geometry::Vec2;
use crate::raster::{CpuRasterImage, RasterComponent, RasterImage, RasterResidency, Resolution};
use crate::vector::VectorGraphic;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
pub enum GpuPreference {
Disabled,
#[default]
Auto,
PreferGpu,
}
impl GpuPreference {
pub const fn prefers_gpu(self) -> bool {
matches!(self, Self::Auto | Self::PreferGpu)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
pub enum CachePolicy {
#[default]
Memoize,
Transparent,
}
pub trait RenderContext {
fn as_any_mut(&mut self) -> &mut dyn Any;
fn gpu_preference(&self) -> GpuPreference {
GpuPreference::Disabled
}
fn prefers_gpu(&self) -> bool {
self.gpu_preference().prefers_gpu()
}
fn gpu_backend(&mut self) -> Option<&mut dyn GpuRasterBackend> {
None
}
fn motion_blur_enabled(&self) -> bool {
true
}
fn render(
&mut self,
component: &dyn RasterComponent,
size: Vec2,
target: Resolution,
residency: RasterResidency,
) -> RasterImage;
fn ensure_residency(&mut self, image: RasterImage, residency: RasterResidency) -> RasterImage {
if image.residency() == residency {
return image;
}
match residency {
RasterResidency::Cpu => RasterImage::Cpu(self.readback(image)),
RasterResidency::Gpu => match image {
RasterImage::Cpu(image) => {
let uploaded = self.gpu_backend().and_then(|gpu| gpu.upload(&image));
match uploaded {
Some(uploaded) if uploaded.residency() == RasterResidency::Gpu => uploaded,
_ => RasterImage::Cpu(image),
}
}
RasterImage::Gpu(_) => image,
},
}
}
fn readback(&mut self, image: RasterImage) -> CpuRasterImage {
match image {
RasterImage::Cpu(image) => image,
image @ RasterImage::Gpu(_) => {
let backend = match &image {
RasterImage::Gpu(surface) => surface.backend(),
RasterImage::Cpu(_) => unreachable!(),
};
if let Some(gpu) = self.gpu_backend() {
if let Some(image) = gpu.readback(image) {
return image;
}
}
panic!(
"render context returned a GPU image for backend '{backend}' but did not implement readback",
)
}
}
}
}
pub struct CompositeInput<'a> {
pub image: &'a RasterImage,
pub offset_x: i32,
pub offset_y: i32,
}
pub struct DropShadowInput<'a> {
pub child: &'a RasterImage,
pub target: Resolution,
pub child_offset_x: i32,
pub child_offset_y: i32,
pub shadow_offset_x: i32,
pub shadow_offset_y: i32,
pub blur_radius: u32,
pub color: Color,
}
pub struct OutlineInput<'a> {
pub child: &'a RasterImage,
pub target: Resolution,
pub child_offset_x: i32,
pub child_offset_y: i32,
pub radius_x: u32,
pub radius_y: u32,
pub color: Color,
}
pub trait GpuRasterBackend {
fn upload(&mut self, _image: &CpuRasterImage) -> Option<RasterImage> {
None
}
fn composite(
&mut self,
target: Resolution,
inputs: &[CompositeInput<'_>],
) -> Option<RasterImage>;
fn drop_shadow(&mut self, input: DropShadowInput<'_>) -> Option<RasterImage>;
fn outline(&mut self, input: OutlineInput<'_>) -> Option<RasterImage>;
fn rasterize(&mut self, graphic: &VectorGraphic, target: Resolution) -> Option<RasterImage>;
fn solid_fill(&mut self, target: Resolution, color: Color) -> Option<RasterImage>;
fn temporal_average(
&mut self,
target: Resolution,
frames: &[&RasterImage],
total: u32,
) -> Option<RasterImage>;
fn readback(&mut self, image: RasterImage) -> Option<CpuRasterImage>;
}
pub struct PassThrough;
impl RenderContext for PassThrough {
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
fn render(
&mut self,
component: &dyn RasterComponent,
size: Vec2,
target: Resolution,
residency: RasterResidency,
) -> RasterImage {
let image = component.render(size, target, residency, self);
self.ensure_residency(image, residency)
}
}