Merge pull request #625 from mahkoh/jorth/fast-ram-access
wl_shm: use udmabuf directly as texture on integrated GPUs
This commit is contained in:
commit
f3d6bff08d
8 changed files with 221 additions and 88 deletions
|
|
@ -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()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,68 +287,144 @@ 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,
|
||||
) -> Result<Option<Rc<dyn GfxBuffer>>, GfxError> {
|
||||
) -> 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 Ok(Some(hb.clone()));
|
||||
return Some(hb.clone());
|
||||
}
|
||||
if *host_buffer_impossible {
|
||||
return Ok(None);
|
||||
return None;
|
||||
}
|
||||
let udmabuf = 'udmabuf: {
|
||||
if let Some(b) = udmabuf {
|
||||
break 'udmabuf b.clone();
|
||||
}
|
||||
if *udmabuf_impossible {
|
||||
return Ok(None);
|
||||
}
|
||||
let Some(dev) = self.client.state.udmabuf.get() else {
|
||||
return Ok(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 Ok(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,
|
||||
Err(e) => {
|
||||
*host_buffer_impossible = true;
|
||||
log::debug!("Could not create gfx host buffer: {}", ErrorFmt(e));
|
||||
return Ok(None);
|
||||
return None;
|
||||
}
|
||||
};
|
||||
*host_buffer = Some(hb.clone());
|
||||
Ok(Some(hb))
|
||||
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> {
|
||||
|
|
@ -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() {
|
||||
|
|
|
|||
|
|
@ -14,7 +14,6 @@ use {
|
|||
utils::{
|
||||
clonecell::CloneCell,
|
||||
copyhashmap::CopyHashMap,
|
||||
errorfmt::ErrorFmt,
|
||||
hash_map_ext::HashMapExt,
|
||||
linkedlist::{LinkedList, LinkedNode, NodeRef},
|
||||
numcell::NumCell,
|
||||
|
|
@ -609,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)
|
||||
|
|
@ -619,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() {
|
||||
|
|
@ -649,19 +657,10 @@ fn schedule_async_upload(
|
|||
back_tex
|
||||
}
|
||||
};
|
||||
match buf.get_gfx_buffer(&ctx, mem, dmabuf_buffer_params) {
|
||||
Ok(Some(hb)) => {
|
||||
return back_tex
|
||||
.async_upload_from_buffer(&hb, node_ref.clone(), back.damage.get())
|
||||
.map_err(WlSurfaceError::PrepareAsyncUpload);
|
||||
}
|
||||
Ok(None) => {}
|
||||
Err(e) => {
|
||||
log::error!(
|
||||
"Could not create GPU mapping of host buffer: {}",
|
||||
ErrorFmt(e),
|
||||
);
|
||||
}
|
||||
if let Some(hb) = buf.get_gfx_buffer(&ctx, mem, dmabuf_buffer_params) {
|
||||
return back_tex
|
||||
.async_upload_from_buffer(&hb, node_ref.clone(), back.damage.get())
|
||||
.map_err(WlSurfaceError::PrepareAsyncUpload);
|
||||
}
|
||||
let mut staging_opt = surface.shm_staging.get();
|
||||
if let Some(staging) = &staging_opt
|
||||
|
|
|
|||
|
|
@ -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(),
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue