use drmkit_core::Device;
use drmkit_dumb::{Buffer, Config, DumbError, MapAccess, Mapping};
use drmkit_fmt::{format_bpp, fourcc};
use drmkit_scene::{
AcquiredBuffer, BindingModel, DamageRect, LayerBufferSource, SourceError, SourceFormat,
};
#[derive(Debug)]
pub struct DumbBufferSource {
buffer: Buffer,
format: SourceFormat,
pending_damage: Vec<DamageRect>,
}
impl DumbBufferSource {
pub fn create(
device: &Device,
width: u32,
height: u32,
fourcc: u32,
) -> Result<Self, DumbError> {
let buffer = allocate_for_format(device, width, height, fourcc)?;
Ok(Self {
format: SourceFormat {
fourcc,
modifier: 0,
width,
height,
},
buffer,
pending_damage: Vec::new(),
})
}
pub fn set_damage(&mut self, rects: &[DamageRect]) {
self.pending_damage.clear();
self.pending_damage.extend_from_slice(rects);
}
#[must_use]
pub const fn buffer(&self) -> &Buffer {
&self.buffer
}
}
impl LayerBufferSource for DumbBufferSource {
fn acquire(&mut self) -> Result<AcquiredBuffer, SourceError> {
let fb_id = self
.buffer
.fb_id()
.ok_or(SourceError::Failed(rustix::io::Errno::INVAL))?;
Ok(AcquiredBuffer {
fb_id,
token: 0,
acquire_fence: None,
damage: std::mem::take(&mut self.pending_damage),
})
}
fn release(&mut self, _acquired: AcquiredBuffer) {
}
fn binding_model(&self) -> BindingModel {
BindingModel::SceneSubmitsFbId
}
fn format(&self) -> SourceFormat {
self.format
}
fn map(&mut self, access: MapAccess) -> Result<Mapping<'_>, SourceError> {
Ok(self.buffer.map(access))
}
fn on_session_paused(&mut self) {
}
fn on_session_resumed(&mut self, device: &Device) -> Result<(), SourceError> {
let SourceFormat {
fourcc,
width,
height,
..
} = self.format;
self.buffer.forget();
self.buffer = allocate_for_format(device, width, height, fourcc)
.map_err(|error| SourceError::Failed(crate::cursor::errno(&error)))?;
Ok(())
}
}
const fn is_semiplanar_yuv(fourcc: u32) -> bool {
matches!(
fourcc,
fourcc::NV12 | fourcc::NV21 | fourcc::P010 | fourcc::P012 | fourcc::P016
)
}
fn allocate_for_format(
device: &Device,
width: u32,
height: u32,
fourcc: u32,
) -> Result<Buffer, DumbError> {
if is_semiplanar_yuv(fourcc) {
return Buffer::create_planar(device, fourcc, width, height);
}
let bpp = format_bpp(fourcc);
if bpp == 0 {
return Err(DumbError::InvalidConfig {
reason: "format has no known single-plane depth",
});
}
Buffer::create(
device,
&Config {
width,
height,
fourcc,
bpp,
add_fb: true,
},
)
}