Merge pull request #246 from mahkoh/jorth/udmabuf
video: add udmabuf allocator
This commit is contained in:
commit
952bd31f48
33 changed files with 883 additions and 256 deletions
|
|
@ -10,6 +10,7 @@ tasks:
|
|||
sudo rmmod bochs
|
||||
sudo modprobe vkms
|
||||
sudo chmod o+rw /dev/dri/card*
|
||||
sudo chmod o+r /dev/udmabuf
|
||||
- build: |
|
||||
cd jay
|
||||
cargo build --features it
|
||||
|
|
|
|||
57
src/allocator.rs
Normal file
57
src/allocator.rs
Normal 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;
|
||||
}
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
use {
|
||||
crate::{
|
||||
allocator::BufferObject,
|
||||
async_engine::{Phase, SpawnedFuture},
|
||||
backend::{
|
||||
BackendDrmDevice, BackendDrmLease, BackendDrmLessee, BackendEvent, Connector,
|
||||
|
|
@ -73,6 +74,7 @@ pub struct PendingDrmDevice {
|
|||
pub struct MetalRenderContext {
|
||||
pub dev_id: DrmDeviceId,
|
||||
pub gfx: Rc<dyn GfxContext>,
|
||||
pub gbm: Rc<GbmDevice>,
|
||||
}
|
||||
|
||||
pub struct MetalDrmDevice {
|
||||
|
|
@ -91,7 +93,7 @@ pub struct MetalDrmDevice {
|
|||
pub cursor_width: u64,
|
||||
pub cursor_height: u64,
|
||||
pub supports_async_commit: bool,
|
||||
pub gbm: GbmDevice,
|
||||
pub gbm: Rc<GbmDevice>,
|
||||
pub handle_events: HandleEvents,
|
||||
pub ctx: CloneCell<Rc<MetalRenderContext>>,
|
||||
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) {
|
||||
Ok(r) => r,
|
||||
Err(e) => return Err(MetalError::CreateRenderContex(e)),
|
||||
|
|
@ -2169,13 +2176,9 @@ impl MetalBackend {
|
|||
let ctx = Rc::new(MetalRenderContext {
|
||||
dev_id: pending.id,
|
||||
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_amd = false;
|
||||
match gbm.drm.version() {
|
||||
|
|
@ -2547,7 +2550,8 @@ impl MetalBackend {
|
|||
}
|
||||
|
||||
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;
|
||||
}
|
||||
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_id: dev.id,
|
||||
gfx,
|
||||
gbm: old_ctx.gbm.clone(),
|
||||
}));
|
||||
if dev.is_render_device() {
|
||||
self.make_render_device(dev, true);
|
||||
|
|
@ -2836,7 +2841,7 @@ impl MetalBackend {
|
|||
return Err(MetalError::MissingRenderModifier(format.name));
|
||||
}
|
||||
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,
|
||||
width,
|
||||
height,
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
use {
|
||||
crate::{
|
||||
allocator::BufferObject,
|
||||
async_engine::{Phase, SpawnedFuture},
|
||||
backend::{
|
||||
AxisSource, Backend, BackendDrmDevice, BackendEvent, Connector, ConnectorEvent,
|
||||
|
|
|
|||
|
|
@ -1,23 +1,30 @@
|
|||
use {
|
||||
crate::{
|
||||
allocator::{Allocator, AllocatorError, BO_USE_LINEAR, BO_USE_RENDERING},
|
||||
cli::{GlobalArgs, ScreenshotArgs, ScreenshotFormat},
|
||||
format::XRGB8888,
|
||||
tools::tool_client::{with_tool_client, Handle, ToolClient},
|
||||
udmabuf::{Udmabuf, UdmabufError},
|
||||
utils::{errorfmt::ErrorFmt, queue::AsyncQueue, windows::WindowsExt},
|
||||
video::{
|
||||
dmabuf::{DmaBuf, DmaBufIds, DmaBufPlane, PlaneVec},
|
||||
drm::Drm,
|
||||
gbm::{GbmDevice, GBM_BO_USE_LINEAR, GBM_BO_USE_RENDERING},
|
||||
drm::{Drm, DrmError},
|
||||
gbm::{GbmDevice, GbmError},
|
||||
},
|
||||
wire::{
|
||||
jay_compositor::TakeScreenshot,
|
||||
jay_screenshot::{Dmabuf, Error},
|
||||
jay_screenshot::{Dmabuf, Dmabuf2, DrmDev, Error, Plane},
|
||||
},
|
||||
},
|
||||
chrono::Local,
|
||||
jay_algorithms::qoi::xrgb8888_encode_qoi,
|
||||
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) {
|
||||
|
|
@ -47,17 +54,62 @@ async fn run(screenshot: Rc<Screenshot>) {
|
|||
Error::handle(tc, sid, result.clone(), |res, err| {
|
||||
res.push(Err(err.msg.to_owned()));
|
||||
});
|
||||
Dmabuf::handle(tc, sid, result.clone(), |res, buf| {
|
||||
res.push(Ok(buf));
|
||||
Dmabuf::handle(tc, sid, result.clone(), |res, ev| {
|
||||
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,
|
||||
Err(e) => {
|
||||
fatal!("Could not take a screenshot: {}", e);
|
||||
}
|
||||
};
|
||||
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 {
|
||||
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> {
|
||||
let drm = match Drm::reopen(buf.drm_dev.raw(), false) {
|
||||
Ok(drm) => drm,
|
||||
Err(e) => {
|
||||
fatal!("Could not open the drm device: {}", ErrorFmt(e));
|
||||
}
|
||||
};
|
||||
let gbm = match GbmDevice::new(&drm) {
|
||||
Ok(g) => g,
|
||||
Err(e) => {
|
||||
fatal!("Could not create a gbm device: {}", ErrorFmt(e));
|
||||
}
|
||||
};
|
||||
let mut planes = PlaneVec::new();
|
||||
planes.push(DmaBufPlane {
|
||||
offset: buf.offset,
|
||||
stride: buf.stride,
|
||||
fd: buf.fd.clone(),
|
||||
});
|
||||
let dmabuf = DmaBuf {
|
||||
id: dma_buf_ids.next(),
|
||||
width: buf.width as _,
|
||||
height: buf.height as _,
|
||||
format: XRGB8888,
|
||||
modifier: (buf.modifier_hi as u64) << 32 | (buf.modifier_lo as u64),
|
||||
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));
|
||||
#[derive(Debug, Error)]
|
||||
pub enum ScreenshotError {
|
||||
#[error("Could not open the drm device")]
|
||||
OpenDrmDevice(#[source] DrmError),
|
||||
#[error("Could not create a gbm device")]
|
||||
CreateGbmDevice(#[source] GbmError),
|
||||
#[error("Could not create a udmabuf allocator")]
|
||||
CreateUdmabuf(#[source] UdmabufError),
|
||||
#[error("Could not import a dmabuf")]
|
||||
ImportDmabuf(#[source] AllocatorError),
|
||||
#[error("Could not map a dmabuf")]
|
||||
MapDmabuf(#[source] AllocatorError),
|
||||
}
|
||||
|
||||
pub fn buf_to_bytes(
|
||||
drm_dev: Option<&Rc<OwnedFd>>,
|
||||
buf: &DmaBuf,
|
||||
format: ScreenshotFormat,
|
||||
) -> Result<Vec<u8>, ScreenshotError> {
|
||||
let allocator: Rc<dyn Allocator> = match drm_dev {
|
||||
Some(drm_dev) => {
|
||||
let drm = Drm::reopen(drm_dev.raw(), false).map_err(ScreenshotError::OpenDrmDevice)?;
|
||||
GbmDevice::new(&drm)
|
||||
.map(Rc::new)
|
||||
.map_err(ScreenshotError::CreateGbmDevice)?
|
||||
}
|
||||
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() };
|
||||
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![];
|
||||
|
|
@ -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])
|
||||
}
|
||||
}
|
||||
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_depth(BitDepth::Eight);
|
||||
encoder.set_srgb(SrgbRenderingIntent::Perceptual);
|
||||
let mut writer = encoder.write_header().unwrap();
|
||||
writer.write_image_data(&image_data).unwrap();
|
||||
}
|
||||
out
|
||||
Ok(out)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -36,9 +36,11 @@ impl DrmFeedback {
|
|||
ids: &DrmFeedbackIds,
|
||||
render_ctx: &dyn GfxContext,
|
||||
) -> Result<Self, DrmFeedbackError> {
|
||||
let main_device = uapi::fstat(render_ctx.gbm().drm.raw())
|
||||
.map_err(OsError::from)?
|
||||
.st_rdev;
|
||||
let drm = match render_ctx.allocator().drm() {
|
||||
Some(drm) => drm.raw(),
|
||||
_ => 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 mut memfd =
|
||||
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 {
|
||||
#[error("Could not stat drm device")]
|
||||
Stat(#[from] OsError),
|
||||
#[error("Graphics API does not have a DRM device")]
|
||||
NoDrmDevice,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
use {
|
||||
crate::{
|
||||
allocator::Allocator,
|
||||
cursor::Cursor,
|
||||
damage::DamageVisualizer,
|
||||
fixed::Fixed,
|
||||
|
|
@ -11,7 +12,7 @@ use {
|
|||
theme::Color,
|
||||
tree::{Node, OutputNode},
|
||||
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,
|
||||
indexmap::IndexSet,
|
||||
|
|
@ -533,7 +534,7 @@ pub trait GfxTexture: Debug {
|
|||
pub trait GfxContext: Debug {
|
||||
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>>;
|
||||
|
||||
|
|
@ -554,7 +555,7 @@ pub trait GfxContext: Debug {
|
|||
damage: Option<&[Rect]>,
|
||||
) -> Result<Rc<dyn GfxTexture>, GfxError>;
|
||||
|
||||
fn gbm(&self) -> &GbmDevice;
|
||||
fn allocator(&self) -> Rc<dyn Allocator>;
|
||||
|
||||
fn gfx_api(&self) -> GfxApi;
|
||||
|
||||
|
|
@ -566,7 +567,7 @@ pub trait GfxContext: Debug {
|
|||
format: &'static Format,
|
||||
) -> Result<Rc<dyn GfxFramebuffer>, GfxError>;
|
||||
|
||||
fn sync_obj_ctx(&self) -> &Rc<SyncObjCtx>;
|
||||
fn sync_obj_ctx(&self) -> Option<&Rc<SyncObjCtx>>;
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
use {
|
||||
crate::{
|
||||
allocator::Allocator,
|
||||
format::{Format, XRGB8888},
|
||||
gfx_api::{
|
||||
BufferResvUser, GfxApiOpt, GfxContext, GfxError, GfxFormat, GfxFramebuffer, GfxImage,
|
||||
|
|
@ -240,8 +241,8 @@ impl GfxContext for GlRenderContext {
|
|||
self.reset_status()
|
||||
}
|
||||
|
||||
fn render_node(&self) -> Rc<CString> {
|
||||
self.render_node()
|
||||
fn render_node(&self) -> Option<Rc<CString>> {
|
||||
Some(self.render_node())
|
||||
}
|
||||
|
||||
fn formats(&self) -> Rc<AHashMap<u32, GfxFormat>> {
|
||||
|
|
@ -278,8 +279,8 @@ impl GfxContext for GlRenderContext {
|
|||
.map_err(|e| e.into())
|
||||
}
|
||||
|
||||
fn gbm(&self) -> &GbmDevice {
|
||||
&self.gbm
|
||||
fn allocator(&self) -> Rc<dyn Allocator> {
|
||||
self.gbm.clone()
|
||||
}
|
||||
|
||||
fn gfx_api(&self) -> GfxApi {
|
||||
|
|
@ -299,7 +300,7 @@ impl GfxContext for GlRenderContext {
|
|||
Ok(Rc::new(Framebuffer { ctx: self, gl: fb }))
|
||||
}
|
||||
|
||||
fn sync_obj_ctx(&self) -> &Rc<SyncObjCtx> {
|
||||
&self.sync_ctx
|
||||
fn sync_obj_ctx(&self) -> Option<&Rc<SyncObjCtx>> {
|
||||
Some(&self.sync_ctx)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ mod util;
|
|||
|
||||
use {
|
||||
crate::{
|
||||
allocator::Allocator,
|
||||
async_engine::AsyncEngine,
|
||||
format::Format,
|
||||
gfx_api::{
|
||||
|
|
@ -31,7 +32,7 @@ use {
|
|||
video::{
|
||||
dmabuf::DmaBuf,
|
||||
drm::{sync_obj::SyncObjCtx, Drm, DrmError},
|
||||
gbm::{GbmDevice, GbmError},
|
||||
gbm::GbmError,
|
||||
},
|
||||
},
|
||||
ahash::AHashMap,
|
||||
|
|
@ -209,8 +210,8 @@ impl GfxContext for Context {
|
|||
None
|
||||
}
|
||||
|
||||
fn render_node(&self) -> Rc<CString> {
|
||||
self.0.device.render_node.clone()
|
||||
fn render_node(&self) -> Option<Rc<CString>> {
|
||||
Some(self.0.device.render_node.clone())
|
||||
}
|
||||
|
||||
fn formats(&self) -> Rc<AHashMap<u32, GfxFormat>> {
|
||||
|
|
@ -255,8 +256,8 @@ impl GfxContext for Context {
|
|||
Ok(tex as _)
|
||||
}
|
||||
|
||||
fn gbm(&self) -> &GbmDevice {
|
||||
&self.0.device.gbm
|
||||
fn allocator(&self) -> Rc<dyn Allocator> {
|
||||
self.0.device.gbm.clone()
|
||||
}
|
||||
|
||||
fn gfx_api(&self) -> GfxApi {
|
||||
|
|
@ -276,8 +277,8 @@ impl GfxContext for Context {
|
|||
Ok(fb)
|
||||
}
|
||||
|
||||
fn sync_obj_ctx(&self) -> &Rc<SyncObjCtx> {
|
||||
&self.0.device.sync_ctx
|
||||
fn sync_obj_ctx(&self) -> Option<&Rc<SyncObjCtx>> {
|
||||
Some(&self.0.device.sync_ctx)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ use {
|
|||
pub struct VulkanDevice {
|
||||
pub(super) physical_device: PhysicalDevice,
|
||||
pub(super) render_node: Rc<CString>,
|
||||
pub(super) gbm: GbmDevice,
|
||||
pub(super) gbm: Rc<GbmDevice>,
|
||||
pub(super) sync_ctx: Rc<SyncObjCtx>,
|
||||
pub(super) instance: Rc<VulkanInstance>,
|
||||
pub(super) device: Device,
|
||||
|
|
@ -276,7 +276,7 @@ impl VulkanInstance {
|
|||
physical_device: phy_dev,
|
||||
render_node,
|
||||
sync_ctx: Rc::new(SyncObjCtx::new(gbm.drm.fd())),
|
||||
gbm,
|
||||
gbm: Rc::new(gbm),
|
||||
instance: self.clone(),
|
||||
device,
|
||||
external_memory_fd,
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ use {
|
|||
};
|
||||
|
||||
pub const CREATE_EI_SESSION_SINCE: Version = Version(5);
|
||||
pub const SCREENSHOT_SPLITUP_SINCE: Version = Version(6);
|
||||
|
||||
pub struct JayCompositorGlobal {
|
||||
name: GlobalName,
|
||||
|
|
@ -69,7 +70,7 @@ impl Global for JayCompositorGlobal {
|
|||
}
|
||||
|
||||
fn version(&self) -> u32 {
|
||||
5
|
||||
6
|
||||
}
|
||||
|
||||
fn required_caps(&self) -> ClientCaps {
|
||||
|
|
@ -117,16 +118,30 @@ impl JayCompositor {
|
|||
match take_screenshot(&self.client.state, include_cursor) {
|
||||
Ok(s) => {
|
||||
let dmabuf = s.bo.dmabuf();
|
||||
let plane = &dmabuf.planes[0];
|
||||
ss.send_dmabuf(
|
||||
&s.drm,
|
||||
&plane.fd,
|
||||
dmabuf.width,
|
||||
dmabuf.height,
|
||||
plane.offset,
|
||||
plane.stride,
|
||||
dmabuf.modifier,
|
||||
);
|
||||
if self.version < SCREENSHOT_SPLITUP_SINCE {
|
||||
if let Some(drm) = &s.drm {
|
||||
let plane = &dmabuf.planes[0];
|
||||
ss.send_dmabuf(
|
||||
drm,
|
||||
&plane.fd,
|
||||
dmabuf.width,
|
||||
dmabuf.height,
|
||||
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) => {
|
||||
let msg = ErrorFmt(e).to_string();
|
||||
|
|
|
|||
|
|
@ -21,10 +21,16 @@ impl JayRenderCtx {
|
|||
pub fn send_render_ctx(&self, ctx: Option<Rc<dyn GfxContext>>) {
|
||||
let mut fd = None;
|
||||
if let Some(ctx) = ctx {
|
||||
match ctx.gbm().drm.dup_render() {
|
||||
Ok(d) => fd = Some(d.fd().clone()),
|
||||
Err(e) => {
|
||||
log::error!("Could not dup drm fd: {}", ErrorFmt(e));
|
||||
let allocator = ctx.allocator();
|
||||
match allocator.drm() {
|
||||
Some(drm) => match drm.dup_render() {
|
||||
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 {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
use {
|
||||
crate::{
|
||||
allocator::{AllocatorError, BufferObject, BO_USE_LINEAR, BO_USE_RENDERING},
|
||||
client::{Client, ClientError},
|
||||
format::XRGB8888,
|
||||
gfx_api::{GfxContext, GfxError, GfxFramebuffer, GfxTexture},
|
||||
|
|
@ -16,17 +17,11 @@ use {
|
|||
numcell::NumCell,
|
||||
option_ext::OptionExt,
|
||||
},
|
||||
video::{
|
||||
dmabuf::DmaBuf,
|
||||
gbm::{GbmBo, GbmError, GBM_BO_USE_LINEAR, GBM_BO_USE_RENDERING},
|
||||
Modifier, INVALID_MODIFIER, LINEAR_MODIFIER,
|
||||
},
|
||||
video::{dmabuf::DmaBuf, INVALID_MODIFIER, LINEAR_MODIFIER},
|
||||
wire::{jay_screencast::*, JayScreencastId},
|
||||
},
|
||||
ahash::AHashSet,
|
||||
indexmap::{indexset, IndexSet},
|
||||
jay_config::video::Transform,
|
||||
once_cell::sync::Lazy,
|
||||
std::{
|
||||
cell::{Cell, RefCell},
|
||||
ops::DerefMut,
|
||||
|
|
@ -108,7 +103,7 @@ struct Pending {
|
|||
}
|
||||
|
||||
struct ScreencastBuffer {
|
||||
_bo: GbmBo,
|
||||
_bo: Rc<dyn BufferObject>,
|
||||
dmabuf: DmaBuf,
|
||||
fb: Rc<dyn GfxFramebuffer>,
|
||||
free: bool,
|
||||
|
|
@ -381,31 +376,27 @@ impl JayScreencast {
|
|||
if width == 0 || height == 0 {
|
||||
continue;
|
||||
}
|
||||
let mut usage = GBM_BO_USE_RENDERING;
|
||||
let mut usage = BO_USE_RENDERING;
|
||||
let modifiers = match self.linear.get() {
|
||||
true if format.write_modifiers.contains(&LINEAR_MODIFIER) => {
|
||||
static MODS: Lazy<IndexSet<Modifier>> =
|
||||
Lazy::new(|| indexset![LINEAR_MODIFIER]);
|
||||
&MODS
|
||||
vec![LINEAR_MODIFIER]
|
||||
}
|
||||
true if format.write_modifiers.contains(&INVALID_MODIFIER) => {
|
||||
usage |= GBM_BO_USE_LINEAR;
|
||||
static MODS: Lazy<IndexSet<Modifier>> =
|
||||
Lazy::new(|| indexset![INVALID_MODIFIER]);
|
||||
&MODS
|
||||
usage |= BO_USE_LINEAR;
|
||||
vec![INVALID_MODIFIER]
|
||||
}
|
||||
true => return Err(JayScreencastError::Modifier),
|
||||
false if format.write_modifiers.is_empty() => {
|
||||
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,
|
||||
width,
|
||||
height,
|
||||
XRGB8888,
|
||||
modifiers,
|
||||
&modifiers,
|
||||
usage,
|
||||
)?;
|
||||
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")]
|
||||
OutOfBounds(u32),
|
||||
#[error(transparent)]
|
||||
GbmError(#[from] GbmError),
|
||||
AllocatorError(#[from] AllocatorError),
|
||||
#[error(transparent)]
|
||||
GfxError(#[from] GfxError),
|
||||
#[error("Render context does not support XRGB8888 format")]
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ use {
|
|||
client::Client,
|
||||
leaks::Tracker,
|
||||
object::{Object, Version},
|
||||
video::dmabuf::{DmaBuf, DmaBufPlane},
|
||||
wire::{jay_screenshot::*, JayScreenshotId},
|
||||
},
|
||||
std::{convert::Infallible, rc::Rc},
|
||||
|
|
@ -45,6 +46,31 @@ impl JayScreenshot {
|
|||
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 {
|
||||
|
|
|
|||
|
|
@ -43,7 +43,9 @@ impl WlDrmGlobal {
|
|||
track!(client, obj);
|
||||
client.add_client_obj(&obj)?;
|
||||
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);
|
||||
}
|
||||
Ok(())
|
||||
|
|
|
|||
|
|
@ -202,7 +202,10 @@ impl Drop for SurfaceBuffer {
|
|||
log::error!("Cannot signal release point because there is no render context");
|
||||
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() {
|
||||
let res = ctx.import_sync_files(
|
||||
&release.sync_obj,
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
use {
|
||||
crate::{
|
||||
allocator::Allocator,
|
||||
async_engine::SpawnedFuture,
|
||||
backend::{
|
||||
AxisSource, Backend, BackendEvent, Connector, ConnectorEvent, ConnectorId,
|
||||
|
|
@ -15,14 +16,18 @@ use {
|
|||
test_error::TestResult, test_gfx_api::TestGfxCtx, test_utils::test_expected_event::TEEH,
|
||||
},
|
||||
state::State,
|
||||
udmabuf::Udmabuf,
|
||||
utils::{
|
||||
clonecell::CloneCell, copyhashmap::CopyHashMap, on_change::OnChange, oserror::OsError,
|
||||
syncqueue::SyncQueue,
|
||||
clonecell::CloneCell, copyhashmap::CopyHashMap, errorfmt::ErrorFmt,
|
||||
on_change::OnChange, oserror::OsError, syncqueue::SyncQueue,
|
||||
},
|
||||
video::{
|
||||
drm::{ConnectorType, Drm},
|
||||
gbm::{GbmDevice, GbmError},
|
||||
},
|
||||
video::drm::{ConnectorType, Drm},
|
||||
},
|
||||
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,
|
||||
uapi::c,
|
||||
};
|
||||
|
|
@ -37,6 +42,10 @@ pub enum TestBackendError {
|
|||
OpenDrmNode(String, #[source] OsError),
|
||||
#[error("Could not create a render context")]
|
||||
RenderContext(#[source] GfxError),
|
||||
#[error("Could not create a gbm device")]
|
||||
CreateGbmDevice(#[source] GbmError),
|
||||
#[error("Could not create any allocator")]
|
||||
CreateAllocator,
|
||||
}
|
||||
|
||||
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() {
|
||||
return Ok(());
|
||||
}
|
||||
self.create_render_context()?;
|
||||
self.create_render_context(prefer_udmabuf)?;
|
||||
self.render_context_installed.set(true);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
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
|
||||
.backend_events
|
||||
.push(BackendEvent::NewConnector(self.default_connector.clone()));
|
||||
|
|
@ -150,47 +163,35 @@ impl TestBackend {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn create_render_context(&self) -> Result<(), 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)),
|
||||
fn create_render_context(&self, prefer_udmabuf: bool) -> Result<(), TestBackendError> {
|
||||
macro_rules! constructor {
|
||||
($c:expr) => {
|
||||
(&|| {
|
||||
$c.map(|a| Rc::new(a) as Rc<dyn Allocator>)
|
||||
.map_err(|e| Box::new(e) as Box<dyn Error>)
|
||||
}) as &dyn Fn() -> Result<Rc<dyn Allocator>, Box<dyn Error>>
|
||||
};
|
||||
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;
|
||||
}
|
||||
let udmabuf = ("udmabuf", constructor!(Udmabuf::new()));
|
||||
let gbm = ("GBM", constructor!(create_gbm_allocator()));
|
||||
let allocators = match prefer_udmabuf {
|
||||
true => [udmabuf, gbm],
|
||||
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() {
|
||||
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 ctx = match TestGfxCtx::new(&drm) {
|
||||
}
|
||||
let allocator = allocator.ok_or(TestBackendError::CreateAllocator)?;
|
||||
let ctx = match TestGfxCtx::new(allocator) {
|
||||
Ok(ctx) => ctx,
|
||||
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 {
|
||||
fn run(self: Rc<Self>) -> SpawnedFuture<Result<(), Box<dyn std::error::Error>>> {
|
||||
let future = (self.test_future)(&self.state);
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ use {
|
|||
|
||||
pub struct TestClient {
|
||||
pub run: Rc<TestRun>,
|
||||
pub server: Rc<Client>,
|
||||
pub _server: Rc<Client>,
|
||||
pub tran: Rc<TestTransport>,
|
||||
pub registry: Rc<TestRegistry>,
|
||||
pub jc: Rc<TestJayCompositor>,
|
||||
|
|
@ -92,12 +92,8 @@ impl TestClient {
|
|||
}
|
||||
|
||||
pub async fn take_screenshot(&self, include_cursor: bool) -> Result<Vec<u8>, TestError> {
|
||||
let dmabuf = self.jc.take_screenshot(include_cursor).await?;
|
||||
let qoi = buf_to_bytes(
|
||||
&self.server.state.dma_buf_ids,
|
||||
&dmabuf,
|
||||
ScreenshotFormat::Qoi,
|
||||
);
|
||||
let (dmabuf, dev) = self.jc.take_screenshot(include_cursor).await?;
|
||||
let qoi = buf_to_bytes(dev.as_ref(), &dmabuf, ScreenshotFormat::Qoi)?;
|
||||
Ok(qoi)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
use {
|
||||
crate::{
|
||||
allocator::{Allocator, AllocatorError, BufferObject, BufferUsage},
|
||||
format::{Format, ARGB8888, XRGB8888},
|
||||
gfx_api::{
|
||||
CopyTexture, FillRect, FramebufferRect, GfxApiOpt, GfxContext, GfxError, GfxFormat,
|
||||
|
|
@ -7,12 +8,7 @@ use {
|
|||
},
|
||||
rect::Rect,
|
||||
theme::Color,
|
||||
video::{
|
||||
dmabuf::DmaBuf,
|
||||
drm::{sync_obj::SyncObjCtx, Drm, DrmError},
|
||||
gbm::{GbmBo, GbmDevice, GbmError},
|
||||
LINEAR_MODIFIER,
|
||||
},
|
||||
video::{dmabuf::DmaBuf, drm::sync_obj::SyncObjCtx, LINEAR_MODIFIER},
|
||||
},
|
||||
ahash::AHashMap,
|
||||
indexmap::IndexSet,
|
||||
|
|
@ -32,15 +28,9 @@ use {
|
|||
#[derive(Error, Debug)]
|
||||
enum TestGfxError {
|
||||
#[error("Could not map dmabuf")]
|
||||
MapDmaBuf(#[source] GbmError),
|
||||
MapDmaBuf(#[source] AllocatorError),
|
||||
#[error("Could not import dmabuf")]
|
||||
ImportDmaBuf(#[source] GbmError),
|
||||
#[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,
|
||||
ImportDmaBuf(#[source] AllocatorError),
|
||||
}
|
||||
|
||||
impl From<TestGfxError> for GfxError {
|
||||
|
|
@ -51,19 +41,11 @@ impl From<TestGfxError> for GfxError {
|
|||
|
||||
pub struct TestGfxCtx {
|
||||
formats: Rc<AHashMap<u32, GfxFormat>>,
|
||||
sync_obj_ctx: Rc<SyncObjCtx>,
|
||||
gbm: GbmDevice,
|
||||
render_node: Rc<CString>,
|
||||
allocator: Rc<dyn Allocator>,
|
||||
}
|
||||
|
||||
impl TestGfxCtx {
|
||||
pub fn new(drm: &Drm) -> 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()));
|
||||
pub fn new(allocator: Rc<dyn Allocator>) -> Result<Rc<Self>, GfxError> {
|
||||
let mut modifiers = IndexSet::new();
|
||||
modifiers.insert(LINEAR_MODIFIER);
|
||||
let mut formats = AHashMap::new();
|
||||
|
|
@ -79,9 +61,7 @@ impl TestGfxCtx {
|
|||
}
|
||||
Ok(Rc::new(Self {
|
||||
formats: Rc::new(formats),
|
||||
sync_obj_ctx: ctx,
|
||||
gbm,
|
||||
render_node: Rc::new(render_node),
|
||||
allocator,
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
|
@ -97,8 +77,8 @@ impl GfxContext for TestGfxCtx {
|
|||
None
|
||||
}
|
||||
|
||||
fn render_node(&self) -> Rc<CString> {
|
||||
self.render_node.clone()
|
||||
fn render_node(&self) -> Option<Rc<CString>> {
|
||||
None
|
||||
}
|
||||
|
||||
fn formats(&self) -> Rc<AHashMap<u32, GfxFormat>> {
|
||||
|
|
@ -109,9 +89,8 @@ impl GfxContext for TestGfxCtx {
|
|||
Ok(Rc::new(TestGfxImage::DmaBuf(TestDmaBufGfxImage {
|
||||
buf: buf.clone(),
|
||||
bo: self
|
||||
.gbm
|
||||
.import_dmabuf(buf, 0)
|
||||
.map(Rc::new)
|
||||
.allocator
|
||||
.import_dmabuf(buf, BufferUsage::none())
|
||||
.map_err(TestGfxError::ImportDmaBuf)?,
|
||||
})))
|
||||
}
|
||||
|
|
@ -142,8 +121,8 @@ impl GfxContext for TestGfxCtx {
|
|||
})))
|
||||
}
|
||||
|
||||
fn gbm(&self) -> &GbmDevice {
|
||||
&self.gbm
|
||||
fn allocator(&self) -> Rc<dyn Allocator> {
|
||||
self.allocator.clone()
|
||||
}
|
||||
|
||||
fn gfx_api(&self) -> GfxApi {
|
||||
|
|
@ -170,8 +149,8 @@ impl GfxContext for TestGfxCtx {
|
|||
}))
|
||||
}
|
||||
|
||||
fn sync_obj_ctx(&self) -> &Rc<SyncObjCtx> {
|
||||
&self.sync_obj_ctx
|
||||
fn sync_obj_ctx(&self) -> Option<&Rc<SyncObjCtx>> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -195,7 +174,7 @@ struct TestShmGfxImage {
|
|||
|
||||
struct TestDmaBufGfxImage {
|
||||
buf: DmaBuf,
|
||||
bo: Rc<GbmBo>,
|
||||
bo: Rc<dyn BufferObject>,
|
||||
}
|
||||
|
||||
impl TestGfxImage {
|
||||
|
|
@ -242,7 +221,7 @@ impl TestGfxImage {
|
|||
);
|
||||
}
|
||||
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 {
|
||||
copy(
|
||||
map.stride(),
|
||||
|
|
@ -478,7 +457,7 @@ impl GfxFramebuffer for TestGfxFb {
|
|||
s.format,
|
||||
),
|
||||
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(
|
||||
map.data_ptr(),
|
||||
d.buf.width,
|
||||
|
|
@ -511,7 +490,7 @@ impl GfxFramebuffer for TestGfxFb {
|
|||
s.format,
|
||||
)?,
|
||||
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(
|
||||
map.data_ptr(),
|
||||
d.buf.width,
|
||||
|
|
|
|||
|
|
@ -9,13 +9,14 @@ use {
|
|||
testrun::ParseFull,
|
||||
},
|
||||
utils::{buffd::MsgParser, cell_ext::CellExt},
|
||||
video::dmabuf::DmaBuf,
|
||||
wire::{
|
||||
jay_compositor::{self, *},
|
||||
jay_screenshot::Dmabuf,
|
||||
JayCompositorId,
|
||||
},
|
||||
},
|
||||
std::{cell::Cell, rc::Rc},
|
||||
uapi::OwnedFd,
|
||||
};
|
||||
|
||||
pub struct TestJayCompositor {
|
||||
|
|
@ -49,10 +50,16 @@ impl TestJayCompositor {
|
|||
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 {
|
||||
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_id: self.id,
|
||||
|
|
@ -62,7 +69,7 @@ impl TestJayCompositor {
|
|||
self.tran.add_obj(js.clone())?;
|
||||
self.tran.sync().await;
|
||||
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),
|
||||
None => bail!("Compositor did not send a screenshot"),
|
||||
}
|
||||
|
|
|
|||
|
|
@ -165,7 +165,7 @@ impl TestRegistry {
|
|||
get_jay_compositor,
|
||||
jay_compositor,
|
||||
jay_compositor,
|
||||
1,
|
||||
6,
|
||||
TestJayCompositor
|
||||
);
|
||||
create_singleton!(get_compositor, compositor, wl_compositor, 6, TestCompositor);
|
||||
|
|
|
|||
|
|
@ -1,21 +1,44 @@
|
|||
use {
|
||||
crate::{
|
||||
format::XRGB8888,
|
||||
it::{test_error::TestError, test_object::TestObject, testrun::ParseFull},
|
||||
state::State,
|
||||
utils::buffd::MsgParser,
|
||||
video::dmabuf::{DmaBuf, DmaBufPlane, PlaneVec},
|
||||
wire::{jay_screenshot::*, JayScreenshotId},
|
||||
},
|
||||
std::cell::Cell,
|
||||
std::{
|
||||
cell::{Cell, RefCell},
|
||||
rc::Rc,
|
||||
},
|
||||
uapi::OwnedFd,
|
||||
};
|
||||
|
||||
pub struct TestJayScreenshot {
|
||||
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 {
|
||||
fn handle_dmabuf(&self, parser: MsgParser<'_, '_>) -> Result<(), TestError> {
|
||||
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(())
|
||||
}
|
||||
|
||||
|
|
@ -24,6 +47,35 @@ impl TestJayScreenshot {
|
|||
self.result.set(Some(Err(ev.msg.to_string())));
|
||||
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! {
|
||||
|
|
@ -31,6 +83,9 @@ test_object! {
|
|||
|
||||
DMABUF => handle_dmabuf,
|
||||
ERROR => handle_error,
|
||||
DRM_DEV => handle_drm_dev,
|
||||
PLANE => handle_plane,
|
||||
DMABUF2 => handle_dmabuf2,
|
||||
}
|
||||
|
||||
impl TestObject for TestJayScreenshot {}
|
||||
|
|
|
|||
|
|
@ -79,7 +79,7 @@ impl TestRun {
|
|||
let client = self.state.clients.get(client_id)?;
|
||||
Ok(Rc::new(TestClient {
|
||||
run: self.clone(),
|
||||
server: client,
|
||||
_server: client,
|
||||
tran,
|
||||
jc,
|
||||
comp: registry.get_compositor().await?,
|
||||
|
|
@ -106,7 +106,14 @@ impl TestRun {
|
|||
}
|
||||
|
||||
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")?;
|
||||
self.state.eng.yield_now().await;
|
||||
let output = match self.state.root.outputs.lock().values().next() {
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ async fn test(run: Rc<TestRun>) -> TestResult {
|
|||
|
||||
tassert!(!run.cfg.graphics_initialized.get());
|
||||
|
||||
run.backend.install_render_context()?;
|
||||
run.backend.install_render_context(true)?;
|
||||
|
||||
tassert!(run.cfg.graphics_initialized.get());
|
||||
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ use {
|
|||
testcase!();
|
||||
|
||||
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>);
|
||||
impl SyncObjWaiter for Waiter {
|
||||
|
|
@ -23,7 +23,11 @@ async fn test(run: Rc<TestRun>) -> TestResult {
|
|||
let waiter = Rc::new(Waiter(Cell::new(false)));
|
||||
|
||||
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),
|
||||
Err(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;
|
||||
tassert_eq!(waiter.0.get(), false);
|
||||
|
||||
eng.sync_obj_ctx().signal(&syncobj, SyncObjPoint(1))?;
|
||||
ctx.signal(&syncobj, SyncObjPoint(1))?;
|
||||
|
||||
client.sync().await;
|
||||
tassert_eq!(waiter.0.get(), true);
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ use {
|
|||
testcase!();
|
||||
|
||||
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 Some(base_fb) = run.state.drm_feedback.get() else {
|
||||
|
|
|
|||
|
|
@ -43,6 +43,7 @@ mod macros;
|
|||
#[macro_use]
|
||||
mod leaks;
|
||||
mod acceptor;
|
||||
mod allocator;
|
||||
mod async_engine;
|
||||
mod backend;
|
||||
mod backends;
|
||||
|
|
@ -91,6 +92,7 @@ mod time;
|
|||
mod tools;
|
||||
mod tree;
|
||||
mod udev;
|
||||
mod udmabuf;
|
||||
mod user_session;
|
||||
mod utils;
|
||||
mod version;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
use {
|
||||
crate::{
|
||||
allocator::{BufferObject, BO_USE_RENDERING},
|
||||
async_engine::{Phase, SpawnedFuture},
|
||||
cursor::KnownCursor,
|
||||
fixed::Fixed,
|
||||
|
|
@ -15,7 +16,6 @@ use {
|
|||
asyncevent::AsyncEvent, clonecell::CloneCell, copyhashmap::CopyHashMap,
|
||||
errorfmt::ErrorFmt, hash_map_ext::HashMapExt, rc_eq::rc_eq,
|
||||
},
|
||||
video::gbm::{GbmBo, GBM_BO_USE_RENDERING},
|
||||
wire::{
|
||||
wp_fractional_scale_v1::PreferredScale, zwlr_layer_surface_v1::Configure,
|
||||
ZwpLinuxBufferParamsV1Id,
|
||||
|
|
@ -722,14 +722,15 @@ impl WindowData {
|
|||
log::error!("Render context cannot render to ARGB8888 format");
|
||||
return;
|
||||
}
|
||||
let modifiers: Vec<_> = format.write_modifiers.iter().copied().collect();
|
||||
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,
|
||||
width,
|
||||
height,
|
||||
ARGB8888,
|
||||
&format.write_modifiers,
|
||||
GBM_BO_USE_RENDERING,
|
||||
&modifiers,
|
||||
BO_USE_RENDERING,
|
||||
) {
|
||||
Ok(b) => b,
|
||||
Err(e) => {
|
||||
|
|
@ -844,13 +845,13 @@ pub struct GuiBuffer {
|
|||
pub wl: Rc<UsrWlBuffer>,
|
||||
pub window: Rc<WindowData>,
|
||||
pub fb: Rc<dyn GfxFramebuffer>,
|
||||
pub _bo: Option<GbmBo>,
|
||||
pub _bo: Option<Rc<dyn BufferObject>>,
|
||||
pub free: Cell<bool>,
|
||||
pub _size: (i32, i32),
|
||||
}
|
||||
|
||||
struct GuiBufferPending {
|
||||
pub bo: Cell<Option<GbmBo>>,
|
||||
pub bo: Cell<Option<Rc<dyn BufferObject>>>,
|
||||
pub window: Rc<WindowData>,
|
||||
pub fb: Rc<dyn GfxFramebuffer>,
|
||||
pub params: Rc<UsrLinuxBufferParams>,
|
||||
|
|
|
|||
|
|
@ -1,14 +1,11 @@
|
|||
use {
|
||||
crate::{
|
||||
allocator::{AllocatorError, BufferObject, BO_USE_LINEAR, BO_USE_RENDERING},
|
||||
format::XRGB8888,
|
||||
gfx_api::GfxError,
|
||||
scale::Scale,
|
||||
state::State,
|
||||
video::{
|
||||
drm::DrmError,
|
||||
gbm::{GbmBo, GbmError, GBM_BO_USE_LINEAR, GBM_BO_USE_RENDERING},
|
||||
INVALID_MODIFIER, LINEAR_MODIFIER,
|
||||
},
|
||||
video::{drm::DrmError, INVALID_MODIFIER, LINEAR_MODIFIER},
|
||||
},
|
||||
jay_config::video::Transform,
|
||||
std::{ops::Deref, rc::Rc},
|
||||
|
|
@ -23,7 +20,7 @@ pub enum ScreenshooterError {
|
|||
#[error("Display is empty")]
|
||||
EmptyDisplay,
|
||||
#[error(transparent)]
|
||||
GbmError(#[from] GbmError),
|
||||
AllocatorError(#[from] AllocatorError),
|
||||
#[error(transparent)]
|
||||
RenderError(#[from] GfxError),
|
||||
#[error(transparent)]
|
||||
|
|
@ -35,8 +32,8 @@ pub enum ScreenshooterError {
|
|||
}
|
||||
|
||||
pub struct Screenshot {
|
||||
pub drm: Rc<OwnedFd>,
|
||||
pub bo: GbmBo,
|
||||
pub drm: Option<Rc<OwnedFd>>,
|
||||
pub bo: Rc<dyn BufferObject>,
|
||||
}
|
||||
|
||||
pub fn take_screenshot(
|
||||
|
|
@ -52,18 +49,18 @@ pub fn take_screenshot(
|
|||
return Err(ScreenshooterError::EmptyDisplay);
|
||||
}
|
||||
let formats = ctx.formats();
|
||||
let mut usage = GBM_BO_USE_RENDERING;
|
||||
let mut usage = BO_USE_RENDERING;
|
||||
let modifiers = match formats.get(&XRGB8888.drm) {
|
||||
None => return Err(ScreenshooterError::XRGB8888),
|
||||
Some(f) if f.write_modifiers.contains(&LINEAR_MODIFIER) => &[LINEAR_MODIFIER],
|
||||
Some(f) if f.write_modifiers.contains(&INVALID_MODIFIER) => {
|
||||
usage |= GBM_BO_USE_LINEAR;
|
||||
usage |= BO_USE_LINEAR;
|
||||
&[INVALID_MODIFIER]
|
||||
}
|
||||
Some(_) => return Err(ScreenshooterError::Linear),
|
||||
};
|
||||
let gbm = ctx.gbm();
|
||||
let bo = gbm.create_bo(
|
||||
let allocator = ctx.allocator();
|
||||
let bo = allocator.create_bo(
|
||||
&state.dma_buf_ids,
|
||||
extents.width(),
|
||||
extents.height(),
|
||||
|
|
@ -83,6 +80,9 @@ pub fn take_screenshot(
|
|||
false,
|
||||
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 })
|
||||
}
|
||||
|
|
|
|||
18
src/state.rs
18
src/state.rs
|
|
@ -429,7 +429,7 @@ impl State {
|
|||
self.cursors.set(None);
|
||||
self.drm_feedback.set(None);
|
||||
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: {
|
||||
if let Some(ctx) = &ctx {
|
||||
|
|
@ -506,10 +506,12 @@ impl State {
|
|||
if !self.render_ctx_ever_initialized.replace(true) {
|
||||
self.add_global(&Rc::new(WlDrmGlobal::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() {
|
||||
self.add_global(&Rc::new(WpLinuxDrmSyncobjManagerV1Global::new(
|
||||
self.globals.name(),
|
||||
)));
|
||||
if let Some(ctx) = ctx.sync_obj_ctx() {
|
||||
if ctx.supports_async_wait() && self.explicit_sync_enabled.get() {
|
||||
self.add_global(&Rc::new(WpLinuxDrmSyncobjManagerV1Global::new(
|
||||
self.globals.name(),
|
||||
)));
|
||||
}
|
||||
}
|
||||
if let Some(config) = self.config.get() {
|
||||
config.graphics_initialized();
|
||||
|
|
@ -1042,7 +1044,11 @@ impl State {
|
|||
log::error!("Cannot signal sync obj point because there is no render context");
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
271
src/udmabuf.rs
Normal file
271
src/udmabuf.rs
Normal 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);
|
||||
|
|
@ -2,6 +2,10 @@
|
|||
|
||||
use {
|
||||
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},
|
||||
utils::oserror::OsError,
|
||||
video::{
|
||||
|
|
@ -38,6 +42,12 @@ pub enum GbmError {
|
|||
NoModifier,
|
||||
}
|
||||
|
||||
impl From<GbmError> for AllocatorError {
|
||||
fn from(value: GbmError) -> Self {
|
||||
Self(Box::new(value))
|
||||
}
|
||||
}
|
||||
|
||||
pub type Device = u8;
|
||||
type Bo = u8;
|
||||
|
||||
|
|
@ -151,17 +161,16 @@ pub struct GbmBoMap {
|
|||
stride: i32,
|
||||
}
|
||||
|
||||
impl GbmBoMap {
|
||||
pub unsafe fn data(&self) -> &[u8] {
|
||||
impl MappedBuffer for GbmBoMap {
|
||||
unsafe fn data(&self) -> &[u8] {
|
||||
&*self.data
|
||||
}
|
||||
|
||||
#[cfg_attr(not(feature = "it"), allow(dead_code))]
|
||||
pub fn data_ptr(&self) -> *mut u8 {
|
||||
fn data_ptr(&self) -> *mut u8 {
|
||||
self.data as _
|
||||
}
|
||||
|
||||
pub fn stride(&self) -> i32 {
|
||||
fn stride(&self) -> i32 {
|
||||
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 {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
|
|
@ -299,15 +358,10 @@ impl Drop for GbmDevice {
|
|||
}
|
||||
|
||||
impl GbmBo {
|
||||
pub fn dmabuf(&self) -> &DmaBuf {
|
||||
&self.dmabuf
|
||||
}
|
||||
|
||||
pub fn map_read(self: &Rc<Self>) -> Result<GbmBoMap, GbmError> {
|
||||
self.map2(GBM_BO_TRANSFER_READ)
|
||||
}
|
||||
|
||||
#[cfg_attr(not(feature = "it"), allow(dead_code))]
|
||||
pub fn map_write(self: &Rc<Self>) -> Result<GbmBoMap, GbmError> {
|
||||
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 {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
|
|
|
|||
|
|
@ -14,3 +14,19 @@ event dmabuf {
|
|||
event error {
|
||||
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),
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue