1
0
Fork 0
forked from wry/wry

wl_shm: use udmabuf directly as texture on integrated GPUs

This commit is contained in:
Julian Orth 2025-10-02 17:18:09 +02:00
parent da33f26918
commit 9abfe88b05
8 changed files with 213 additions and 70 deletions

View file

@ -749,6 +749,8 @@ pub trait GfxContext: Debug {
fn formats(&self) -> Rc<AHashMap<u32, GfxFormat>>;
fn fast_ram_access(&self) -> bool;
fn dmabuf_fb(self: Rc<Self>, buf: &DmaBuf) -> Result<Rc<dyn GfxFramebuffer>, GfxError> {
self.dmabuf_img(buf)?.to_framebuffer()
}

View file

@ -68,6 +68,7 @@ pub struct EglDisplay {
pub gbm: Rc<GbmDevice>,
pub dpy: EGLDisplay,
pub explicit_sync: bool,
pub fast_ram_access: bool,
}
impl EglDisplay {
@ -106,16 +107,20 @@ impl EglDisplay {
gbm: Rc::new(gbm),
dpy,
explicit_sync: false,
fast_ram_access: false,
};
let mut major = 0;
let mut minor = 0;
if (egl.eglInitialize)(dpy.dpy, &mut major, &mut minor) != EGL_TRUE {
return Err(RenderError::Initialize);
}
if !software && EXTS.contains(EXT_DEVICE_QUERY) {
if get_device_ext(procs, dpy.dpy)?.contains(MESA_DEVICE_SOFTWARE) {
if EXTS.contains(EXT_DEVICE_QUERY)
&& get_device_ext(procs, dpy.dpy)?.contains(MESA_DEVICE_SOFTWARE)
{
if !software {
return Err(RenderError::NoHardwareRenderer);
}
dpy.fast_ram_access = true;
}
dpy.exts = get_display_ext(dpy.dpy);
if !dpy.exts.intersects(KHR_IMAGE_BASE) {

View file

@ -257,6 +257,10 @@ impl GfxContext for GlRenderContext {
self.formats()
}
fn fast_ram_access(&self) -> bool {
self.ctx.dpy.fast_ram_access
}
fn dmabuf_fb(self: Rc<Self>, buf: &DmaBuf) -> Result<Rc<dyn GfxFramebuffer>, GfxError> {
(&self)
.dmabuf_fb(buf)

View file

@ -280,6 +280,10 @@ impl GfxContext for Context {
self.0.formats.clone()
}
fn fast_ram_access(&self) -> bool {
self.0.device.fast_ram_access
}
fn dmabuf_img(self: Rc<Self>, buf: &DmaBuf) -> Result<Rc<dyn GfxImage>, GfxError> {
self.0
.import_dmabuf(buf)

View file

@ -83,6 +83,7 @@ pub struct VulkanDevice {
pub(super) uniform_buffer_offset_mask: DeviceSize,
pub(super) uniform_buffer_descriptor_size: usize,
pub(super) lost: Cell<bool>,
pub(super) fast_ram_access: bool,
}
impl Drop for VulkanDevice {
@ -129,7 +130,7 @@ impl VulkanInstance {
}
}
fn find_dev(&self, drm: &Drm) -> Result<PhysicalDevice, VulkanError> {
fn find_dev(&self, drm: &Drm) -> Result<(PhysicalDevice, PhysicalDeviceType), VulkanError> {
let dev = drm.dev();
log::log!(
self.log_level,
@ -188,7 +189,7 @@ impl VulkanInstance {
Some(&extensions),
Some(&driver_props),
);
return Ok(phy_dev);
return Ok((phy_dev, props.device_type));
}
devices.push((props, Some(extensions), Some(driver_props)));
}
@ -210,7 +211,7 @@ impl VulkanInstance {
Err(VulkanError::NoDeviceFound(dev))
}
fn find_software_renderer(&self) -> Result<PhysicalDevice, VulkanError> {
fn find_software_renderer(&self) -> Result<(PhysicalDevice, PhysicalDeviceType), VulkanError> {
let phy_devs = unsafe { self.instance.enumerate_physical_devices() };
let phy_devs = match phy_devs {
Ok(d) => d,
@ -222,7 +223,7 @@ impl VulkanInstance {
continue;
}
if props.device_type == PhysicalDeviceType::CPU {
return Ok(phy_dev);
return Ok((phy_dev, props.device_type));
}
}
Err(VulkanError::NoSoftwareRenderer)
@ -344,7 +345,7 @@ impl VulkanInstance {
.ok_or(VulkanError::NoRenderNode)
.map(Rc::new)?;
let gbm = GbmDevice::new(drm).map_err(VulkanError::Gbm)?;
let phy_dev = match software {
let (phy_dev, dev_ty) = match software {
true => self.find_software_renderer()?,
false => self.find_dev(drm)?,
};
@ -543,6 +544,11 @@ impl VulkanInstance {
"Created queues with priorities {max_graphics_priority:?}/{max_transfer_priority:?}",
);
}
let fast_ram_access = match dev_ty {
PhysicalDeviceType::CPU => true,
PhysicalDeviceType::INTEGRATED_GPU => true,
_ => false,
};
Ok(Rc::new(VulkanDevice {
physical_device: phy_dev,
render_node,
@ -572,6 +578,7 @@ impl VulkanInstance {
uniform_buffer_offset_mask,
uniform_buffer_descriptor_size,
lost: Cell::new(false),
fast_ram_access,
}))
}
}

View file

@ -9,7 +9,10 @@ use {
object::{Object, Version},
rect::{Rect, Region},
utils::{errorfmt::ErrorFmt, page_size::page_size},
video::dmabuf::DmaBuf,
video::{
LINEAR_MODIFIER,
dmabuf::{DmaBuf, DmaBufPlane},
},
wire::{WlBufferId, wl_buffer::*},
},
std::{
@ -41,6 +44,8 @@ pub struct DmabufBufferParams {
udmabuf_impossible: bool,
host_buffer: Option<Rc<dyn GfxBuffer>>,
host_buffer_impossible: bool,
tex: Option<Rc<dyn GfxTexture>>,
tex_impossible: bool,
}
pub struct WlBuffer {
@ -120,15 +125,18 @@ impl WlBuffer {
if (stride as u64) < min_row_size {
return Err(WlBufferError::StrideTooSmall);
}
let udmabuf_impossible = !mem.pool().is_sealed_memfd();
let dmabuf_buffer_params = match udmabuf {
None => DmabufBufferParams {
size: bytes as usize,
udmabuf: None,
udmabuf_offset: 0,
udmabuf_size: 0,
udmabuf_impossible: !mem.pool().is_sealed_memfd(),
udmabuf_impossible,
host_buffer: None,
host_buffer_impossible: !mem.pool().is_sealed_memfd(),
host_buffer_impossible: udmabuf_impossible,
tex: None,
tex_impossible: udmabuf_impossible,
},
Some((udmabuf, size)) => DmabufBufferParams {
size,
@ -138,6 +146,8 @@ impl WlBuffer {
udmabuf_impossible: false,
host_buffer: None,
host_buffer_impossible: false,
tex: None,
tex_impossible: false,
},
};
Ok(Self {
@ -209,20 +219,22 @@ impl WlBuffer {
udmabuf_impossible,
host_buffer,
host_buffer_impossible,
tex,
tex_impossible,
..
},
..
} => {
host_buffer.take();
*host_buffer_impossible = *udmabuf_impossible;
return match surface {
Some(s) => {
s.shm_staging.take();
s.shm_textures.back().tex.take();
s.shm_textures.front().tex.take().is_some()
}
None => false,
};
let mut had_texture = tex.take().is_some();
*tex_impossible = *udmabuf_impossible;
if let Some(s) = surface {
s.shm_staging.take();
s.shm_textures.back().tex.take();
had_texture |= s.shm_textures.front().tex.take().is_some();
}
return had_texture;
}
WlBufferStorage::Dmabuf { tex, .. } => tex.is_some(),
};
@ -255,7 +267,13 @@ impl WlBuffer {
match &*self.storage.borrow() {
None => None,
Some(s) => match s {
WlBufferStorage::Shm { .. } => {
WlBufferStorage::Shm {
dmabuf_buffer_params,
..
} => {
if let Some(tex) = &dmabuf_buffer_params.tex {
return Some(tex.clone());
}
surface.shm_textures.front().tex.get().map(|t| t as _)
}
WlBufferStorage::Dmabuf { tex, .. } => tex.clone(),
@ -269,20 +287,57 @@ impl WlBuffer {
}
}
pub fn get_gfx_buffer(
self: &Rc<Self>,
ctx: &Rc<dyn GfxContext>,
pub fn get_udmabuf(
&self,
mem: &Rc<ClientMemOffset>,
dmabuf_buffer_params: &mut DmabufBufferParams,
) -> Option<Rc<dyn GfxBuffer>> {
) -> Option<Rc<OwnedFd>> {
let DmabufBufferParams {
size,
udmabuf,
udmabuf_offset,
udmabuf_size,
udmabuf_impossible,
..
} = dmabuf_buffer_params;
if let Some(b) = udmabuf {
return Some(b.clone());
}
if *udmabuf_impossible {
return None;
}
let dev = self.client.state.udmabuf.get()?;
let mask = page_size() - 1;
let offset = mem.offset() & mask;
let base = mem.offset() & !mask;
let end = (mem.offset() + *size + mask) & !mask;
let len = end - base;
match dev.create_dmabuf_from_memfd(mem.pool().fd(), base, len) {
Ok(b) => {
let b = Rc::new(b);
*udmabuf_offset = offset;
*udmabuf_size = len;
*udmabuf = Some(b.clone());
Some(b)
}
Err(e) => {
*udmabuf_impossible = true;
log::debug!("Could not create udmabuf: {}", ErrorFmt(e));
None
}
}
}
pub fn get_gfx_buffer(
&self,
ctx: &Rc<dyn GfxContext>,
mem: &Rc<ClientMemOffset>,
dmabuf_buffer_params: &mut DmabufBufferParams,
) -> Option<Rc<dyn GfxBuffer>> {
let DmabufBufferParams {
host_buffer,
host_buffer_impossible,
..
} = dmabuf_buffer_params;
if let Some(hb) = host_buffer {
return Some(hb.clone());
@ -290,36 +345,14 @@ impl WlBuffer {
if *host_buffer_impossible {
return None;
}
let udmabuf = 'udmabuf: {
if let Some(b) = udmabuf {
break 'udmabuf b.clone();
}
if *udmabuf_impossible {
return None;
}
let Some(dev) = self.client.state.udmabuf.get() else {
return None;
};
let mask = page_size() - 1;
let offset = mem.offset() & mask;
let base = mem.offset() & !mask;
let end = (mem.offset() + *size + mask) & !mask;
let len = end - base;
match dev.create_dmabuf_from_memfd(mem.pool().fd(), base, len) {
Ok(b) => {
let b = Rc::new(b);
*udmabuf_offset = offset;
*udmabuf_size = len;
*udmabuf = Some(b.clone());
b
}
Err(e) => {
*udmabuf_impossible = true;
log::debug!("Could not create udmabuf: {}", ErrorFmt(e));
return None;
}
}
};
let udmabuf = self.get_udmabuf(mem, dmabuf_buffer_params)?;
let DmabufBufferParams {
udmabuf_offset,
udmabuf_size,
host_buffer,
host_buffer_impossible,
..
} = dmabuf_buffer_params;
let hb =
match ctx.create_dmabuf_buffer(&udmabuf, *udmabuf_offset, *udmabuf_size, self.format) {
Ok(hb) => hb,
@ -333,6 +366,67 @@ impl WlBuffer {
Some(hb)
}
pub fn import_udmabuf_texture(
&self,
ctx: &Rc<dyn GfxContext>,
mem: &Rc<ClientMemOffset>,
stride: i32,
dmabuf_buffer_params: &mut DmabufBufferParams,
) -> bool {
let DmabufBufferParams {
tex,
tex_impossible,
..
} = dmabuf_buffer_params;
if tex.is_some() {
return true;
}
if *tex_impossible {
return false;
}
let Some(udmabuf) = self.get_udmabuf(mem, dmabuf_buffer_params) else {
return false;
};
let DmabufBufferParams {
udmabuf_offset,
tex,
tex_impossible,
..
} = dmabuf_buffer_params;
let mut dmabuf = DmaBuf {
id: self.client.state.dma_buf_ids.next(),
width: self.width,
height: self.height,
format: self.format,
modifier: LINEAR_MODIFIER,
planes: Default::default(),
is_disjoint: Default::default(),
};
dmabuf.planes.push(DmaBufPlane {
offset: *udmabuf_offset as _,
stride: stride as _,
fd: udmabuf,
});
let img = match ctx.clone().dmabuf_img(&dmabuf) {
Ok(i) => i,
Err(e) => {
*tex_impossible = true;
log::debug!("Could not import udmabuf as GfxImage: {}", ErrorFmt(e));
return false;
}
};
let tex_ = match img.to_texture() {
Ok(i) => i,
Err(e) => {
*tex_impossible = true;
log::debug!("Could not import udmabuf as GfxTexture: {}", ErrorFmt(e));
return false;
}
};
*tex = Some(tex_);
true
}
fn update_texture(&self, surface: &WlSurface, sync_shm: bool) -> Result<(), WlBufferError> {
let storage = &mut *self.storage.borrow_mut();
let storage = match storage {
@ -340,19 +434,33 @@ impl WlBuffer {
_ => return Ok(()),
};
match storage {
WlBufferStorage::Shm { mem, stride, .. } => {
if sync_shm && let Some(ctx) = self.client.state.render_ctx.get() {
let tex = ctx.async_shmem_texture(
self.format,
self.width,
self.height,
*stride,
&self.client.state.cpu_worker,
)?;
mem.access(|mem| tex.clone().sync_upload(mem, Region::new(self.rect)))??;
surface.shm_textures.front().tex.set(Some(tex));
surface.shm_textures.front().damage.clear();
WlBufferStorage::Shm {
mem,
stride,
dmabuf_buffer_params,
..
} => {
if !sync_shm {
return Ok(());
}
let Some(ctx) = self.client.state.render_ctx.get() else {
return Ok(());
};
if ctx.fast_ram_access()
&& self.import_udmabuf_texture(&ctx, mem, *stride, dmabuf_buffer_params)
{
return Ok(());
}
let tex = ctx.async_shmem_texture(
self.format,
self.width,
self.height,
*stride,
&self.client.state.cpu_worker,
)?;
mem.access(|mem| tex.clone().sync_upload(mem, Region::new(self.rect)))??;
surface.shm_textures.front().tex.set(Some(tex));
surface.shm_textures.front().damage.clear();
}
WlBufferStorage::Dmabuf { img, tex, .. } => {
if tex.is_none() {

View file

@ -608,6 +608,20 @@ fn schedule_async_upload(
return Ok(None);
};
let back = surface.shm_textures.back();
let state = &surface.client.state;
let ctx = state
.render_ctx
.get()
.ok_or(WlSurfaceError::NoRenderContext)?;
if ctx.fast_ram_access() && buf.import_udmabuf_texture(&ctx, mem, *stride, dmabuf_buffer_params)
{
back.damage.clear();
back.tex.take();
if surface.shm_textures.front().tex.is_none() {
surface.shm_staging.take();
}
return Ok(None);
}
let mut back_tex_opt = back.tex.get();
if let Some(back_tex) = &back_tex_opt
&& !back_tex.compatible_with(buf.format, buf.rect.width(), buf.rect.height(), *stride)
@ -618,11 +632,6 @@ fn schedule_async_upload(
back.damage.clear();
back.damage.damage(slice::from_ref(&buf.rect));
};
let state = &surface.client.state;
let ctx = state
.render_ctx
.get()
.ok_or(WlSurfaceError::NoRenderContext)?;
let back_tex = match back_tex_opt {
Some(b) => {
if pending.damage_full || pending.surface_damage.is_not_empty() {

View file

@ -107,6 +107,10 @@ impl GfxContext for TestGfxCtx {
self.formats.clone()
}
fn fast_ram_access(&self) -> bool {
true
}
fn dmabuf_img(self: Rc<Self>, buf: &DmaBuf) -> Result<Rc<dyn GfxImage>, GfxError> {
Ok(Rc::new(TestGfxImage::DmaBuf(TestDmaBufGfxImage {
buf: buf.clone(),