diff --git a/src/gfx_api.rs b/src/gfx_api.rs index 86801b11..810bdd81 100644 --- a/src/gfx_api.rs +++ b/src/gfx_api.rs @@ -749,6 +749,8 @@ pub trait GfxContext: Debug { fn formats(&self) -> Rc>; + fn fast_ram_access(&self) -> bool; + fn dmabuf_fb(self: Rc, buf: &DmaBuf) -> Result, GfxError> { self.dmabuf_img(buf)?.to_framebuffer() } diff --git a/src/gfx_apis/gl/egl/display.rs b/src/gfx_apis/gl/egl/display.rs index a5bff244..cffebaa8 100644 --- a/src/gfx_apis/gl/egl/display.rs +++ b/src/gfx_apis/gl/egl/display.rs @@ -68,6 +68,7 @@ pub struct EglDisplay { pub gbm: Rc, 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) { diff --git a/src/gfx_apis/gl/renderer/context.rs b/src/gfx_apis/gl/renderer/context.rs index d4f1ac99..2be8defb 100644 --- a/src/gfx_apis/gl/renderer/context.rs +++ b/src/gfx_apis/gl/renderer/context.rs @@ -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, buf: &DmaBuf) -> Result, GfxError> { (&self) .dmabuf_fb(buf) diff --git a/src/gfx_apis/vulkan.rs b/src/gfx_apis/vulkan.rs index 31844fb4..5d7a2d88 100644 --- a/src/gfx_apis/vulkan.rs +++ b/src/gfx_apis/vulkan.rs @@ -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, buf: &DmaBuf) -> Result, GfxError> { self.0 .import_dmabuf(buf) diff --git a/src/gfx_apis/vulkan/device.rs b/src/gfx_apis/vulkan/device.rs index 39d8ca03..518f86f2 100644 --- a/src/gfx_apis/vulkan/device.rs +++ b/src/gfx_apis/vulkan/device.rs @@ -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, + pub(super) fast_ram_access: bool, } impl Drop for VulkanDevice { @@ -129,7 +130,7 @@ impl VulkanInstance { } } - fn find_dev(&self, drm: &Drm) -> Result { + 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 { + 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, })) } } diff --git a/src/ifs/wl_buffer.rs b/src/ifs/wl_buffer.rs index cbb0a2cc..88b2e54a 100644 --- a/src/ifs/wl_buffer.rs +++ b/src/ifs/wl_buffer.rs @@ -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>, host_buffer_impossible: bool, + tex: Option>, + 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, - ctx: &Rc, + pub fn get_udmabuf( + &self, mem: &Rc, dmabuf_buffer_params: &mut DmabufBufferParams, - ) -> Result>, GfxError> { + ) -> Option> { 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, + mem: &Rc, + dmabuf_buffer_params: &mut DmabufBufferParams, + ) -> Option> { + 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, + mem: &Rc, + 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() { diff --git a/src/ifs/wl_surface/commit_timeline.rs b/src/ifs/wl_surface/commit_timeline.rs index ef18bb04..a60bf33d 100644 --- a/src/ifs/wl_surface/commit_timeline.rs +++ b/src/ifs/wl_surface/commit_timeline.rs @@ -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 diff --git a/src/it/test_gfx_api.rs b/src/it/test_gfx_api.rs index 8e5a1865..596de1de 100644 --- a/src/it/test_gfx_api.rs +++ b/src/it/test_gfx_api.rs @@ -107,6 +107,10 @@ impl GfxContext for TestGfxCtx { self.formats.clone() } + fn fast_ram_access(&self) -> bool { + true + } + fn dmabuf_img(self: Rc, buf: &DmaBuf) -> Result, GfxError> { Ok(Rc::new(TestGfxImage::DmaBuf(TestDmaBufGfxImage { buf: buf.clone(),