mod device_surface_render_target;
mod i_vello_gpu;
#[cfg(target_os = "macos")]
pub mod metal;
#[cfg(target_os = "macos")]
pub mod metal_offscreen;
mod vello_wgpu_device;
pub use device_surface_render_target::DeviceSurfaceRenderTarget;
pub use i_vello_gpu::{create_window_scene_sink, window_rendering_mode, IVelloGpu};
pub use vello_wgpu_device::{VelloWgpuDevice, VelloWgpuDeviceError};
use ferroui_base::platform::{IOptionalFeatureProvider, IPlatformGraphicsContext};
use std::rc::Rc;
pub fn create_gpu(graphics_context: &Rc<dyn IPlatformGraphicsContext>) -> Rc<dyn IVelloGpu> {
let features: &dyn IOptionalFeatureProvider = &**graphics_context;
#[cfg(target_os = "macos")]
if let Some(metal) = features.try_get::<dyn ferroui_metal::IMetalDevice>() {
return match metal::VelloMetalGpu::new(metal) {
Ok(gpu) => gpu,
Err(error) => panic!("{error}"),
};
}
let _ = features;
panic!("Graphics context of type is not supported");
}
#[cfg(all(test, feature = "hybrid"))]
mod layer_tests;
#[cfg(test)]
mod tests;
use ferroui_base::logging::{LogArea, LogEventLevel, Logger};
pub struct VelloGpuTexture<'a> {
pub texture: &'a wgpu::Texture,
}
#[cfg(feature = "hybrid")]
pub(crate) fn premultiplied_rgba(image: &peniko::ImageData) -> std::borrow::Cow<'_, [u8]> {
use std::borrow::Cow;
let data = image.data.data();
let swap = matches!(image.format, peniko::ImageFormat::Bgra8);
let premultiply = matches!(image.alpha_type, peniko::ImageAlphaType::Alpha);
if !swap && !premultiply {
return Cow::Borrowed(data);
}
let mut rgba = data.to_vec();
for pixel in rgba.chunks_exact_mut(4) {
if swap {
pixel.swap(0, 2);
}
if premultiply {
premultiply_pixel(pixel);
}
}
Cow::Owned(rgba)
}
pub(crate) fn premultiply_pixel(pixel: &mut [u8]) {
let alpha = u32::from(pixel[3]);
if alpha == 255 {
return;
}
for channel in &mut pixel[..3] {
*channel = ((u32::from(*channel) * alpha + 127) / 255) as u8;
}
}
pub(crate) fn log_render_failure(mode: &str, error: &str) {
if let Some(logger) = Logger::try_get(LogEventLevel::Error, LogArea::VISUAL) {
logger.log_with_values(None, "The {Mode} mode of the Vello backend could not render a scene: {Error}", &[&mode, &error]);
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum VelloTextureAlpha {
Premultiplied,
Straight,
}
pub struct VelloDeviceTexture {
device: std::sync::Arc<VelloWgpuDevice>,
texture: wgpu::Texture,
view: wgpu::TextureView,
id: u64,
alpha: VelloTextureAlpha,
}
impl std::fmt::Debug for VelloDeviceTexture {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("VelloDeviceTexture")
.field("id", &self.id)
.field("width", &self.texture.width())
.field("height", &self.texture.height())
.finish()
}
}
impl VelloDeviceTexture {
pub fn new(
device: &std::sync::Arc<VelloWgpuDevice>,
width: u32,
height: u32,
alpha: VelloTextureAlpha,
) -> std::sync::Arc<Self> {
use std::sync::atomic::{AtomicU64, Ordering};
static NEXT_ID: AtomicU64 = AtomicU64::new(1 << 63);
let usage = match alpha {
VelloTextureAlpha::Premultiplied => wgpu::TextureUsages::RENDER_ATTACHMENT,
VelloTextureAlpha::Straight => wgpu::TextureUsages::STORAGE_BINDING,
};
let texture = device.create_rgba_texture(width, height, usage | wgpu::TextureUsages::TEXTURE_BINDING);
let view = texture.create_view(&wgpu::TextureViewDescriptor::default());
crate::perf::count(crate::perf::Phase::SurfaceOnDevice, u64::from(width.max(1)) * u64::from(height.max(1)) * 4);
std::sync::Arc::new(Self {
device: device.clone(),
texture,
view,
id: NEXT_ID.fetch_add(1, Ordering::Relaxed),
alpha,
})
}
pub fn device(&self) -> &std::sync::Arc<VelloWgpuDevice> {
&self.device
}
pub fn texture(&self) -> &wgpu::Texture {
&self.texture
}
pub fn view(&self) -> &wgpu::TextureView {
&self.view
}
pub fn alpha(&self) -> VelloTextureAlpha {
self.alpha
}
pub fn id(&self) -> u64 {
self.id
}
pub fn width(&self) -> u32 {
self.texture.width()
}
pub fn height(&self) -> u32 {
self.texture.height()
}
pub fn read_pixels(&self) -> Option<Vec<u8>> {
let mut pixels = vec![0u8; self.width() as usize * self.height() as usize * 4];
if !self.device.read_texture(&self.texture, &mut pixels) {
return None;
}
if self.alpha == VelloTextureAlpha::Straight {
pixels.chunks_exact_mut(4).for_each(premultiply_pixel);
}
Some(pixels)
}
}
pub(crate) fn with_autorelease_pool<R>(f: impl FnOnce() -> R) -> R {
#[cfg(target_os = "macos")]
{
objc2::rc::autoreleasepool(|_| f())
}
#[cfg(not(target_os = "macos"))]
{
f()
}
}