pub(crate) mod raster;
use drm::buffer::DrmFourcc;
use drm::control::dumbbuffer::DumbBuffer;
use drm::control::{Device as ControlDevice, framebuffer};
use crate::gbm::{Allocator, Scanout};
use crate::kms::{Card, Output};
use crate::vk::{self, Frame, Text, Thumb};
use raster::Canvas;
enum Backend {
Vk { gpu: vk::Gpu, targets: Vec<vk::Target>, fbs: Vec<ash::vk::Framebuffer> },
Cpu { canvas: Canvas, dumbs: Vec<DumbBuffer> },
}
pub struct UiPlane {
fbs: Vec<framebuffer::Handle>, scanouts: Vec<Scanout>, backend: Backend,
}
impl UiPlane {
pub fn new(alloc: &Allocator, card: &Card, output: &Output, w: u32, h: u32, n: usize) -> Result<(UiPlane, Text), String> {
if std::env::var("MEDIAS_UI").is_ok_and(|v| v.trim() == "cpu") {
println!("ui: CPU rasterizer (MEDIAS_UI=cpu)");
} else {
match Self::vulkan(alloc, card, output, w, h, n) {
Ok(r) => return Ok(r),
Err(e) => println!("ui: Vulkan unavailable ({e}); CPU rasterizer"),
}
}
let text = Text::new()?;
let mut plane = UiPlane {
fbs: Vec::new(),
scanouts: Vec::new(),
backend: Backend::Cpu { canvas: Canvas::new(w, h), dumbs: Vec::new() },
};
for _ in 0..n {
let Backend::Cpu { dumbs, .. } = &mut plane.backend else { unreachable!() };
let step = card.create_dumb_buffer((w, h), DrmFourcc::Argb8888, 32).and_then(|db| {
match card.add_framebuffer(&db, 32, 32) {
Ok(fb) => Ok((db, fb)),
Err(e) => {
let _ = card.destroy_dumb_buffer(db);
Err(e)
}
}
});
match step {
Ok((db, fb)) => {
dumbs.push(db);
plane.fbs.push(fb);
}
Err(e) => {
plane.destroy(text, card);
return Err(format!("ui dumb buffer: {e}"));
}
}
}
Ok((plane, text))
}
fn vulkan(alloc: &Allocator, card: &Card, output: &Output, w: u32, h: u32, n: usize) -> Result<(UiPlane, Text), String> {
let gpu = vk::Gpu::new()?;
let text = gpu.create_text()?;
let mut plane = UiPlane {
fbs: Vec::new(),
scanouts: Vec::new(),
backend: Backend::Vk { gpu, targets: Vec::new(), fbs: Vec::new() },
};
for _ in 0..n {
let Backend::Vk { gpu, targets, fbs } = &mut plane.backend else { unreachable!() };
let step = gpu.create_scanout_target(alloc, card, output, w, h, DrmFourcc::Argb8888).and_then(|(buf, target)| {
match text.framebuffer(gpu, &target) {
Ok(fb) => Ok((buf, target, fb)),
Err(e) => {
gpu.destroy_target(target);
let _ = card.destroy_framebuffer(buf.fb);
Err(e)
}
}
});
match step {
Ok((buf, target, fb)) => {
plane.fbs.push(buf.fb);
plane.scanouts.push(buf);
targets.push(target);
fbs.push(fb);
}
Err(e) => {
plane.destroy(text, card);
return Err(e);
}
}
}
if let Backend::Vk { gpu, .. } = &plane.backend {
println!("ui: Vulkan on {}", gpu.name);
}
Ok((plane, text))
}
pub fn fb(&self, i: usize) -> framebuffer::Handle {
self.fbs[i]
}
pub fn upload_thumb(&mut self, text: &Text, slot: usize, w: u32, h: u32, rgba: &[u8]) -> Result<Thumb, String> {
match &mut self.backend {
Backend::Vk { gpu, .. } => text.upload_thumb(gpu, slot, w, h, rgba),
Backend::Cpu { canvas, .. } => canvas.upload_thumb(slot, w, h, rgba),
}
}
pub fn render(&mut self, card: &Card, text: &Text, i: usize, frame: &Frame) -> Result<(), String> {
match &mut self.backend {
Backend::Vk { gpu, targets, fbs } => text.render(gpu, &targets[i], fbs[i], frame),
Backend::Cpu { canvas, dumbs } => {
canvas.render(text, frame);
let db = &mut dumbs[i];
let stride = drm::buffer::Buffer::pitch(db) as usize;
let mut map = card.map_dumb_buffer(db).map_err(|e| format!("ui map: {e}"))?;
canvas.write(map.as_mut(), stride);
Ok(())
}
}
}
pub fn destroy(self, text: Text, card: &Card) {
for fb in self.fbs {
let _ = card.destroy_framebuffer(fb);
}
match self.backend {
Backend::Vk { gpu, targets, fbs } => {
for fb in fbs {
gpu.destroy_framebuffer(fb);
}
text.destroy(&gpu);
for t in targets {
gpu.destroy_target(t);
}
drop(self.scanouts);
}
Backend::Cpu { dumbs, .. } => {
for db in dumbs {
let _ = card.destroy_dumb_buffer(db);
}
}
}
}
}