1
0
Fork 0
forked from wry/wry

video: add udmabuf allocator

This commit is contained in:
Julian Orth 2024-09-01 20:23:04 +02:00
parent 2579834a60
commit 62cd29056a
33 changed files with 883 additions and 256 deletions

View file

@ -10,6 +10,7 @@ tasks:
sudo rmmod bochs sudo rmmod bochs
sudo modprobe vkms sudo modprobe vkms
sudo chmod o+rw /dev/dri/card* sudo chmod o+rw /dev/dri/card*
sudo chmod o+r /dev/udmabuf
- build: | - build: |
cd jay cd jay
cargo build --features it cargo build --features it

57
src/allocator.rs Normal file
View file

@ -0,0 +1,57 @@
use {
crate::{
format::Format,
video::{
dmabuf::{DmaBuf, DmaBufIds},
drm::Drm,
Modifier,
},
},
std::{error::Error, rc::Rc},
thiserror::Error,
};
#[derive(Debug, Error)]
#[error(transparent)]
pub struct AllocatorError(#[from] pub Box<dyn Error>);
bitflags! {
BufferUsage: u32;
BO_USE_SCANOUT = 1 << 0,
BO_USE_CURSOR = 1 << 1,
BO_USE_RENDERING = 1 << 2,
BO_USE_WRITE = 1 << 3,
BO_USE_LINEAR = 1 << 4,
BO_USE_PROTECTED = 1 << 5,
}
pub trait Allocator {
fn drm(&self) -> Option<&Drm>;
fn create_bo(
&self,
dma_buf_ids: &DmaBufIds,
width: i32,
height: i32,
format: &'static Format,
modifiers: &[Modifier],
usage: BufferUsage,
) -> Result<Rc<dyn BufferObject>, AllocatorError>;
fn import_dmabuf(
&self,
dmabuf: &DmaBuf,
usage: BufferUsage,
) -> Result<Rc<dyn BufferObject>, AllocatorError>;
}
pub trait BufferObject {
fn dmabuf(&self) -> &DmaBuf;
fn map_read(self: Rc<Self>) -> Result<Box<dyn MappedBuffer>, AllocatorError>;
fn map_write(self: Rc<Self>) -> Result<Box<dyn MappedBuffer>, AllocatorError>;
}
pub trait MappedBuffer {
unsafe fn data(&self) -> &[u8];
#[cfg_attr(not(test), allow(dead_code))]
fn data_ptr(&self) -> *mut u8;
fn stride(&self) -> i32;
}

View file

@ -1,5 +1,6 @@
use { use {
crate::{ crate::{
allocator::BufferObject,
async_engine::{Phase, SpawnedFuture}, async_engine::{Phase, SpawnedFuture},
backend::{ backend::{
BackendDrmDevice, BackendDrmLease, BackendDrmLessee, BackendEvent, Connector, BackendDrmDevice, BackendDrmLease, BackendDrmLessee, BackendEvent, Connector,
@ -73,6 +74,7 @@ pub struct PendingDrmDevice {
pub struct MetalRenderContext { pub struct MetalRenderContext {
pub dev_id: DrmDeviceId, pub dev_id: DrmDeviceId,
pub gfx: Rc<dyn GfxContext>, pub gfx: Rc<dyn GfxContext>,
pub gbm: Rc<GbmDevice>,
} }
pub struct MetalDrmDevice { pub struct MetalDrmDevice {
@ -91,7 +93,7 @@ pub struct MetalDrmDevice {
pub cursor_width: u64, pub cursor_width: u64,
pub cursor_height: u64, pub cursor_height: u64,
pub supports_async_commit: bool, pub supports_async_commit: bool,
pub gbm: GbmDevice, pub gbm: Rc<GbmDevice>,
pub handle_events: HandleEvents, pub handle_events: HandleEvents,
pub ctx: CloneCell<Rc<MetalRenderContext>>, pub ctx: CloneCell<Rc<MetalRenderContext>>,
pub on_change: OnChange<crate::backend::DrmEvent>, pub on_change: OnChange<crate::backend::DrmEvent>,
@ -2162,6 +2164,11 @@ impl MetalBackend {
} }
} }
let gbm = match GbmDevice::new(master) {
Ok(g) => Rc::new(g),
Err(e) => return Err(MetalError::GbmDevice(e)),
};
let gfx = match self.state.create_gfx_context(master, None) { let gfx = match self.state.create_gfx_context(master, None) {
Ok(r) => r, Ok(r) => r,
Err(e) => return Err(MetalError::CreateRenderContex(e)), Err(e) => return Err(MetalError::CreateRenderContex(e)),
@ -2169,13 +2176,9 @@ impl MetalBackend {
let ctx = Rc::new(MetalRenderContext { let ctx = Rc::new(MetalRenderContext {
dev_id: pending.id, dev_id: pending.id,
gfx, gfx,
gbm: gbm.clone(),
}); });
let gbm = match GbmDevice::new(master) {
Ok(g) => g,
Err(e) => return Err(MetalError::GbmDevice(e)),
};
let mut is_nvidia = false; let mut is_nvidia = false;
let mut is_amd = false; let mut is_amd = false;
match gbm.drm.version() { match gbm.drm.version() {
@ -2547,7 +2550,8 @@ impl MetalBackend {
} }
fn set_gfx_api(&self, dev: &MetalDrmDevice, api: GfxApi) { fn set_gfx_api(&self, dev: &MetalDrmDevice, api: GfxApi) {
if dev.ctx.get().gfx.gfx_api() == api { let old_ctx = dev.ctx.get();
if old_ctx.gfx.gfx_api() == api {
return; return;
} }
let gfx = match self.state.create_gfx_context(&dev.master, Some(api)) { let gfx = match self.state.create_gfx_context(&dev.master, Some(api)) {
@ -2566,6 +2570,7 @@ impl MetalBackend {
dev.ctx.set(Rc::new(MetalRenderContext { dev.ctx.set(Rc::new(MetalRenderContext {
dev_id: dev.id, dev_id: dev.id,
gfx, gfx,
gbm: old_ctx.gbm.clone(),
})); }));
if dev.is_render_device() { if dev.is_render_device() {
self.make_render_device(dev, true); self.make_render_device(dev, true);
@ -2836,7 +2841,7 @@ impl MetalBackend {
return Err(MetalError::MissingRenderModifier(format.name)); return Err(MetalError::MissingRenderModifier(format.name));
} }
usage = GBM_BO_USE_RENDERING | GBM_BO_USE_LINEAR; usage = GBM_BO_USE_RENDERING | GBM_BO_USE_LINEAR;
let render_bo = render_ctx.gfx.gbm().create_bo( let render_bo = render_ctx.gbm.create_bo(
&self.state.dma_buf_ids, &self.state.dma_buf_ids,
width, width,
height, height,

View file

@ -1,5 +1,6 @@
use { use {
crate::{ crate::{
allocator::BufferObject,
async_engine::{Phase, SpawnedFuture}, async_engine::{Phase, SpawnedFuture},
backend::{ backend::{
AxisSource, Backend, BackendDrmDevice, BackendEvent, Connector, ConnectorEvent, AxisSource, Backend, BackendDrmDevice, BackendEvent, Connector, ConnectorEvent,

View file

@ -1,23 +1,30 @@
use { use {
crate::{ crate::{
allocator::{Allocator, AllocatorError, BO_USE_LINEAR, BO_USE_RENDERING},
cli::{GlobalArgs, ScreenshotArgs, ScreenshotFormat}, cli::{GlobalArgs, ScreenshotArgs, ScreenshotFormat},
format::XRGB8888, format::XRGB8888,
tools::tool_client::{with_tool_client, Handle, ToolClient}, tools::tool_client::{with_tool_client, Handle, ToolClient},
udmabuf::{Udmabuf, UdmabufError},
utils::{errorfmt::ErrorFmt, queue::AsyncQueue, windows::WindowsExt}, utils::{errorfmt::ErrorFmt, queue::AsyncQueue, windows::WindowsExt},
video::{ video::{
dmabuf::{DmaBuf, DmaBufIds, DmaBufPlane, PlaneVec}, dmabuf::{DmaBuf, DmaBufIds, DmaBufPlane, PlaneVec},
drm::Drm, drm::{Drm, DrmError},
gbm::{GbmDevice, GBM_BO_USE_LINEAR, GBM_BO_USE_RENDERING}, gbm::{GbmDevice, GbmError},
}, },
wire::{ wire::{
jay_compositor::TakeScreenshot, jay_compositor::TakeScreenshot,
jay_screenshot::{Dmabuf, Error}, jay_screenshot::{Dmabuf, Dmabuf2, DrmDev, Error, Plane},
}, },
}, },
chrono::Local, chrono::Local,
jay_algorithms::qoi::xrgb8888_encode_qoi, jay_algorithms::qoi::xrgb8888_encode_qoi,
png::{BitDepth, ColorType, Encoder, SrgbRenderingIntent}, png::{BitDepth, ColorType, Encoder, SrgbRenderingIntent},
std::rc::Rc, std::{
cell::{Cell, RefCell},
rc::Rc,
},
thiserror::Error,
uapi::OwnedFd,
}; };
pub fn main(global: GlobalArgs, args: ScreenshotArgs) { pub fn main(global: GlobalArgs, args: ScreenshotArgs) {
@ -47,17 +54,62 @@ async fn run(screenshot: Rc<Screenshot>) {
Error::handle(tc, sid, result.clone(), |res, err| { Error::handle(tc, sid, result.clone(), |res, err| {
res.push(Err(err.msg.to_owned())); res.push(Err(err.msg.to_owned()));
}); });
Dmabuf::handle(tc, sid, result.clone(), |res, buf| { Dmabuf::handle(tc, sid, result.clone(), |res, ev| {
res.push(Ok(buf)); let mut planes = PlaneVec::new();
planes.push(DmaBufPlane {
offset: ev.offset,
stride: ev.stride,
fd: ev.fd,
});
let buf = DmaBuf {
id: DmaBufIds::default().next(),
width: ev.width as _,
height: ev.height as _,
format: XRGB8888,
modifier: ((ev.modifier_hi as u64) << 32) | (ev.modifier_lo as u64),
planes,
};
res.push(Ok((buf, Some(ev.drm_dev))));
}); });
let buf = match result.pop().await { let drm_dev = Rc::new(Cell::new(None));
let planes = Rc::new(RefCell::new(PlaneVec::new()));
DrmDev::handle(tc, sid, drm_dev.clone(), |res, buf| {
res.set(Some(buf.drm_dev));
});
Plane::handle(tc, sid, planes.clone(), |res, buf| {
res.borrow_mut().push(DmaBufPlane {
offset: buf.offset,
stride: buf.stride,
fd: buf.fd,
});
});
Dmabuf2::handle(
tc,
sid,
(drm_dev, planes, result.clone()),
|(dev, planes, res), ev| {
let buf = DmaBuf {
id: DmaBufIds::default().next(),
width: ev.width,
height: ev.height,
format: XRGB8888,
modifier: ev.modifier,
planes: planes.take(),
};
res.push(Ok((buf, dev.take())))
},
);
let (buf, drm_dev) = match result.pop().await {
Ok(b) => b, Ok(b) => b,
Err(e) => { Err(e) => {
fatal!("Could not take a screenshot: {}", e); fatal!("Could not take a screenshot: {}", e);
} }
}; };
let format = screenshot.args.format; let format = screenshot.args.format;
let data = buf_to_bytes(&DmaBufIds::default(), &buf, format); let data = match buf_to_bytes(drm_dev.as_ref(), &buf, format) {
Ok(d) => d,
Err(e) => fatal!("{}", ErrorFmt(e)),
};
let filename = match &screenshot.args.filename { let filename = match &screenshot.args.filename {
Some(f) => f.clone(), Some(f) => f.clone(),
_ => { _ => {
@ -74,48 +126,48 @@ async fn run(screenshot: Rc<Screenshot>) {
} }
} }
pub fn buf_to_bytes(dma_buf_ids: &DmaBufIds, buf: &Dmabuf, format: ScreenshotFormat) -> Vec<u8> { #[derive(Debug, Error)]
let drm = match Drm::reopen(buf.drm_dev.raw(), false) { pub enum ScreenshotError {
Ok(drm) => drm, #[error("Could not open the drm device")]
Err(e) => { OpenDrmDevice(#[source] DrmError),
fatal!("Could not open the drm device: {}", ErrorFmt(e)); #[error("Could not create a gbm device")]
} CreateGbmDevice(#[source] GbmError),
}; #[error("Could not create a udmabuf allocator")]
let gbm = match GbmDevice::new(&drm) { CreateUdmabuf(#[source] UdmabufError),
Ok(g) => g, #[error("Could not import a dmabuf")]
Err(e) => { ImportDmabuf(#[source] AllocatorError),
fatal!("Could not create a gbm device: {}", ErrorFmt(e)); #[error("Could not map a dmabuf")]
} MapDmabuf(#[source] AllocatorError),
}; }
let mut planes = PlaneVec::new();
planes.push(DmaBufPlane { pub fn buf_to_bytes(
offset: buf.offset, drm_dev: Option<&Rc<OwnedFd>>,
stride: buf.stride, buf: &DmaBuf,
fd: buf.fd.clone(), format: ScreenshotFormat,
}); ) -> Result<Vec<u8>, ScreenshotError> {
let dmabuf = DmaBuf { let allocator: Rc<dyn Allocator> = match drm_dev {
id: dma_buf_ids.next(), Some(drm_dev) => {
width: buf.width as _, let drm = Drm::reopen(drm_dev.raw(), false).map_err(ScreenshotError::OpenDrmDevice)?;
height: buf.height as _, GbmDevice::new(&drm)
format: XRGB8888, .map(Rc::new)
modifier: (buf.modifier_hi as u64) << 32 | (buf.modifier_lo as u64), .map_err(ScreenshotError::CreateGbmDevice)?
planes,
};
let bo = match gbm.import_dmabuf(&dmabuf, GBM_BO_USE_LINEAR | GBM_BO_USE_RENDERING) {
Ok(bo) => Rc::new(bo),
Err(e) => {
fatal!("Could not import screenshot dmabuf: {}", ErrorFmt(e));
}
};
let bo_map = match bo.map_read() {
Ok(map) => map,
Err(e) => {
fatal!("Could not map dmabuf: {}", ErrorFmt(e));
} }
None => Udmabuf::new()
.map(Rc::new)
.map_err(ScreenshotError::CreateUdmabuf)?,
}; };
let bo = allocator
.import_dmabuf(buf, BO_USE_LINEAR | BO_USE_RENDERING)
.map_err(ScreenshotError::ImportDmabuf)?;
let bo_map = bo.map_read().map_err(ScreenshotError::MapDmabuf)?;
let data = unsafe { bo_map.data() }; let data = unsafe { bo_map.data() };
if format == ScreenshotFormat::Qoi { if format == ScreenshotFormat::Qoi {
return xrgb8888_encode_qoi(data, buf.width, buf.height, bo_map.stride() as u32); return Ok(xrgb8888_encode_qoi(
data,
buf.width as _,
buf.height as _,
bo_map.stride() as u32,
));
} }
let mut out = vec![]; let mut out = vec![];
@ -128,12 +180,12 @@ pub fn buf_to_bytes(dma_buf_ids: &DmaBufIds, buf: &Dmabuf, format: ScreenshotFor
image_data.extend_from_slice(&[pixel[2], pixel[1], pixel[0], 255]) image_data.extend_from_slice(&[pixel[2], pixel[1], pixel[0], 255])
} }
} }
let mut encoder = Encoder::new(&mut out, buf.width, buf.height); let mut encoder = Encoder::new(&mut out, buf.width as _, buf.height as _);
encoder.set_color(ColorType::Rgba); encoder.set_color(ColorType::Rgba);
encoder.set_depth(BitDepth::Eight); encoder.set_depth(BitDepth::Eight);
encoder.set_srgb(SrgbRenderingIntent::Perceptual); encoder.set_srgb(SrgbRenderingIntent::Perceptual);
let mut writer = encoder.write_header().unwrap(); let mut writer = encoder.write_header().unwrap();
writer.write_image_data(&image_data).unwrap(); writer.write_image_data(&image_data).unwrap();
} }
out Ok(out)
} }

View file

@ -36,9 +36,11 @@ impl DrmFeedback {
ids: &DrmFeedbackIds, ids: &DrmFeedbackIds,
render_ctx: &dyn GfxContext, render_ctx: &dyn GfxContext,
) -> Result<Self, DrmFeedbackError> { ) -> Result<Self, DrmFeedbackError> {
let main_device = uapi::fstat(render_ctx.gbm().drm.raw()) let drm = match render_ctx.allocator().drm() {
.map_err(OsError::from)? Some(drm) => drm.raw(),
.st_rdev; _ => return Err(DrmFeedbackError::NoDrmDevice),
};
let main_device = uapi::fstat(drm).map_err(OsError::from)?.st_rdev;
let (data, index_map) = create_fd_data(render_ctx); let (data, index_map) = create_fd_data(render_ctx);
let mut memfd = let mut memfd =
uapi::memfd_create("drm_feedback", c::MFD_CLOEXEC | c::MFD_ALLOW_SEALING).unwrap(); uapi::memfd_create("drm_feedback", c::MFD_CLOEXEC | c::MFD_ALLOW_SEALING).unwrap();
@ -118,4 +120,6 @@ fn create_fd_data(ctx: &dyn GfxContext) -> (Vec<u8>, AHashMap<(u32, Modifier), u
pub enum DrmFeedbackError { pub enum DrmFeedbackError {
#[error("Could not stat drm device")] #[error("Could not stat drm device")]
Stat(#[from] OsError), Stat(#[from] OsError),
#[error("Graphics API does not have a DRM device")]
NoDrmDevice,
} }

View file

@ -1,5 +1,6 @@
use { use {
crate::{ crate::{
allocator::Allocator,
cursor::Cursor, cursor::Cursor,
damage::DamageVisualizer, damage::DamageVisualizer,
fixed::Fixed, fixed::Fixed,
@ -11,7 +12,7 @@ use {
theme::Color, theme::Color,
tree::{Node, OutputNode}, tree::{Node, OutputNode},
utils::{clonecell::UnsafeCellCloneSafe, transform_ext::TransformExt}, utils::{clonecell::UnsafeCellCloneSafe, transform_ext::TransformExt},
video::{dmabuf::DmaBuf, drm::sync_obj::SyncObjCtx, gbm::GbmDevice, Modifier}, video::{dmabuf::DmaBuf, drm::sync_obj::SyncObjCtx, Modifier},
}, },
ahash::AHashMap, ahash::AHashMap,
indexmap::IndexSet, indexmap::IndexSet,
@ -533,7 +534,7 @@ pub trait GfxTexture: Debug {
pub trait GfxContext: Debug { pub trait GfxContext: Debug {
fn reset_status(&self) -> Option<ResetStatus>; fn reset_status(&self) -> Option<ResetStatus>;
fn render_node(&self) -> Rc<CString>; fn render_node(&self) -> Option<Rc<CString>>;
fn formats(&self) -> Rc<AHashMap<u32, GfxFormat>>; fn formats(&self) -> Rc<AHashMap<u32, GfxFormat>>;
@ -554,7 +555,7 @@ pub trait GfxContext: Debug {
damage: Option<&[Rect]>, damage: Option<&[Rect]>,
) -> Result<Rc<dyn GfxTexture>, GfxError>; ) -> Result<Rc<dyn GfxTexture>, GfxError>;
fn gbm(&self) -> &GbmDevice; fn allocator(&self) -> Rc<dyn Allocator>;
fn gfx_api(&self) -> GfxApi; fn gfx_api(&self) -> GfxApi;
@ -566,7 +567,7 @@ pub trait GfxContext: Debug {
format: &'static Format, format: &'static Format,
) -> Result<Rc<dyn GfxFramebuffer>, GfxError>; ) -> Result<Rc<dyn GfxFramebuffer>, GfxError>;
fn sync_obj_ctx(&self) -> &Rc<SyncObjCtx>; fn sync_obj_ctx(&self) -> Option<&Rc<SyncObjCtx>>;
} }
#[derive(Debug)] #[derive(Debug)]

View file

@ -1,5 +1,6 @@
use { use {
crate::{ crate::{
allocator::Allocator,
format::{Format, XRGB8888}, format::{Format, XRGB8888},
gfx_api::{ gfx_api::{
BufferResvUser, GfxApiOpt, GfxContext, GfxError, GfxFormat, GfxFramebuffer, GfxImage, BufferResvUser, GfxApiOpt, GfxContext, GfxError, GfxFormat, GfxFramebuffer, GfxImage,
@ -240,8 +241,8 @@ impl GfxContext for GlRenderContext {
self.reset_status() self.reset_status()
} }
fn render_node(&self) -> Rc<CString> { fn render_node(&self) -> Option<Rc<CString>> {
self.render_node() Some(self.render_node())
} }
fn formats(&self) -> Rc<AHashMap<u32, GfxFormat>> { fn formats(&self) -> Rc<AHashMap<u32, GfxFormat>> {
@ -278,8 +279,8 @@ impl GfxContext for GlRenderContext {
.map_err(|e| e.into()) .map_err(|e| e.into())
} }
fn gbm(&self) -> &GbmDevice { fn allocator(&self) -> Rc<dyn Allocator> {
&self.gbm self.gbm.clone()
} }
fn gfx_api(&self) -> GfxApi { fn gfx_api(&self) -> GfxApi {
@ -299,7 +300,7 @@ impl GfxContext for GlRenderContext {
Ok(Rc::new(Framebuffer { ctx: self, gl: fb })) Ok(Rc::new(Framebuffer { ctx: self, gl: fb }))
} }
fn sync_obj_ctx(&self) -> &Rc<SyncObjCtx> { fn sync_obj_ctx(&self) -> Option<&Rc<SyncObjCtx>> {
&self.sync_ctx Some(&self.sync_ctx)
} }
} }

View file

@ -17,6 +17,7 @@ mod util;
use { use {
crate::{ crate::{
allocator::Allocator,
async_engine::AsyncEngine, async_engine::AsyncEngine,
format::Format, format::Format,
gfx_api::{ gfx_api::{
@ -31,7 +32,7 @@ use {
video::{ video::{
dmabuf::DmaBuf, dmabuf::DmaBuf,
drm::{sync_obj::SyncObjCtx, Drm, DrmError}, drm::{sync_obj::SyncObjCtx, Drm, DrmError},
gbm::{GbmDevice, GbmError}, gbm::GbmError,
}, },
}, },
ahash::AHashMap, ahash::AHashMap,
@ -209,8 +210,8 @@ impl GfxContext for Context {
None None
} }
fn render_node(&self) -> Rc<CString> { fn render_node(&self) -> Option<Rc<CString>> {
self.0.device.render_node.clone() Some(self.0.device.render_node.clone())
} }
fn formats(&self) -> Rc<AHashMap<u32, GfxFormat>> { fn formats(&self) -> Rc<AHashMap<u32, GfxFormat>> {
@ -255,8 +256,8 @@ impl GfxContext for Context {
Ok(tex as _) Ok(tex as _)
} }
fn gbm(&self) -> &GbmDevice { fn allocator(&self) -> Rc<dyn Allocator> {
&self.0.device.gbm self.0.device.gbm.clone()
} }
fn gfx_api(&self) -> GfxApi { fn gfx_api(&self) -> GfxApi {
@ -276,8 +277,8 @@ impl GfxContext for Context {
Ok(fb) Ok(fb)
} }
fn sync_obj_ctx(&self) -> &Rc<SyncObjCtx> { fn sync_obj_ctx(&self) -> Option<&Rc<SyncObjCtx>> {
&self.0.device.sync_ctx Some(&self.0.device.sync_ctx)
} }
} }

View file

@ -49,7 +49,7 @@ use {
pub struct VulkanDevice { pub struct VulkanDevice {
pub(super) physical_device: PhysicalDevice, pub(super) physical_device: PhysicalDevice,
pub(super) render_node: Rc<CString>, pub(super) render_node: Rc<CString>,
pub(super) gbm: GbmDevice, pub(super) gbm: Rc<GbmDevice>,
pub(super) sync_ctx: Rc<SyncObjCtx>, pub(super) sync_ctx: Rc<SyncObjCtx>,
pub(super) instance: Rc<VulkanInstance>, pub(super) instance: Rc<VulkanInstance>,
pub(super) device: Device, pub(super) device: Device,
@ -276,7 +276,7 @@ impl VulkanInstance {
physical_device: phy_dev, physical_device: phy_dev,
render_node, render_node,
sync_ctx: Rc::new(SyncObjCtx::new(gbm.drm.fd())), sync_ctx: Rc::new(SyncObjCtx::new(gbm.drm.fd())),
gbm, gbm: Rc::new(gbm),
instance: self.clone(), instance: self.clone(),
device, device,
external_memory_fd, external_memory_fd,

View file

@ -32,6 +32,7 @@ use {
}; };
pub const CREATE_EI_SESSION_SINCE: Version = Version(5); pub const CREATE_EI_SESSION_SINCE: Version = Version(5);
pub const SCREENSHOT_SPLITUP_SINCE: Version = Version(6);
pub struct JayCompositorGlobal { pub struct JayCompositorGlobal {
name: GlobalName, name: GlobalName,
@ -69,7 +70,7 @@ impl Global for JayCompositorGlobal {
} }
fn version(&self) -> u32 { fn version(&self) -> u32 {
5 6
} }
fn required_caps(&self) -> ClientCaps { fn required_caps(&self) -> ClientCaps {
@ -117,16 +118,30 @@ impl JayCompositor {
match take_screenshot(&self.client.state, include_cursor) { match take_screenshot(&self.client.state, include_cursor) {
Ok(s) => { Ok(s) => {
let dmabuf = s.bo.dmabuf(); let dmabuf = s.bo.dmabuf();
let plane = &dmabuf.planes[0]; if self.version < SCREENSHOT_SPLITUP_SINCE {
ss.send_dmabuf( if let Some(drm) = &s.drm {
&s.drm, let plane = &dmabuf.planes[0];
&plane.fd, ss.send_dmabuf(
dmabuf.width, drm,
dmabuf.height, &plane.fd,
plane.offset, dmabuf.width,
plane.stride, dmabuf.height,
dmabuf.modifier, plane.offset,
); plane.stride,
dmabuf.modifier,
);
} else {
ss.send_error("Buffer has no associated DRM device");
}
} else {
if let Some(drm) = &s.drm {
ss.send_drm_dev(drm);
}
for plane in &dmabuf.planes {
ss.send_plane(plane);
}
ss.send_dmabuf2(dmabuf);
}
} }
Err(e) => { Err(e) => {
let msg = ErrorFmt(e).to_string(); let msg = ErrorFmt(e).to_string();

View file

@ -21,10 +21,16 @@ impl JayRenderCtx {
pub fn send_render_ctx(&self, ctx: Option<Rc<dyn GfxContext>>) { pub fn send_render_ctx(&self, ctx: Option<Rc<dyn GfxContext>>) {
let mut fd = None; let mut fd = None;
if let Some(ctx) = ctx { if let Some(ctx) = ctx {
match ctx.gbm().drm.dup_render() { let allocator = ctx.allocator();
Ok(d) => fd = Some(d.fd().clone()), match allocator.drm() {
Err(e) => { Some(drm) => match drm.dup_render() {
log::error!("Could not dup drm fd: {}", ErrorFmt(e)); Ok(d) => fd = Some(d.fd().clone()),
Err(e) => {
log::error!("Could not dup drm fd: {}", ErrorFmt(e));
}
},
None => {
log::error!("Allocator does not have a DRM device");
} }
} }
} else { } else {

View file

@ -1,5 +1,6 @@
use { use {
crate::{ crate::{
allocator::{AllocatorError, BufferObject, BO_USE_LINEAR, BO_USE_RENDERING},
client::{Client, ClientError}, client::{Client, ClientError},
format::XRGB8888, format::XRGB8888,
gfx_api::{GfxContext, GfxError, GfxFramebuffer, GfxTexture}, gfx_api::{GfxContext, GfxError, GfxFramebuffer, GfxTexture},
@ -16,17 +17,11 @@ use {
numcell::NumCell, numcell::NumCell,
option_ext::OptionExt, option_ext::OptionExt,
}, },
video::{ video::{dmabuf::DmaBuf, INVALID_MODIFIER, LINEAR_MODIFIER},
dmabuf::DmaBuf,
gbm::{GbmBo, GbmError, GBM_BO_USE_LINEAR, GBM_BO_USE_RENDERING},
Modifier, INVALID_MODIFIER, LINEAR_MODIFIER,
},
wire::{jay_screencast::*, JayScreencastId}, wire::{jay_screencast::*, JayScreencastId},
}, },
ahash::AHashSet, ahash::AHashSet,
indexmap::{indexset, IndexSet},
jay_config::video::Transform, jay_config::video::Transform,
once_cell::sync::Lazy,
std::{ std::{
cell::{Cell, RefCell}, cell::{Cell, RefCell},
ops::DerefMut, ops::DerefMut,
@ -108,7 +103,7 @@ struct Pending {
} }
struct ScreencastBuffer { struct ScreencastBuffer {
_bo: GbmBo, _bo: Rc<dyn BufferObject>,
dmabuf: DmaBuf, dmabuf: DmaBuf,
fb: Rc<dyn GfxFramebuffer>, fb: Rc<dyn GfxFramebuffer>,
free: bool, free: bool,
@ -381,31 +376,27 @@ impl JayScreencast {
if width == 0 || height == 0 { if width == 0 || height == 0 {
continue; continue;
} }
let mut usage = GBM_BO_USE_RENDERING; let mut usage = BO_USE_RENDERING;
let modifiers = match self.linear.get() { let modifiers = match self.linear.get() {
true if format.write_modifiers.contains(&LINEAR_MODIFIER) => { true if format.write_modifiers.contains(&LINEAR_MODIFIER) => {
static MODS: Lazy<IndexSet<Modifier>> = vec![LINEAR_MODIFIER]
Lazy::new(|| indexset![LINEAR_MODIFIER]);
&MODS
} }
true if format.write_modifiers.contains(&INVALID_MODIFIER) => { true if format.write_modifiers.contains(&INVALID_MODIFIER) => {
usage |= GBM_BO_USE_LINEAR; usage |= BO_USE_LINEAR;
static MODS: Lazy<IndexSet<Modifier>> = vec![INVALID_MODIFIER]
Lazy::new(|| indexset![INVALID_MODIFIER]);
&MODS
} }
true => return Err(JayScreencastError::Modifier), true => return Err(JayScreencastError::Modifier),
false if format.write_modifiers.is_empty() => { false if format.write_modifiers.is_empty() => {
return Err(JayScreencastError::XRGB8888Writing) return Err(JayScreencastError::XRGB8888Writing)
} }
false => &format.write_modifiers, false => format.write_modifiers.iter().copied().collect(),
}; };
let buffer = ctx.gbm().create_bo( let buffer = ctx.allocator().create_bo(
&self.client.state.dma_buf_ids, &self.client.state.dma_buf_ids,
width, width,
height, height,
XRGB8888, XRGB8888,
modifiers, &modifiers,
usage, usage,
)?; )?;
let fb = ctx.clone().dmabuf_img(buffer.dmabuf())?.to_framebuffer()?; let fb = ctx.clone().dmabuf_img(buffer.dmabuf())?.to_framebuffer()?;
@ -681,7 +672,7 @@ pub enum JayScreencastError {
#[error("Buffer index {0} is out-of-bounds")] #[error("Buffer index {0} is out-of-bounds")]
OutOfBounds(u32), OutOfBounds(u32),
#[error(transparent)] #[error(transparent)]
GbmError(#[from] GbmError), AllocatorError(#[from] AllocatorError),
#[error(transparent)] #[error(transparent)]
GfxError(#[from] GfxError), GfxError(#[from] GfxError),
#[error("Render context does not support XRGB8888 format")] #[error("Render context does not support XRGB8888 format")]

View file

@ -3,6 +3,7 @@ use {
client::Client, client::Client,
leaks::Tracker, leaks::Tracker,
object::{Object, Version}, object::{Object, Version},
video::dmabuf::{DmaBuf, DmaBufPlane},
wire::{jay_screenshot::*, JayScreenshotId}, wire::{jay_screenshot::*, JayScreenshotId},
}, },
std::{convert::Infallible, rc::Rc}, std::{convert::Infallible, rc::Rc},
@ -45,6 +46,31 @@ impl JayScreenshot {
msg, msg,
}); });
} }
pub fn send_drm_dev(&self, drm: &Rc<OwnedFd>) {
self.client.event(DrmDev {
self_id: self.id,
drm_dev: drm.clone(),
})
}
pub fn send_plane(&self, plane: &DmaBufPlane) {
self.client.event(Plane {
self_id: self.id,
fd: plane.fd.clone(),
offset: plane.offset,
stride: plane.stride,
})
}
pub fn send_dmabuf2(&self, buf: &DmaBuf) {
self.client.event(Dmabuf2 {
self_id: self.id,
width: buf.width,
height: buf.height,
modifier: buf.modifier,
})
}
} }
impl JayScreenshotRequestHandler for JayScreenshot { impl JayScreenshotRequestHandler for JayScreenshot {

View file

@ -43,7 +43,9 @@ impl WlDrmGlobal {
track!(client, obj); track!(client, obj);
client.add_client_obj(&obj)?; client.add_client_obj(&obj)?;
if let Some(rc) = client.state.render_ctx.get() { if let Some(rc) = client.state.render_ctx.get() {
obj.send_device(&rc.render_node()); if let Some(rn) = rc.render_node() {
obj.send_device(&rn);
}
obj.send_capabilities(PRIME); obj.send_capabilities(PRIME);
} }
Ok(()) Ok(())

View file

@ -202,7 +202,10 @@ impl Drop for SurfaceBuffer {
log::error!("Cannot signal release point because there is no render context"); log::error!("Cannot signal release point because there is no render context");
return; return;
}; };
let ctx = ctx.sync_obj_ctx(); let Some(ctx) = ctx.sync_obj_ctx() else {
log::error!("Cannot signal release point because there is no syncobj context");
return;
};
if sync_files.is_not_empty() { if sync_files.is_not_empty() {
let res = ctx.import_sync_files( let res = ctx.import_sync_files(
&release.sync_obj, &release.sync_obj,

View file

@ -1,5 +1,6 @@
use { use {
crate::{ crate::{
allocator::Allocator,
async_engine::SpawnedFuture, async_engine::SpawnedFuture,
backend::{ backend::{
AxisSource, Backend, BackendEvent, Connector, ConnectorEvent, ConnectorId, AxisSource, Backend, BackendEvent, Connector, ConnectorEvent, ConnectorId,
@ -15,14 +16,18 @@ use {
test_error::TestResult, test_gfx_api::TestGfxCtx, test_utils::test_expected_event::TEEH, test_error::TestResult, test_gfx_api::TestGfxCtx, test_utils::test_expected_event::TEEH,
}, },
state::State, state::State,
udmabuf::Udmabuf,
utils::{ utils::{
clonecell::CloneCell, copyhashmap::CopyHashMap, on_change::OnChange, oserror::OsError, clonecell::CloneCell, copyhashmap::CopyHashMap, errorfmt::ErrorFmt,
syncqueue::SyncQueue, on_change::OnChange, oserror::OsError, syncqueue::SyncQueue,
},
video::{
drm::{ConnectorType, Drm},
gbm::{GbmDevice, GbmError},
}, },
video::drm::{ConnectorType, Drm},
}, },
bstr::ByteSlice, bstr::ByteSlice,
std::{any::Any, cell::Cell, io, os::unix::ffi::OsStrExt, pin::Pin, rc::Rc}, std::{any::Any, cell::Cell, error::Error, io, os::unix::ffi::OsStrExt, pin::Pin, rc::Rc},
thiserror::Error, thiserror::Error,
uapi::c, uapi::c,
}; };
@ -37,6 +42,10 @@ pub enum TestBackendError {
OpenDrmNode(String, #[source] OsError), OpenDrmNode(String, #[source] OsError),
#[error("Could not create a render context")] #[error("Could not create a render context")]
RenderContext(#[source] GfxError), RenderContext(#[source] GfxError),
#[error("Could not create a gbm device")]
CreateGbmDevice(#[source] GbmError),
#[error("Could not create any allocator")]
CreateAllocator,
} }
pub struct TestBackend { pub struct TestBackend {
@ -124,17 +133,21 @@ impl TestBackend {
} }
} }
pub fn install_render_context(&self) -> TestResult { pub fn install_render_context(&self, prefer_udmabuf: bool) -> TestResult {
if self.render_context_installed.get() { if self.render_context_installed.get() {
return Ok(()); return Ok(());
} }
self.create_render_context()?; self.create_render_context(prefer_udmabuf)?;
self.render_context_installed.set(true); self.render_context_installed.set(true);
Ok(()) Ok(())
} }
pub fn install_default(&self) -> TestResult { pub fn install_default(&self) -> TestResult {
self.install_render_context()?; self.install_default2(true)
}
pub fn install_default2(&self, prefer_udmabuf: bool) -> TestResult {
self.install_render_context(prefer_udmabuf)?;
self.state self.state
.backend_events .backend_events
.push(BackendEvent::NewConnector(self.default_connector.clone())); .push(BackendEvent::NewConnector(self.default_connector.clone()));
@ -150,47 +163,35 @@ impl TestBackend {
Ok(()) Ok(())
} }
fn create_render_context(&self) -> Result<(), TestBackendError> { fn create_render_context(&self, prefer_udmabuf: bool) -> Result<(), TestBackendError> {
let dri = match std::fs::read_dir("/dev/dri") { macro_rules! constructor {
Ok(d) => d, ($c:expr) => {
Err(e) => return Err(TestBackendError::ReadDri(e)), (&|| {
}; $c.map(|a| Rc::new(a) as Rc<dyn Allocator>)
let mut files = vec![]; .map_err(|e| Box::new(e) as Box<dyn Error>)
for f in dri { }) as &dyn Fn() -> Result<Rc<dyn Allocator>, Box<dyn Error>>
let f = match f {
Ok(f) => f,
Err(e) => return Err(TestBackendError::ReadDri(e)),
}; };
files.push(f.path());
} }
let node = 'node: { let udmabuf = ("udmabuf", constructor!(Udmabuf::new()));
for f in &files { let gbm = ("GBM", constructor!(create_gbm_allocator()));
if let Some(file) = f.file_name() { let allocators = match prefer_udmabuf {
if file.as_bytes().starts_with_str("renderD") { true => [udmabuf, gbm],
break 'node f; false => [gbm, udmabuf],
} };
let mut allocator = None::<Rc<dyn Allocator>>;
for (name, f) in allocators {
match f() {
Ok(a) => {
allocator = Some(a);
break;
}
Err(e) => {
log::error!("Could not create {name} allocator: {}", ErrorFmt(&*e));
} }
} }
for f in &files { }
if let Some(file) = f.file_name() { let allocator = allocator.ok_or(TestBackendError::CreateAllocator)?;
if file.as_bytes().starts_with_str("card") { let ctx = match TestGfxCtx::new(allocator) {
break 'node f;
}
}
}
return Err(TestBackendError::NoDrmNode);
};
let file = match uapi::open(node.as_path(), c::O_RDWR | c::O_CLOEXEC, 0) {
Ok(f) => Rc::new(f),
Err(e) => {
return Err(TestBackendError::OpenDrmNode(
node.as_os_str().as_bytes().as_bstr().to_string(),
e.into(),
))
}
};
let drm = Drm::open_existing(file);
let ctx = match TestGfxCtx::new(&drm) {
Ok(ctx) => ctx, Ok(ctx) => ctx,
Err(e) => return Err(TestBackendError::RenderContext(e)), Err(e) => return Err(TestBackendError::RenderContext(e)),
}; };
@ -199,6 +200,50 @@ impl TestBackend {
} }
} }
fn create_gbm_allocator() -> Result<GbmDevice, TestBackendError> {
let dri = match std::fs::read_dir("/dev/dri") {
Ok(d) => d,
Err(e) => return Err(TestBackendError::ReadDri(e)),
};
let mut files = vec![];
for f in dri {
let f = match f {
Ok(f) => f,
Err(e) => return Err(TestBackendError::ReadDri(e)),
};
files.push(f.path());
}
let node = 'node: {
for f in &files {
if let Some(file) = f.file_name() {
if file.as_bytes().starts_with_str("renderD") {
break 'node f;
}
}
}
for f in &files {
if let Some(file) = f.file_name() {
if file.as_bytes().starts_with_str("card") {
break 'node f;
}
}
}
return Err(TestBackendError::NoDrmNode);
};
let file = match uapi::open(node.as_path(), c::O_RDWR | c::O_CLOEXEC, 0) {
Ok(f) => Rc::new(f),
Err(e) => {
return Err(TestBackendError::OpenDrmNode(
node.as_os_str().as_bytes().as_bstr().to_string(),
e.into(),
))
}
};
let drm = Drm::open_existing(file);
let gbm = GbmDevice::new(&drm).map_err(TestBackendError::CreateGbmDevice)?;
Ok(gbm)
}
impl Backend for TestBackend { impl Backend for TestBackend {
fn run(self: Rc<Self>) -> SpawnedFuture<Result<(), Box<dyn std::error::Error>>> { fn run(self: Rc<Self>) -> SpawnedFuture<Result<(), Box<dyn std::error::Error>>> {
let future = (self.test_future)(&self.state); let future = (self.test_future)(&self.state);

View file

@ -25,7 +25,7 @@ use {
pub struct TestClient { pub struct TestClient {
pub run: Rc<TestRun>, pub run: Rc<TestRun>,
pub server: Rc<Client>, pub _server: Rc<Client>,
pub tran: Rc<TestTransport>, pub tran: Rc<TestTransport>,
pub registry: Rc<TestRegistry>, pub registry: Rc<TestRegistry>,
pub jc: Rc<TestJayCompositor>, pub jc: Rc<TestJayCompositor>,
@ -92,12 +92,8 @@ impl TestClient {
} }
pub async fn take_screenshot(&self, include_cursor: bool) -> Result<Vec<u8>, TestError> { pub async fn take_screenshot(&self, include_cursor: bool) -> Result<Vec<u8>, TestError> {
let dmabuf = self.jc.take_screenshot(include_cursor).await?; let (dmabuf, dev) = self.jc.take_screenshot(include_cursor).await?;
let qoi = buf_to_bytes( let qoi = buf_to_bytes(dev.as_ref(), &dmabuf, ScreenshotFormat::Qoi)?;
&self.server.state.dma_buf_ids,
&dmabuf,
ScreenshotFormat::Qoi,
);
Ok(qoi) Ok(qoi)
} }

View file

@ -1,5 +1,6 @@
use { use {
crate::{ crate::{
allocator::{Allocator, AllocatorError, BufferObject, BufferUsage},
format::{Format, ARGB8888, XRGB8888}, format::{Format, ARGB8888, XRGB8888},
gfx_api::{ gfx_api::{
CopyTexture, FillRect, FramebufferRect, GfxApiOpt, GfxContext, GfxError, GfxFormat, CopyTexture, FillRect, FramebufferRect, GfxApiOpt, GfxContext, GfxError, GfxFormat,
@ -7,12 +8,7 @@ use {
}, },
rect::Rect, rect::Rect,
theme::Color, theme::Color,
video::{ video::{dmabuf::DmaBuf, drm::sync_obj::SyncObjCtx, LINEAR_MODIFIER},
dmabuf::DmaBuf,
drm::{sync_obj::SyncObjCtx, Drm, DrmError},
gbm::{GbmBo, GbmDevice, GbmError},
LINEAR_MODIFIER,
},
}, },
ahash::AHashMap, ahash::AHashMap,
indexmap::IndexSet, indexmap::IndexSet,
@ -32,15 +28,9 @@ use {
#[derive(Error, Debug)] #[derive(Error, Debug)]
enum TestGfxError { enum TestGfxError {
#[error("Could not map dmabuf")] #[error("Could not map dmabuf")]
MapDmaBuf(#[source] GbmError), MapDmaBuf(#[source] AllocatorError),
#[error("Could not import dmabuf")] #[error("Could not import dmabuf")]
ImportDmaBuf(#[source] GbmError), ImportDmaBuf(#[source] AllocatorError),
#[error("Could not create a gbm device")]
CreateGbmDevice(#[source] GbmError),
#[error("Could not retrieve the render node path")]
GetRenderNode(#[source] DrmError),
#[error("Drm device does not have a render node")]
NoRenderNode,
} }
impl From<TestGfxError> for GfxError { impl From<TestGfxError> for GfxError {
@ -51,19 +41,11 @@ impl From<TestGfxError> for GfxError {
pub struct TestGfxCtx { pub struct TestGfxCtx {
formats: Rc<AHashMap<u32, GfxFormat>>, formats: Rc<AHashMap<u32, GfxFormat>>,
sync_obj_ctx: Rc<SyncObjCtx>, allocator: Rc<dyn Allocator>,
gbm: GbmDevice,
render_node: Rc<CString>,
} }
impl TestGfxCtx { impl TestGfxCtx {
pub fn new(drm: &Drm) -> Result<Rc<Self>, GfxError> { pub fn new(allocator: Rc<dyn Allocator>) -> Result<Rc<Self>, GfxError> {
let render_node = drm
.get_render_node()
.map_err(TestGfxError::GetRenderNode)?
.ok_or(TestGfxError::NoRenderNode)?;
let gbm = GbmDevice::new(drm).map_err(TestGfxError::CreateGbmDevice)?;
let ctx = Rc::new(SyncObjCtx::new(drm.fd()));
let mut modifiers = IndexSet::new(); let mut modifiers = IndexSet::new();
modifiers.insert(LINEAR_MODIFIER); modifiers.insert(LINEAR_MODIFIER);
let mut formats = AHashMap::new(); let mut formats = AHashMap::new();
@ -79,9 +61,7 @@ impl TestGfxCtx {
} }
Ok(Rc::new(Self { Ok(Rc::new(Self {
formats: Rc::new(formats), formats: Rc::new(formats),
sync_obj_ctx: ctx, allocator,
gbm,
render_node: Rc::new(render_node),
})) }))
} }
} }
@ -97,8 +77,8 @@ impl GfxContext for TestGfxCtx {
None None
} }
fn render_node(&self) -> Rc<CString> { fn render_node(&self) -> Option<Rc<CString>> {
self.render_node.clone() None
} }
fn formats(&self) -> Rc<AHashMap<u32, GfxFormat>> { fn formats(&self) -> Rc<AHashMap<u32, GfxFormat>> {
@ -109,9 +89,8 @@ impl GfxContext for TestGfxCtx {
Ok(Rc::new(TestGfxImage::DmaBuf(TestDmaBufGfxImage { Ok(Rc::new(TestGfxImage::DmaBuf(TestDmaBufGfxImage {
buf: buf.clone(), buf: buf.clone(),
bo: self bo: self
.gbm .allocator
.import_dmabuf(buf, 0) .import_dmabuf(buf, BufferUsage::none())
.map(Rc::new)
.map_err(TestGfxError::ImportDmaBuf)?, .map_err(TestGfxError::ImportDmaBuf)?,
}))) })))
} }
@ -142,8 +121,8 @@ impl GfxContext for TestGfxCtx {
}))) })))
} }
fn gbm(&self) -> &GbmDevice { fn allocator(&self) -> Rc<dyn Allocator> {
&self.gbm self.allocator.clone()
} }
fn gfx_api(&self) -> GfxApi { fn gfx_api(&self) -> GfxApi {
@ -170,8 +149,8 @@ impl GfxContext for TestGfxCtx {
})) }))
} }
fn sync_obj_ctx(&self) -> &Rc<SyncObjCtx> { fn sync_obj_ctx(&self) -> Option<&Rc<SyncObjCtx>> {
&self.sync_obj_ctx None
} }
} }
@ -195,7 +174,7 @@ struct TestShmGfxImage {
struct TestDmaBufGfxImage { struct TestDmaBufGfxImage {
buf: DmaBuf, buf: DmaBuf,
bo: Rc<GbmBo>, bo: Rc<dyn BufferObject>,
} }
impl TestGfxImage { impl TestGfxImage {
@ -242,7 +221,7 @@ impl TestGfxImage {
); );
} }
TestGfxImage::DmaBuf(d) => { TestGfxImage::DmaBuf(d) => {
let map = d.bo.map_read().map_err(TestGfxError::MapDmaBuf)?; let map = d.bo.clone().map_read().map_err(TestGfxError::MapDmaBuf)?;
unsafe { unsafe {
copy( copy(
map.stride(), map.stride(),
@ -478,7 +457,7 @@ impl GfxFramebuffer for TestGfxFb {
s.format, s.format,
), ),
TestGfxImage::DmaBuf(d) => { TestGfxImage::DmaBuf(d) => {
let map = d.bo.map_read().map_err(TestGfxError::MapDmaBuf)?; let map = d.bo.clone().map_read().map_err(TestGfxError::MapDmaBuf)?;
copy( copy(
map.data_ptr(), map.data_ptr(),
d.buf.width, d.buf.width,
@ -511,7 +490,7 @@ impl GfxFramebuffer for TestGfxFb {
s.format, s.format,
)?, )?,
TestGfxImage::DmaBuf(d) => { TestGfxImage::DmaBuf(d) => {
let map = d.bo.map_write().map_err(TestGfxError::MapDmaBuf)?; let map = d.bo.clone().map_write().map_err(TestGfxError::MapDmaBuf)?;
apply( apply(
map.data_ptr(), map.data_ptr(),
d.buf.width, d.buf.width,

View file

@ -9,13 +9,14 @@ use {
testrun::ParseFull, testrun::ParseFull,
}, },
utils::{buffd::MsgParser, cell_ext::CellExt}, utils::{buffd::MsgParser, cell_ext::CellExt},
video::dmabuf::DmaBuf,
wire::{ wire::{
jay_compositor::{self, *}, jay_compositor::{self, *},
jay_screenshot::Dmabuf,
JayCompositorId, JayCompositorId,
}, },
}, },
std::{cell::Cell, rc::Rc}, std::{cell::Cell, rc::Rc},
uapi::OwnedFd,
}; };
pub struct TestJayCompositor { pub struct TestJayCompositor {
@ -49,10 +50,16 @@ impl TestJayCompositor {
Ok(()) Ok(())
} }
pub async fn take_screenshot(&self, include_cursor: bool) -> Result<Dmabuf, TestError> { pub async fn take_screenshot(
&self,
include_cursor: bool,
) -> Result<(DmaBuf, Option<Rc<OwnedFd>>), TestError> {
let js = Rc::new(TestJayScreenshot { let js = Rc::new(TestJayScreenshot {
id: self.tran.id(), id: self.tran.id(),
result: Cell::new(None), state: self.tran.run.state.clone(),
drm_dev: Default::default(),
planes: Default::default(),
result: Default::default(),
}); });
self.tran.send(TakeScreenshot2 { self.tran.send(TakeScreenshot2 {
self_id: self.id, self_id: self.id,
@ -62,7 +69,7 @@ impl TestJayCompositor {
self.tran.add_obj(js.clone())?; self.tran.add_obj(js.clone())?;
self.tran.sync().await; self.tran.sync().await;
match js.result.take() { match js.result.take() {
Some(Ok(res)) => Ok(res), Some(Ok(res)) => Ok((res, js.drm_dev.take())),
Some(Err(res)) => bail!("Compositor could not take a screenshot: {}", res), Some(Err(res)) => bail!("Compositor could not take a screenshot: {}", res),
None => bail!("Compositor did not send a screenshot"), None => bail!("Compositor did not send a screenshot"),
} }

View file

@ -165,7 +165,7 @@ impl TestRegistry {
get_jay_compositor, get_jay_compositor,
jay_compositor, jay_compositor,
jay_compositor, jay_compositor,
1, 6,
TestJayCompositor TestJayCompositor
); );
create_singleton!(get_compositor, compositor, wl_compositor, 6, TestCompositor); create_singleton!(get_compositor, compositor, wl_compositor, 6, TestCompositor);

View file

@ -1,21 +1,44 @@
use { use {
crate::{ crate::{
format::XRGB8888,
it::{test_error::TestError, test_object::TestObject, testrun::ParseFull}, it::{test_error::TestError, test_object::TestObject, testrun::ParseFull},
state::State,
utils::buffd::MsgParser, utils::buffd::MsgParser,
video::dmabuf::{DmaBuf, DmaBufPlane, PlaneVec},
wire::{jay_screenshot::*, JayScreenshotId}, wire::{jay_screenshot::*, JayScreenshotId},
}, },
std::cell::Cell, std::{
cell::{Cell, RefCell},
rc::Rc,
},
uapi::OwnedFd,
}; };
pub struct TestJayScreenshot { pub struct TestJayScreenshot {
pub id: JayScreenshotId, pub id: JayScreenshotId,
pub result: Cell<Option<Result<Dmabuf, String>>>, pub state: Rc<State>,
pub drm_dev: Cell<Option<Rc<OwnedFd>>>,
pub planes: RefCell<PlaneVec<DmaBufPlane>>,
pub result: Cell<Option<Result<DmaBuf, String>>>,
} }
impl TestJayScreenshot { impl TestJayScreenshot {
fn handle_dmabuf(&self, parser: MsgParser<'_, '_>) -> Result<(), TestError> { fn handle_dmabuf(&self, parser: MsgParser<'_, '_>) -> Result<(), TestError> {
let ev = Dmabuf::parse_full(parser)?; let ev = Dmabuf::parse_full(parser)?;
self.result.set(Some(Ok(ev))); let mut planes = PlaneVec::new();
planes.push(DmaBufPlane {
offset: ev.offset,
stride: ev.stride,
fd: ev.fd,
});
self.result.set(Some(Ok(DmaBuf {
id: self.state.dma_buf_ids.next(),
width: ev.width as _,
height: ev.height as _,
format: XRGB8888,
modifier: ((ev.modifier_hi as u64) << 32) | (ev.modifier_lo as u64),
planes,
})));
Ok(()) Ok(())
} }
@ -24,6 +47,35 @@ impl TestJayScreenshot {
self.result.set(Some(Err(ev.msg.to_string()))); self.result.set(Some(Err(ev.msg.to_string())));
Ok(()) Ok(())
} }
fn handle_drm_dev(&self, parser: MsgParser<'_, '_>) -> Result<(), TestError> {
let ev = DrmDev::parse_full(parser)?;
self.drm_dev.set(Some(ev.drm_dev));
Ok(())
}
fn handle_plane(&self, parser: MsgParser<'_, '_>) -> Result<(), TestError> {
let ev = Plane::parse_full(parser)?;
self.planes.borrow_mut().push(DmaBufPlane {
offset: ev.offset,
stride: ev.stride,
fd: ev.fd,
});
Ok(())
}
fn handle_dmabuf2(&self, parser: MsgParser<'_, '_>) -> Result<(), TestError> {
let ev = Dmabuf2::parse_full(parser)?;
self.result.set(Some(Ok(DmaBuf {
id: self.state.dma_buf_ids.next(),
width: ev.width as _,
height: ev.height as _,
format: XRGB8888,
modifier: ev.modifier,
planes: self.planes.take(),
})));
Ok(())
}
} }
test_object! { test_object! {
@ -31,6 +83,9 @@ test_object! {
DMABUF => handle_dmabuf, DMABUF => handle_dmabuf,
ERROR => handle_error, ERROR => handle_error,
DRM_DEV => handle_drm_dev,
PLANE => handle_plane,
DMABUF2 => handle_dmabuf2,
} }
impl TestObject for TestJayScreenshot {} impl TestObject for TestJayScreenshot {}

View file

@ -79,7 +79,7 @@ impl TestRun {
let client = self.state.clients.get(client_id)?; let client = self.state.clients.get(client_id)?;
Ok(Rc::new(TestClient { Ok(Rc::new(TestClient {
run: self.clone(), run: self.clone(),
server: client, _server: client,
tran, tran,
jc, jc,
comp: registry.get_compositor().await?, comp: registry.get_compositor().await?,
@ -106,7 +106,14 @@ impl TestRun {
} }
pub async fn create_default_setup(&self) -> Result<DefaultSetup, TestError> { pub async fn create_default_setup(&self) -> Result<DefaultSetup, TestError> {
self.backend.install_default()?; self.create_default_setup2(true).await
}
pub async fn create_default_setup2(
&self,
prefer_udmabuf: bool,
) -> Result<DefaultSetup, TestError> {
self.backend.install_default2(prefer_udmabuf)?;
let seat = self.get_seat("default")?; let seat = self.get_seat("default")?;
self.state.eng.yield_now().await; self.state.eng.yield_now().await;
let output = match self.state.root.outputs.lock().values().next() { let output = match self.state.root.outputs.lock().values().next() {

View file

@ -10,7 +10,7 @@ async fn test(run: Rc<TestRun>) -> TestResult {
tassert!(!run.cfg.graphics_initialized.get()); tassert!(!run.cfg.graphics_initialized.get());
run.backend.install_render_context()?; run.backend.install_render_context(true)?;
tassert!(run.cfg.graphics_initialized.get()); tassert!(run.cfg.graphics_initialized.get());

View file

@ -11,7 +11,7 @@ use {
testcase!(); testcase!();
async fn test(run: Rc<TestRun>) -> TestResult { async fn test(run: Rc<TestRun>) -> TestResult {
let _ds = run.create_default_setup().await?; let _ds = run.create_default_setup2(false).await?;
struct Waiter(Cell<bool>); struct Waiter(Cell<bool>);
impl SyncObjWaiter for Waiter { impl SyncObjWaiter for Waiter {
@ -23,7 +23,11 @@ async fn test(run: Rc<TestRun>) -> TestResult {
let waiter = Rc::new(Waiter(Cell::new(false))); let waiter = Rc::new(Waiter(Cell::new(false)));
let eng = run.state.render_ctx.get().unwrap(); let eng = run.state.render_ctx.get().unwrap();
let syncobj = match eng.sync_obj_ctx().create_sync_obj() { let Some(ctx) = eng.sync_obj_ctx() else {
log::warn!("Cannot test explicit sync on this system: render context does not support sync objects");
return Ok(());
};
let syncobj = match ctx.create_sync_obj() {
Ok(s) => Rc::new(s), Ok(s) => Rc::new(s),
Err(e) => { Err(e) => {
log::warn!("Cannot test explicit sync on this system: {}", ErrorFmt(e)); log::warn!("Cannot test explicit sync on this system: {}", ErrorFmt(e));
@ -56,7 +60,7 @@ async fn test(run: Rc<TestRun>) -> TestResult {
client.sync().await; client.sync().await;
tassert_eq!(waiter.0.get(), false); tassert_eq!(waiter.0.get(), false);
eng.sync_obj_ctx().signal(&syncobj, SyncObjPoint(1))?; ctx.signal(&syncobj, SyncObjPoint(1))?;
client.sync().await; client.sync().await;
tassert_eq!(waiter.0.get(), true); tassert_eq!(waiter.0.get(), true);

View file

@ -12,7 +12,7 @@ use {
testcase!(); testcase!();
async fn test(run: Rc<TestRun>) -> TestResult { async fn test(run: Rc<TestRun>) -> TestResult {
let ds = run.create_default_setup().await?; let ds = run.create_default_setup2(false).await?;
let scanout_feedback = { let scanout_feedback = {
let Some(base_fb) = run.state.drm_feedback.get() else { let Some(base_fb) = run.state.drm_feedback.get() else {

View file

@ -43,6 +43,7 @@ mod macros;
#[macro_use] #[macro_use]
mod leaks; mod leaks;
mod acceptor; mod acceptor;
mod allocator;
mod async_engine; mod async_engine;
mod backend; mod backend;
mod backends; mod backends;
@ -91,6 +92,7 @@ mod time;
mod tools; mod tools;
mod tree; mod tree;
mod udev; mod udev;
mod udmabuf;
mod user_session; mod user_session;
mod utils; mod utils;
mod version; mod version;

View file

@ -1,5 +1,6 @@
use { use {
crate::{ crate::{
allocator::{BufferObject, BO_USE_RENDERING},
async_engine::{Phase, SpawnedFuture}, async_engine::{Phase, SpawnedFuture},
cursor::KnownCursor, cursor::KnownCursor,
fixed::Fixed, fixed::Fixed,
@ -15,7 +16,6 @@ use {
asyncevent::AsyncEvent, clonecell::CloneCell, copyhashmap::CopyHashMap, asyncevent::AsyncEvent, clonecell::CloneCell, copyhashmap::CopyHashMap,
errorfmt::ErrorFmt, hash_map_ext::HashMapExt, rc_eq::rc_eq, errorfmt::ErrorFmt, hash_map_ext::HashMapExt, rc_eq::rc_eq,
}, },
video::gbm::{GbmBo, GBM_BO_USE_RENDERING},
wire::{ wire::{
wp_fractional_scale_v1::PreferredScale, zwlr_layer_surface_v1::Configure, wp_fractional_scale_v1::PreferredScale, zwlr_layer_surface_v1::Configure,
ZwpLinuxBufferParamsV1Id, ZwpLinuxBufferParamsV1Id,
@ -722,14 +722,15 @@ impl WindowData {
log::error!("Render context cannot render to ARGB8888 format"); log::error!("Render context cannot render to ARGB8888 format");
return; return;
} }
let modifiers: Vec<_> = format.write_modifiers.iter().copied().collect();
for _ in 0..NUM_BUFFERS { for _ in 0..NUM_BUFFERS {
let bo = match ctx.ctx.gbm().create_bo( let bo = match ctx.ctx.allocator().create_bo(
&self.dpy.state.dma_buf_ids, &self.dpy.state.dma_buf_ids,
width, width,
height, height,
ARGB8888, ARGB8888,
&format.write_modifiers, &modifiers,
GBM_BO_USE_RENDERING, BO_USE_RENDERING,
) { ) {
Ok(b) => b, Ok(b) => b,
Err(e) => { Err(e) => {
@ -844,13 +845,13 @@ pub struct GuiBuffer {
pub wl: Rc<UsrWlBuffer>, pub wl: Rc<UsrWlBuffer>,
pub window: Rc<WindowData>, pub window: Rc<WindowData>,
pub fb: Rc<dyn GfxFramebuffer>, pub fb: Rc<dyn GfxFramebuffer>,
pub _bo: Option<GbmBo>, pub _bo: Option<Rc<dyn BufferObject>>,
pub free: Cell<bool>, pub free: Cell<bool>,
pub _size: (i32, i32), pub _size: (i32, i32),
} }
struct GuiBufferPending { struct GuiBufferPending {
pub bo: Cell<Option<GbmBo>>, pub bo: Cell<Option<Rc<dyn BufferObject>>>,
pub window: Rc<WindowData>, pub window: Rc<WindowData>,
pub fb: Rc<dyn GfxFramebuffer>, pub fb: Rc<dyn GfxFramebuffer>,
pub params: Rc<UsrLinuxBufferParams>, pub params: Rc<UsrLinuxBufferParams>,

View file

@ -1,14 +1,11 @@
use { use {
crate::{ crate::{
allocator::{AllocatorError, BufferObject, BO_USE_LINEAR, BO_USE_RENDERING},
format::XRGB8888, format::XRGB8888,
gfx_api::GfxError, gfx_api::GfxError,
scale::Scale, scale::Scale,
state::State, state::State,
video::{ video::{drm::DrmError, INVALID_MODIFIER, LINEAR_MODIFIER},
drm::DrmError,
gbm::{GbmBo, GbmError, GBM_BO_USE_LINEAR, GBM_BO_USE_RENDERING},
INVALID_MODIFIER, LINEAR_MODIFIER,
},
}, },
jay_config::video::Transform, jay_config::video::Transform,
std::{ops::Deref, rc::Rc}, std::{ops::Deref, rc::Rc},
@ -23,7 +20,7 @@ pub enum ScreenshooterError {
#[error("Display is empty")] #[error("Display is empty")]
EmptyDisplay, EmptyDisplay,
#[error(transparent)] #[error(transparent)]
GbmError(#[from] GbmError), AllocatorError(#[from] AllocatorError),
#[error(transparent)] #[error(transparent)]
RenderError(#[from] GfxError), RenderError(#[from] GfxError),
#[error(transparent)] #[error(transparent)]
@ -35,8 +32,8 @@ pub enum ScreenshooterError {
} }
pub struct Screenshot { pub struct Screenshot {
pub drm: Rc<OwnedFd>, pub drm: Option<Rc<OwnedFd>>,
pub bo: GbmBo, pub bo: Rc<dyn BufferObject>,
} }
pub fn take_screenshot( pub fn take_screenshot(
@ -52,18 +49,18 @@ pub fn take_screenshot(
return Err(ScreenshooterError::EmptyDisplay); return Err(ScreenshooterError::EmptyDisplay);
} }
let formats = ctx.formats(); let formats = ctx.formats();
let mut usage = GBM_BO_USE_RENDERING; let mut usage = BO_USE_RENDERING;
let modifiers = match formats.get(&XRGB8888.drm) { let modifiers = match formats.get(&XRGB8888.drm) {
None => return Err(ScreenshooterError::XRGB8888), None => return Err(ScreenshooterError::XRGB8888),
Some(f) if f.write_modifiers.contains(&LINEAR_MODIFIER) => &[LINEAR_MODIFIER], Some(f) if f.write_modifiers.contains(&LINEAR_MODIFIER) => &[LINEAR_MODIFIER],
Some(f) if f.write_modifiers.contains(&INVALID_MODIFIER) => { Some(f) if f.write_modifiers.contains(&INVALID_MODIFIER) => {
usage |= GBM_BO_USE_LINEAR; usage |= BO_USE_LINEAR;
&[INVALID_MODIFIER] &[INVALID_MODIFIER]
} }
Some(_) => return Err(ScreenshooterError::Linear), Some(_) => return Err(ScreenshooterError::Linear),
}; };
let gbm = ctx.gbm(); let allocator = ctx.allocator();
let bo = gbm.create_bo( let bo = allocator.create_bo(
&state.dma_buf_ids, &state.dma_buf_ids,
extents.width(), extents.width(),
extents.height(), extents.height(),
@ -83,6 +80,9 @@ pub fn take_screenshot(
false, false,
Transform::None, Transform::None,
)?; )?;
let drm = gbm.drm.dup_render()?.fd().clone(); let drm = match allocator.drm() {
Some(drm) => Some(drm.dup_render()?.fd().clone()),
_ => None,
};
Ok(Screenshot { drm, bo }) Ok(Screenshot { drm, bo })
} }

View file

@ -429,7 +429,7 @@ impl State {
self.cursors.set(None); self.cursors.set(None);
self.drm_feedback.set(None); self.drm_feedback.set(None);
self.wait_for_sync_obj self.wait_for_sync_obj
.set_ctx(ctx.as_ref().map(|c| c.sync_obj_ctx().clone())); .set_ctx(ctx.as_ref().and_then(|c| c.sync_obj_ctx().cloned()));
'handle_new_feedback: { 'handle_new_feedback: {
if let Some(ctx) = &ctx { if let Some(ctx) = &ctx {
@ -506,10 +506,12 @@ impl State {
if !self.render_ctx_ever_initialized.replace(true) { if !self.render_ctx_ever_initialized.replace(true) {
self.add_global(&Rc::new(WlDrmGlobal::new(self.globals.name()))); self.add_global(&Rc::new(WlDrmGlobal::new(self.globals.name())));
self.add_global(&Rc::new(ZwpLinuxDmabufV1Global::new(self.globals.name()))); self.add_global(&Rc::new(ZwpLinuxDmabufV1Global::new(self.globals.name())));
if ctx.sync_obj_ctx().supports_async_wait() && self.explicit_sync_enabled.get() { if let Some(ctx) = ctx.sync_obj_ctx() {
self.add_global(&Rc::new(WpLinuxDrmSyncobjManagerV1Global::new( if ctx.supports_async_wait() && self.explicit_sync_enabled.get() {
self.globals.name(), self.add_global(&Rc::new(WpLinuxDrmSyncobjManagerV1Global::new(
))); self.globals.name(),
)));
}
} }
if let Some(config) = self.config.get() { if let Some(config) = self.config.get() {
config.graphics_initialized(); config.graphics_initialized();
@ -1042,7 +1044,11 @@ impl State {
log::error!("Cannot signal sync obj point because there is no render context"); log::error!("Cannot signal sync obj point because there is no render context");
return; return;
}; };
if let Err(e) = ctx.sync_obj_ctx().signal(sync_obj, point) { let Some(ctx) = ctx.sync_obj_ctx() else {
log::error!("Cannot signal sync obj point because there is no syncobj context");
return;
};
if let Err(e) = ctx.signal(sync_obj, point) {
log::error!("Could not signal sync obj: {}", ErrorFmt(e)); log::error!("Could not signal sync obj: {}", ErrorFmt(e));
} }
} }

271
src/udmabuf.rs Normal file
View file

@ -0,0 +1,271 @@
use {
crate::{
allocator::{Allocator, AllocatorError, BufferObject, BufferUsage, MappedBuffer},
format::Format,
utils::{oserror::OsError, page_size::page_size},
video::{
dmabuf::{DmaBuf, DmaBufIds, DmaBufPlane, PlaneVec},
drm::Drm,
Modifier, LINEAR_MODIFIER,
},
},
std::{ptr, rc::Rc},
thiserror::Error,
uapi::{
c,
c::{
ioctl, mmap, munmap, F_SEAL_SHRINK, MAP_SHARED, MFD_ALLOW_SEALING, O_RDONLY, PROT_READ,
PROT_WRITE,
},
map_err, open, OwnedFd, _IOW,
},
};
#[derive(Debug, Error)]
pub enum UdmabufError {
#[error("Could not open /dev/udmabuf")]
Open(#[source] OsError),
#[error("Only the linear modifier can be allocated")]
Modifier,
#[error("Format {0} is not supported")]
Format(&'static str),
#[error("Could not create a memfd")]
Memfd(#[source] OsError),
#[error("Size calculation overflowed")]
Overflow,
#[error("Could not resize the memfd")]
Truncate(#[source] OsError),
#[error("Could not seal the memfd")]
Seal(#[source] OsError),
#[error("Could not create a dmabuf")]
CreateDmabuf(#[source] OsError),
#[error("Only a single plane is supported")]
Planes,
#[error("Stride is invalid")]
Stride,
#[error("Could not stat the dmabuf")]
Stat(#[source] OsError),
#[error("Dmabuf is too small for required size")]
Size,
#[error("Could not map dmabuf")]
Map(#[source] OsError),
}
pub struct Udmabuf {
fd: OwnedFd,
}
impl Udmabuf {
pub fn new() -> Result<Self, UdmabufError> {
let fd = match open("/dev/udmabuf", O_RDONLY, 0) {
Ok(b) => b,
Err(e) => return Err(UdmabufError::Open(e.into())),
};
Ok(Self { fd })
}
}
impl Allocator for Udmabuf {
fn drm(&self) -> Option<&Drm> {
None
}
fn create_bo(
&self,
dma_buf_ids: &DmaBufIds,
width: i32,
height: i32,
format: &'static Format,
modifiers: &[Modifier],
_usage: BufferUsage,
) -> Result<Rc<dyn BufferObject>, AllocatorError> {
if !modifiers.contains(&LINEAR_MODIFIER) {
return Err(UdmabufError::Modifier.into());
}
let Some(shm_info) = &format.shm_info else {
return Err(UdmabufError::Format(format.name).into());
};
let height = height as u64;
let width = width as u64;
if height > 1 << 16 || width > 1 << 16 {
return Err(UdmabufError::Overflow.into());
}
let stride_mask = 255;
let stride = (width * shm_info.bpp as u64 + stride_mask) & !stride_mask;
let size_mask = page_size() as u64 - 1;
let size = (height * stride + size_mask) & !size_mask;
let memfd = match uapi::memfd_create("udmabuf", MFD_ALLOW_SEALING) {
Ok(f) => f,
Err(e) => return Err(UdmabufError::Memfd(e.into()).into()),
};
if let Err(e) = uapi::ftruncate(memfd.raw(), size as _) {
return Err(UdmabufError::Truncate(e.into()).into());
}
if let Err(e) = uapi::fcntl_add_seals(memfd.raw(), F_SEAL_SHRINK) {
return Err(UdmabufError::Seal(e.into()).into());
}
let mut cmd = udmabuf_create {
memfd: memfd.raw() as u32,
flags: 0,
offset: 0,
size: size as u64,
};
let dmabuf = unsafe { ioctl(self.fd.raw(), UDMABUF_CREATE, &mut cmd) };
let dmabuf = match map_err!(dmabuf) {
Ok(d) => OwnedFd::new(d),
Err(e) => return Err(UdmabufError::CreateDmabuf(e.into()).into()),
};
let mut planes = PlaneVec::new();
planes.push(DmaBufPlane {
offset: 0,
stride: stride as _,
fd: Rc::new(dmabuf),
});
let dmabuf = DmaBuf {
id: dma_buf_ids.next(),
width: width as _,
height: height as _,
format,
modifier: LINEAR_MODIFIER,
planes,
};
Ok(Rc::new(UdmabufBo {
buf: dmabuf,
size: size as _,
}))
}
fn import_dmabuf(
&self,
dmabuf: &DmaBuf,
_usage: BufferUsage,
) -> Result<Rc<dyn BufferObject>, AllocatorError> {
if dmabuf.planes.len() != 1 {
return Err(UdmabufError::Planes.into());
}
if dmabuf.modifier != LINEAR_MODIFIER {
return Err(UdmabufError::Modifier.into());
}
let plane = &dmabuf.planes[0];
let Some(shm_info) = &dmabuf.format.shm_info else {
return Err(UdmabufError::Format(dmabuf.format.name).into());
};
let height = dmabuf.height as u64;
let width = dmabuf.width as u64;
let stride = plane.stride as u64;
let offset = plane.offset as u64;
if height > 1 << 16 || width > 1 << 16 {
return Err(UdmabufError::Overflow.into());
}
if stride < width * shm_info.bpp as u64 {
return Err(UdmabufError::Stride.into());
}
let size = offset + stride * height;
if usize::try_from(size).is_err() {
return Err(UdmabufError::Overflow.into());
}
let stat = match uapi::fstat(plane.fd.raw()) {
Ok(s) => s,
Err(e) => return Err(UdmabufError::Stat(e.into()).into()),
};
if (stat.st_size as u64) < size {
return Err(UdmabufError::Size.into());
}
Ok(Rc::new(UdmabufBo {
buf: dmabuf.clone(),
size: size as usize,
}))
}
}
struct UdmabufBo {
buf: DmaBuf,
size: usize,
}
impl BufferObject for UdmabufBo {
fn dmabuf(&self) -> &DmaBuf {
&self.buf
}
fn map_read(self: Rc<Self>) -> Result<Box<dyn MappedBuffer>, AllocatorError> {
self.map_write()
}
fn map_write(self: Rc<Self>) -> Result<Box<dyn MappedBuffer>, AllocatorError> {
let plane = &self.buf.planes[0];
unsafe {
let res = mmap(
ptr::null_mut(),
self.size,
PROT_READ | PROT_WRITE,
MAP_SHARED,
plane.fd.raw(),
0,
);
if res == c::MAP_FAILED {
return Err(UdmabufError::Map(OsError::default()).into());
}
let offset = plane.offset as _;
let data =
std::slice::from_raw_parts_mut((res as *mut u8).add(offset), self.size - offset);
Ok(Box::new(UdmabufMap {
data,
stride: plane.stride as _,
ptr: res,
len: self.size,
_bo: self,
}))
}
}
}
struct UdmabufMap {
_bo: Rc<UdmabufBo>,
data: *mut [u8],
stride: i32,
ptr: *mut c::c_void,
len: usize,
}
impl Drop for UdmabufMap {
fn drop(&mut self) {
unsafe {
let res = munmap(self.ptr, self.len);
if let Err(e) = map_err!(res) {
log::error!("Could not unmap udmabuf: {}", OsError::from(e));
}
}
}
}
impl MappedBuffer for UdmabufMap {
unsafe fn data(&self) -> &[u8] {
&*self.data
}
fn data_ptr(&self) -> *mut u8 {
self.data as _
}
fn stride(&self) -> i32 {
self.stride
}
}
impl From<UdmabufError> for AllocatorError {
fn from(value: UdmabufError) -> Self {
Self(Box::new(value))
}
}
#[allow(non_camel_case_types)]
#[repr(C)]
struct udmabuf_create {
memfd: u32,
flags: u32,
offset: u64,
size: u64,
}
const UDMABUF_CREATE: u64 = _IOW::<udmabuf_create>(b'u' as u64, 0x42);

View file

@ -2,6 +2,10 @@
use { use {
crate::{ crate::{
allocator::{
Allocator, AllocatorError, BufferObject, BufferUsage, MappedBuffer, BO_USE_CURSOR,
BO_USE_LINEAR, BO_USE_PROTECTED, BO_USE_RENDERING, BO_USE_SCANOUT, BO_USE_WRITE,
},
format::{formats, Format}, format::{formats, Format},
utils::oserror::OsError, utils::oserror::OsError,
video::{ video::{
@ -38,6 +42,12 @@ pub enum GbmError {
NoModifier, NoModifier,
} }
impl From<GbmError> for AllocatorError {
fn from(value: GbmError) -> Self {
Self(Box::new(value))
}
}
pub type Device = u8; pub type Device = u8;
type Bo = u8; type Bo = u8;
@ -151,17 +161,16 @@ pub struct GbmBoMap {
stride: i32, stride: i32,
} }
impl GbmBoMap { impl MappedBuffer for GbmBoMap {
pub unsafe fn data(&self) -> &[u8] { unsafe fn data(&self) -> &[u8] {
&*self.data &*self.data
} }
#[cfg_attr(not(feature = "it"), allow(dead_code))] fn data_ptr(&self) -> *mut u8 {
pub fn data_ptr(&self) -> *mut u8 {
self.data as _ self.data as _
} }
pub fn stride(&self) -> i32 { fn stride(&self) -> i32 {
self.stride self.stride
} }
} }
@ -290,6 +299,56 @@ impl GbmDevice {
} }
} }
impl Allocator for GbmDevice {
fn drm(&self) -> Option<&Drm> {
Some(&self.drm)
}
fn create_bo(
&self,
dma_buf_ids: &DmaBufIds,
width: i32,
height: i32,
format: &'static Format,
modifiers: &[Modifier],
usage: BufferUsage,
) -> Result<Rc<dyn BufferObject>, AllocatorError> {
let usage = map_usage(usage);
self.create_bo(dma_buf_ids, width, height, format, modifiers, usage)
.map(|v| Rc::new(v) as _)
.map_err(|v| v.into())
}
fn import_dmabuf(
&self,
dmabuf: &DmaBuf,
usage: BufferUsage,
) -> Result<Rc<dyn BufferObject>, AllocatorError> {
let usage = map_usage(usage);
self.import_dmabuf(dmabuf, usage)
.map(|v| Rc::new(v) as _)
.map_err(|v| v.into())
}
}
fn map_usage(usage: BufferUsage) -> u32 {
let mut gbm = 0;
macro_rules! map {
($bu:ident to $gbu:ident) => {
if usage.contains($bu) {
gbm |= $gbu;
}
};
}
map!(BO_USE_SCANOUT to GBM_BO_USE_SCANOUT);
map!(BO_USE_CURSOR to GBM_BO_USE_CURSOR);
map!(BO_USE_RENDERING to GBM_BO_USE_RENDERING);
map!(BO_USE_WRITE to GBM_BO_USE_WRITE);
map!(BO_USE_LINEAR to GBM_BO_USE_LINEAR);
map!(BO_USE_PROTECTED to GBM_BO_USE_PROTECTED);
gbm
}
impl Drop for GbmDevice { impl Drop for GbmDevice {
fn drop(&mut self) { fn drop(&mut self) {
unsafe { unsafe {
@ -299,15 +358,10 @@ impl Drop for GbmDevice {
} }
impl GbmBo { impl GbmBo {
pub fn dmabuf(&self) -> &DmaBuf {
&self.dmabuf
}
pub fn map_read(self: &Rc<Self>) -> Result<GbmBoMap, GbmError> { pub fn map_read(self: &Rc<Self>) -> Result<GbmBoMap, GbmError> {
self.map2(GBM_BO_TRANSFER_READ) self.map2(GBM_BO_TRANSFER_READ)
} }
#[cfg_attr(not(feature = "it"), allow(dead_code))]
pub fn map_write(self: &Rc<Self>) -> Result<GbmBoMap, GbmError> { pub fn map_write(self: &Rc<Self>) -> Result<GbmBoMap, GbmError> {
self.map2(GBM_BO_TRANSFER_READ_WRITE) self.map2(GBM_BO_TRANSFER_READ_WRITE)
} }
@ -340,6 +394,24 @@ impl GbmBo {
} }
} }
impl BufferObject for GbmBo {
fn dmabuf(&self) -> &DmaBuf {
&self.dmabuf
}
fn map_read(self: Rc<Self>) -> Result<Box<dyn MappedBuffer>, AllocatorError> {
GbmBo::map_read(&self)
.map(|v| Box::new(v) as _)
.map_err(|v| v.into())
}
fn map_write(self: Rc<Self>) -> Result<Box<dyn MappedBuffer>, AllocatorError> {
GbmBo::map_write(&self)
.map(|v| Box::new(v) as _)
.map_err(|v| v.into())
}
}
impl Drop for GbmBoMap { impl Drop for GbmBoMap {
fn drop(&mut self) { fn drop(&mut self) {
unsafe { unsafe {

View file

@ -14,3 +14,19 @@ event dmabuf {
event error { event error {
msg: str, msg: str,
} }
event drm_dev (since = 6) {
drm_dev: fd,
}
event plane (since = 6) {
fd: fd,
offset: u32,
stride: u32,
}
event dmabuf2 (since = 6) {
width: i32,
height: i32,
modifier: pod(u64),
}