Skip to content

Commit

Permalink
video: add udmabuf allocator
Browse files Browse the repository at this point in the history
  • Loading branch information
mahkoh committed Sep 1, 2024
1 parent 2579834 commit d1462f8
Show file tree
Hide file tree
Showing 33 changed files with 882 additions and 258 deletions.
4 changes: 1 addition & 3 deletions .builds/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,7 @@ tasks:
sudo pacman -S --noconfirm rustup libinput pango mesa libxkbcommon xorg-xwayland adwaita-icon-theme libxcursor cmake shaderc
rustup toolchain install stable
- configure: |
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
Expand Down
57 changes: 57 additions & 0 deletions src/allocator.rs
Original file line number Diff line number Diff line change
@@ -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;
}
21 changes: 13 additions & 8 deletions src/backends/metal/video.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use {
crate::{
allocator::BufferObject,
async_engine::{Phase, SpawnedFuture},
backend::{
BackendDrmDevice, BackendDrmLease, BackendDrmLessee, BackendEvent, Connector,
Expand Down Expand Up @@ -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 {
Expand All @@ -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>,
Expand Down Expand Up @@ -2162,20 +2164,21 @@ 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)),
};
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() {
Expand Down Expand Up @@ -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)) {
Expand All @@ -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);
Expand Down Expand Up @@ -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,
Expand Down
1 change: 1 addition & 0 deletions src/backends/x.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use {
crate::{
allocator::BufferObject,
async_engine::{Phase, SpawnedFuture},
backend::{
AxisSource, Backend, BackendDrmDevice, BackendEvent, Connector, ConnectorEvent,
Expand Down
148 changes: 100 additions & 48 deletions src/cli/screenshot.rs
Original file line number Diff line number Diff line change
@@ -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) {
Expand Down Expand Up @@ -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 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));
});
let buf = match result.pop().await {
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(),
_ => {
Expand All @@ -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![];
Expand All @@ -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)
}
10 changes: 7 additions & 3 deletions src/drm_feedback.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down Expand Up @@ -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,
}
Loading

0 comments on commit d1462f8

Please sign in to comment.