diff --git a/build/egl.rs b/build/egl.rs index 148f421f..d82aa654 100644 --- a/build/egl.rs +++ b/build/egl.rs @@ -80,6 +80,34 @@ fn write_egl_procs(f: &mut W) -> anyhow::Result<()> { &[("target", "GLenum"), ("image", "GLeglImageOES")][..], ), ("glGetGraphicsResetStatusKHR", "GLenum", &[][..]), + ( + "eglCreateSyncKHR", + "EGLSyncKHR", + &[ + ("dpy", "EGLDisplay"), + ("ty", "EGLenum"), + ("attrib_list", "*const EGLint"), + ][..], + ), + ( + "eglDestroySyncKHR", + "EGLBoolean", + &[("dpy", "EGLDisplay"), ("sync", "EGLSyncKHR")][..], + ), + ( + "eglDupNativeFenceFDANDROID", + "EGLint", + &[("dpy", "EGLDisplay"), ("sync", "EGLSyncKHR")][..], + ), + ( + "eglWaitSyncKHR", + "EGLint", + &[ + ("dpy", "EGLDisplay"), + ("sync", "EGLSyncKHR"), + ("flags", "EGLint"), + ][..], + ), ]; writeln!(f, "use std::ptr;")?; diff --git a/src/backend.rs b/src/backend.rs index 82a2569e..da81118f 100644 --- a/src/backend.rs +++ b/src/backend.rs @@ -3,7 +3,7 @@ use { async_engine::SpawnedFuture, drm_feedback::DrmFeedback, fixed::Fixed, - gfx_api::GfxFramebuffer, + gfx_api::{GfxFramebuffer, SyncFile}, ifs::wl_seat::wl_pointer::{CONTINUOUS, FINGER, HORIZONTAL_SCROLL, VERTICAL_SCROLL, WHEEL}, libinput::consts::DeviceCapability, video::drm::{ConnectorType, DrmError, DrmVersion}, @@ -106,6 +106,7 @@ pub trait HardwareCursor: Debug { fn get_buffer(&self) -> Rc; fn set_position(&self, x: i32, y: i32); fn swap_buffer(&self); + fn set_sync_file(&self, sync_file: Option); fn commit(&self); fn size(&self) -> (i32, i32); } diff --git a/src/backends/metal/video.rs b/src/backends/metal/video.rs index 630a17b4..4f9c3328 100644 --- a/src/backends/metal/video.rs +++ b/src/backends/metal/video.rs @@ -9,7 +9,10 @@ use { drm_feedback::DrmFeedback, edid::Descriptor, format::{Format, ARGB8888, XRGB8888}, - gfx_api::{BufferResv, GfxApiOpt, GfxContext, GfxFramebuffer, GfxRenderPass, GfxTexture}, + gfx_api::{ + AcquireSync, BufferResv, GfxApiOpt, GfxContext, GfxFramebuffer, GfxRenderPass, + GfxTexture, ReleaseSync, SyncFile, + }, ifs::wp_presentation_feedback::{KIND_HW_COMPLETION, KIND_VSYNC}, renderer::RenderResult, state::State, @@ -227,6 +230,7 @@ pub struct MetalConnector { pub cursor_buffers: CloneCell>>, pub cursor_front_buffer: NumCell, pub cursor_swap_buffer: Cell, + pub cursor_sync_file: CloneCell>, pub drm_feedback: CloneCell>>, pub scanout_buffers: RefCell>, @@ -244,6 +248,7 @@ pub struct MetalHardwareCursor { pub cursor_x_pending: Cell, pub cursor_y_pending: Cell, pub cursor_buffers: Rc<[RenderBuffer; 3]>, + pub sync_file: CloneCell>, pub have_changes: Cell, } @@ -270,6 +275,11 @@ impl HardwareCursor for MetalHardwareCursor { self.have_changes.set(true); } + fn set_sync_file(&self, sync_file: Option) { + self.sync_file.set(sync_file); + self.have_changes.set(true); + } + fn commit(&self) { if self.generation != self.connector.cursor_generation.get() { return; @@ -285,6 +295,7 @@ impl HardwareCursor for MetalHardwareCursor { if self.cursor_swap_buffer.take() { self.connector.cursor_swap_buffer.set(true); } + self.connector.cursor_sync_file.set(self.sync_file.take()); self.connector.cursor_changed.set(true); if self.connector.can_present.get() { self.connector.schedule_present(); @@ -350,6 +361,7 @@ pub struct DirectScanoutCache { #[derive(Debug)] pub struct DirectScanoutData { tex: Rc, + acquire_sync: AcquireSync, _resv: Option>, fb: Rc, dma_buf_id: DmaBufId, @@ -370,6 +382,7 @@ pub struct DirectScanoutPosition { pub struct PresentFb { fb: Rc, direct_scanout_data: Option, + sync_file: Option, } impl MetalConnector { @@ -396,6 +409,7 @@ impl MetalConnector { cursor_x_pending: Cell::new(self.cursor_x.get()), cursor_y_pending: Cell::new(self.cursor_y.get()), cursor_buffers: cp.clone(), + sync_file: Default::default(), have_changes: Cell::new(false), }) as _), _ => None, @@ -474,6 +488,10 @@ impl MetalConnector { } ct }; + if let AcquireSync::None = ct.acquire_sync { + // Cannot perform scanout without sync. + return None; + } if ct.source.buffer_transform != ct.target.output_transform { // Rotations and mirroring are not supported. return None; @@ -526,6 +544,7 @@ impl MetalConnector { if let Some(buffer) = cache.get(&dmabuf.id) { return buffer.fb.as_ref().map(|fb| DirectScanoutData { tex: buffer.tex.upgrade().unwrap(), + acquire_sync: ct.acquire_sync.clone(), _resv: ct.buffer_resv.clone(), fb: fb.clone(), dma_buf_id: dmabuf.id, @@ -550,6 +569,7 @@ impl MetalConnector { let data = match self.dev.master.add_fb(dmabuf, Some(format.format)) { Ok(fb) => Some(DirectScanoutData { tex: ct.tex.clone(), + acquire_sync: ct.acquire_sync.clone(), _resv: ct.buffer_resv.clone(), fb: Rc::new(fb), dma_buf_id: dmabuf.id, @@ -630,21 +650,31 @@ impl MetalConnector { }; log::debug!("{} direct scanout on {}", change, self.kernel_id()); } - let fb = match &direct_scanout_data { + let sync_file; + let fb; + match &direct_scanout_data { None => { - buffer_fb + let sf = buffer_fb .perform_render_pass(pass) .map_err(MetalError::RenderFrame)?; - buffer.copy_to_dev()?; + sync_file = buffer.copy_to_dev(sf)?; self.next_buffer.fetch_add(1); output.perform_screencopies(&buffer.render_tex, !render_hw_cursor, 0, 0, None); - buffer.drm.clone() + fb = buffer.drm.clone(); + } + Some(dsd) => { + sync_file = match &dsd.acquire_sync { + AcquireSync::None => None, + AcquireSync::Implicit => None, + AcquireSync::SyncFile { sync_file } => Some(sync_file.clone()), + }; + fb = dsd.fb.clone(); } - Some(dsd) => dsd.fb.clone(), }; Ok(PresentFb { fb, direct_scanout_data, + sync_file, }) } @@ -699,6 +729,7 @@ impl MetalConnector { ) } }; + let in_fence = fb.sync_file.as_ref().map(|s| s.raw()).unwrap_or(-1); changes.change_object(plane.id, |c| { c.change(plane.fb_id, fb.fb.id().0 as _); c.change(plane.src_w.id, (src_width as u64) << 16); @@ -707,11 +738,13 @@ impl MetalConnector { c.change(plane.crtc_y.id, crtc_y as u64); c.change(plane.crtc_w.id, crtc_w as u64); c.change(plane.crtc_h.id, crtc_h as u64); + c.change(plane.in_fence_fd, in_fence as u64); }); new_fb = Some(fb); } } let mut cursor_swap_buffer = false; + let mut cursor_sync_file = None; if self.cursor_changed.get() && cursor.is_some() { let plane = cursor.unwrap(); if self.cursor_enabled.get() { @@ -719,12 +752,14 @@ impl MetalConnector { let mut front_buffer = self.cursor_front_buffer.get(); if cursor_swap_buffer { front_buffer = front_buffer.wrapping_add(1); + cursor_sync_file = self.cursor_sync_file.get(); } let buffers = self.cursor_buffers.get().unwrap(); let buffer = &buffers[front_buffer % buffers.len()]; if cursor_swap_buffer { - buffer.copy_to_dev()?; + cursor_sync_file = buffer.copy_to_dev(cursor_sync_file)?; } + let in_fence = cursor_sync_file.as_ref().map(|s| s.raw()).unwrap_or(-1); let (width, height) = buffer.dev_fb.physical_size(); changes.change_object(plane.id, |c| { c.change(plane.fb_id, buffer.drm.id().0 as _); @@ -737,6 +772,7 @@ impl MetalConnector { c.change(plane.src_y.id, 0); c.change(plane.src_w.id, (width as u64) << 16); c.change(plane.src_h.id, (height as u64) << 16); + c.change(plane.in_fence_fd, in_fence as u64); }); } else { changes.change_object(plane.id, |c| { @@ -775,6 +811,7 @@ impl MetalConnector { if cursor_swap_buffer { self.cursor_swap_buffer.set(false); self.cursor_front_buffer.fetch_add(1); + self.cursor_sync_file.take(); } self.can_present.set(false); self.has_damage.set(false); @@ -1026,6 +1063,7 @@ fn create_connector( cursor_changed: Cell::new(false), cursor_front_buffer: Default::default(), cursor_swap_buffer: Cell::new(false), + cursor_sync_file: Default::default(), drm_feedback: Default::default(), scanout_buffers: Default::default(), active_framebuffer: Default::default(), @@ -2426,12 +2464,13 @@ impl RenderBuffer { .unwrap_or_else(|| self.dev_fb.clone()) } - fn copy_to_dev(&self) -> Result<(), MetalError> { + fn copy_to_dev(&self, sync_file: Option) -> Result, MetalError> { let Some(tex) = &self.dev_tex else { - return Ok(()); + return Ok(sync_file); }; + let acquire_point = AcquireSync::from_sync_file(sync_file); self.dev_fb - .copy_texture(tex, 0, 0) + .copy_texture(tex, acquire_point, ReleaseSync::Implicit, 0, 0) .map_err(MetalError::CopyToOutput) } } diff --git a/src/cursor.rs b/src/cursor.rs index c644f28e..a9759b47 100644 --- a/src/cursor.rs +++ b/src/cursor.rs @@ -2,7 +2,7 @@ use { crate::{ fixed::Fixed, format::ARGB8888, - gfx_api::{GfxContext, GfxError, GfxTexture}, + gfx_api::{AcquireSync, GfxContext, GfxError, GfxTexture, ReleaseSync}, rect::Rect, renderer::Renderer, scale::Scale, @@ -379,6 +379,8 @@ fn render_img(image: &InstantiatedCursorImage, renderer: &mut Renderer, x: Fixed scale, None, None, + AcquireSync::None, + ReleaseSync::None, ); } } @@ -390,9 +392,18 @@ impl Cursor for StaticCursor { fn render_hardware_cursor(&self, renderer: &mut Renderer) { if let Some(img) = self.image.scales.get(&renderer.scale()) { - renderer - .base - .render_texture(&img.tex, 0, 0, None, None, renderer.scale(), None, None); + renderer.base.render_texture( + &img.tex, + 0, + 0, + None, + None, + renderer.scale(), + None, + None, + AcquireSync::None, + ReleaseSync::None, + ); } } @@ -420,9 +431,18 @@ impl Cursor for AnimatedCursor { fn render_hardware_cursor(&self, renderer: &mut Renderer) { let img = &self.images[self.idx.get()]; if let Some(img) = img.scales.get(&renderer.scale()) { - renderer - .base - .render_texture(&img.tex, 0, 0, None, None, renderer.scale(), None, None); + renderer.base.render_texture( + &img.tex, + 0, + 0, + None, + None, + renderer.scale(), + None, + None, + AcquireSync::None, + ReleaseSync::None, + ); } } diff --git a/src/gfx_api.rs b/src/gfx_api.rs index 1d411222..f3187b5d 100644 --- a/src/gfx_api.rs +++ b/src/gfx_api.rs @@ -145,6 +145,8 @@ pub struct CopyTexture { pub source: SampleRect, pub target: FramebufferRect, pub buffer_resv: Option>, + pub acquire_sync: AcquireSync, + pub release_sync: ReleaseSync, } #[derive(Clone, Debug)] @@ -160,6 +162,40 @@ impl Deref for SyncFile { unsafe impl UnsafeCellCloneSafe for SyncFile {} +#[derive(Clone)] +pub enum AcquireSync { + None, + Implicit, + SyncFile { sync_file: SyncFile }, +} + +impl AcquireSync { + pub fn from_sync_file(sync_file: Option) -> Self { + match sync_file { + None => Self::Implicit, + Some(sync_file) => Self::SyncFile { sync_file }, + } + } +} + +#[derive(Copy, Clone, Eq, PartialEq)] +pub enum ReleaseSync { + None, + Implicit, + Explicit, +} + +impl Debug for AcquireSync { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + let name = match self { + AcquireSync::None => "None", + AcquireSync::Implicit => "Implicit", + AcquireSync::SyncFile { .. } => "SyncFile", + }; + f.debug_struct(name).finish_non_exhaustive() + } +} + pub trait BufferResv: Debug { fn set_sync_file(&self, user: BufferResvUser, sync_file: &SyncFile); } @@ -189,7 +225,11 @@ pub trait GfxFramebuffer: Debug { fn physical_size(&self) -> (i32, i32); - fn render(&self, ops: Vec, clear: Option<&Color>) -> Result<(), GfxError>; + fn render( + &self, + ops: Vec, + clear: Option<&Color>, + ) -> Result, GfxError>; fn copy_to_shm( self: Rc, @@ -206,11 +246,11 @@ pub trait GfxFramebuffer: Debug { } impl dyn GfxFramebuffer { - pub fn clear(&self) -> Result<(), GfxError> { + pub fn clear(&self) -> Result, GfxError> { self.clear_with(0.0, 0.0, 0.0, 0.0) } - pub fn clear_with(&self, r: f32, g: f32, b: f32, a: f32) -> Result<(), GfxError> { + pub fn clear_with(&self, r: f32, g: f32, b: f32, a: f32) -> Result, GfxError> { let ops = self.take_render_ops(); self.render(ops, Some(&Color { r, g, b, a })) } @@ -240,13 +280,26 @@ impl dyn GfxFramebuffer { pub fn copy_texture( &self, texture: &Rc, + acquire_sync: AcquireSync, + release_sync: ReleaseSync, x: i32, y: i32, - ) -> Result<(), GfxError> { + ) -> Result, GfxError> { let mut ops = self.take_render_ops(); let scale = Scale::from_int(1); let mut renderer = self.renderer_base(&mut ops, scale, Transform::None); - renderer.render_texture(texture, x, y, None, None, scale, None, None); + renderer.render_texture( + texture, + x, + y, + None, + None, + scale, + None, + None, + acquire_sync, + release_sync, + ); let clear = self.format().has_alpha.then_some(&Color::TRANSPARENT); self.render(ops, clear) } @@ -256,7 +309,7 @@ impl dyn GfxFramebuffer { scale: Scale, clear: Option<&Color>, f: &mut dyn FnMut(&mut RendererBase), - ) -> Result<(), GfxError> { + ) -> Result, GfxError> { let mut ops = self.take_render_ops(); let mut renderer = self.renderer_base(&mut ops, scale, Transform::None); f(&mut renderer); @@ -331,7 +384,7 @@ impl dyn GfxFramebuffer { } } - pub fn perform_render_pass(&self, pass: GfxRenderPass) -> Result<(), GfxError> { + pub fn perform_render_pass(&self, pass: GfxRenderPass) -> Result, GfxError> { self.render(pass.ops, pass.clear.as_ref()) } @@ -343,7 +396,7 @@ impl dyn GfxFramebuffer { result: Option<&mut RenderResult>, scale: Scale, render_hardware_cursor: bool, - ) -> Result<(), GfxError> { + ) -> Result, GfxError> { self.render_node( node, state, @@ -366,7 +419,7 @@ impl dyn GfxFramebuffer { render_hardware_cursor: bool, black_background: bool, transform: Transform, - ) -> Result<(), GfxError> { + ) -> Result, GfxError> { let pass = self.create_render_pass( node, state, @@ -386,7 +439,7 @@ impl dyn GfxFramebuffer { state: &State, scale: Scale, transform: Transform, - ) -> Result<(), GfxError> { + ) -> Result, GfxError> { let mut ops = self.take_render_ops(); let mut renderer = Renderer { base: self.renderer_base(&mut ops, scale, transform), diff --git a/src/gfx_apis/gl.rs b/src/gfx_apis/gl.rs index ecfa58fc..db1dbfc4 100644 --- a/src/gfx_apis/gl.rs +++ b/src/gfx_apis/gl.rs @@ -68,8 +68,8 @@ macro_rules! dynload { use { crate::{ gfx_api::{ - CopyTexture, FillRect, FramebufferRect, GfxApiOpt, GfxContext, GfxError, GfxTexture, - SampleRect, + AcquireSync, CopyTexture, FillRect, GfxApiOpt, GfxContext, GfxError, GfxTexture, + ReleaseSync, SyncFile, }, gfx_apis::gl::{ gl::texture::image_target, @@ -80,8 +80,9 @@ use { }, }, theme::Color, - utils::{rc_eq::rc_eq, vecstorage::VecStorage}, + utils::{errorfmt::ErrorFmt, rc_eq::rc_eq, vecstorage::VecStorage}, video::{ + dmabuf::DMA_BUF_SYNC_READ, drm::{Drm, DrmError}, gbm::GbmError, }, @@ -181,6 +182,14 @@ enum RenderError { NoSupportedFormats, #[error("Cannot convert a shm texture into a framebuffer")] ShmTextureToFb, + #[error("Could not create EGLSyncKHR")] + CreateEglSync, + #[error("Could not destroy EGLSyncKHR")] + DestroyEglSync, + #[error("Could not export sync file")] + ExportSyncFile, + #[error("Could not insert wait for EGLSyncKHR")] + WaitSync, } #[derive(Default)] @@ -190,7 +199,7 @@ struct GfxGlState { copy_tex: VecStorage<&'static CopyTexture>, } -fn run_ops(fb: &Framebuffer, ops: &[GfxApiOpt]) { +fn run_ops(fb: &Framebuffer, ops: &[GfxApiOpt]) -> Option { let mut state = fb.ctx.gl_state.borrow_mut(); let state = &mut *state; let mut fill_rect = state.fill_rect.take(); @@ -256,9 +265,30 @@ fn run_ops(fb: &Framebuffer, ops: &[GfxApiOpt]) { } } for tex in &*copy_tex { - render_texture(&fb.ctx, &tex.tex.as_gl(), &tex.target, &tex.source) + render_texture(&fb.ctx, tex); } } + if fb.ctx.ctx.dpy.explicit_sync { + let file = match fb.ctx.ctx.export_sync_file() { + Ok(f) => SyncFile(Rc::new(f)), + Err(e) => { + log::error!("Could not create sync file: {}", ErrorFmt(e)); + return None; + } + }; + let user = fb.ctx.buffer_resv_user; + for op in ops { + if let GfxApiOpt::CopyTexture(ct) = op { + if ct.release_sync == ReleaseSync::Explicit { + if let Some(resv) = &ct.buffer_resv { + resv.set_sync_file(user, &file); + } + } + } + } + return Some(file); + } + None } fn fill_boxes3(ctx: &GlRenderContext, boxes: &[[f32; 2]], color: &Color) { @@ -280,15 +310,13 @@ fn fill_boxes3(ctx: &GlRenderContext, boxes: &[[f32; 2]], color: &Color) { } } -fn render_texture( - ctx: &GlRenderContext, - texture: &Texture, - target_rect: &FramebufferRect, - src: &SampleRect, -) { +fn render_texture(ctx: &GlRenderContext, tex: &CopyTexture) { + let texture = tex.tex.as_gl(); assert!(rc_eq(&ctx.ctx, &texture.ctx.ctx)); let gles = ctx.ctx.dpy.gles; unsafe { + handle_explicit_sync(ctx, texture, &tex.acquire_sync); + (gles.glActiveTexture)(GL_TEXTURE0); let target = image_target(texture.gl.external_only); @@ -321,8 +349,8 @@ fn render_texture( (gles.glUniform1i)(prog.tex, 0); - let texcoord = src.to_points(); - let pos = target_rect.to_points(); + let texcoord = tex.source.to_points(); + let pos = tex.target.to_points(); (gles.glVertexAttribPointer)( prog.texcoord as _, @@ -346,6 +374,36 @@ fn render_texture( } } +fn handle_explicit_sync(ctx: &GlRenderContext, texture: &Texture, sync: &AcquireSync) { + let sync_file = match sync { + AcquireSync::None | AcquireSync::Implicit => return, + AcquireSync::SyncFile { sync_file } => sync_file, + }; + let sync_file = match uapi::fcntl_dupfd_cloexec(sync_file.raw(), 0) { + Ok(s) => s, + Err(e) => { + log::error!("Could not dup sync file: {}", ErrorFmt(e)); + return; + } + }; + if ctx.ctx.dpy.explicit_sync { + let sync = match ctx.ctx.create_sync(Some(sync_file)) { + Ok(s) => s, + Err(e) => { + log::error!("Could import sync file: {}", ErrorFmt(e)); + return; + } + }; + sync.wait(); + } else { + if let Some(img) = &texture.gl.img { + if let Err(e) = img.dmabuf.import_sync_file(DMA_BUF_SYNC_READ, &sync_file) { + log::error!("Could not import sync file into dmabuf: {}", ErrorFmt(e)); + } + } + } +} + impl dyn GfxTexture { fn as_gl(&self) -> &Texture { self.as_any() diff --git a/src/gfx_apis/gl/egl/display.rs b/src/gfx_apis/gl/egl/display.rs index 7237dc32..80e4a91c 100644 --- a/src/gfx_apis/gl/egl/display.rs +++ b/src/gfx_apis/gl/egl/display.rs @@ -24,9 +24,10 @@ use { PROCS, }, ext::{ - get_display_ext, get_gl_ext, DisplayExt, GlExt, EXT_CREATE_CONTEXT_ROBUSTNESS, - EXT_IMAGE_DMA_BUF_IMPORT_MODIFIERS, GL_OES_EGL_IMAGE, GL_OES_EGL_IMAGE_EXTERNAL, - KHR_IMAGE_BASE, KHR_NO_CONFIG_CONTEXT, KHR_SURFACELESS_CONTEXT, + get_display_ext, get_gl_ext, DisplayExt, GlExt, ANDROID_NATIVE_FENCE_SYNC, + EXT_CREATE_CONTEXT_ROBUSTNESS, EXT_IMAGE_DMA_BUF_IMPORT_MODIFIERS, + GL_OES_EGL_IMAGE, GL_OES_EGL_IMAGE_EXTERNAL, KHR_FENCE_SYNC, KHR_IMAGE_BASE, + KHR_NO_CONFIG_CONTEXT, KHR_SURFACELESS_CONTEXT, KHR_WAIT_SYNC, MESA_CONFIGLESS_CONTEXT, }, proc::ExtProc, @@ -65,6 +66,7 @@ pub struct EglDisplay { pub formats: AHashMap, pub gbm: Rc, pub dpy: EGLDisplay, + pub explicit_sync: bool, } impl EglDisplay { @@ -99,6 +101,7 @@ impl EglDisplay { formats: AHashMap::new(), gbm: Rc::new(gbm), dpy, + explicit_sync: false, }; let mut major = 0; let mut minor = 0; @@ -122,6 +125,9 @@ impl EglDisplay { return Err(RenderError::SurfacelessContext); } dpy.formats = query_formats(procs, dpy.dpy)?; + dpy.explicit_sync = dpy + .exts + .contains(KHR_FENCE_SYNC | KHR_WAIT_SYNC | ANDROID_NATIVE_FENCE_SYNC); Ok(Rc::new(dpy)) } diff --git a/src/gfx_apis/gl/egl/sys.rs b/src/gfx_apis/gl/egl/sys.rs index 044d4de5..0b4493e3 100644 --- a/src/gfx_apis/gl/egl/sys.rs +++ b/src/gfx_apis/gl/egl/sys.rs @@ -6,6 +6,7 @@ pub type EGLBoolean = c::c_uint; #[allow(dead_code)] pub type EGLuint64KHR = u64; pub type EGLAttrib = isize; +pub type EGLSyncKHR = *mut u8; egl_transparent!(EGLDisplay); egl_transparent!(EGLSurface); @@ -83,6 +84,8 @@ pub const EGL_DMA_BUF_PLANE3_MODIFIER_LO_EXT: EGLint = 0x3449; pub const EGL_DMA_BUF_PLANE3_MODIFIER_HI_EXT: EGLint = 0x344A; pub const EGL_IMAGE_PRESERVED_KHR: EGLint = 0x30D2; pub const EGL_LINUX_DMA_BUF_EXT: EGLint = 0x3270; +pub const EGL_SYNC_NATIVE_FENCE_ANDROID: EGLenum = 0x3144; +pub const EGL_SYNC_NATIVE_FENCE_FD_ANDROID: EGLint = 0x3145; dynload! { EGL: Egl from "libEGL.so" { diff --git a/src/gfx_apis/gl/ext.rs b/src/gfx_apis/gl/ext.rs index fdc23ea1..6967b4cd 100644 --- a/src/gfx_apis/gl/ext.rs +++ b/src/gfx_apis/gl/ext.rs @@ -78,6 +78,9 @@ bitflags! { KHR_SURFACELESS_CONTEXT = 1 << 5, IMG_CONTEXT_PRIORITY = 1 << 6, EXT_CREATE_CONTEXT_ROBUSTNESS = 1 << 7, + KHR_FENCE_SYNC = 1 << 8, + KHR_WAIT_SYNC = 1 << 9, + ANDROID_NATIVE_FENCE_SYNC = 1 << 10, } pub(crate) unsafe fn get_display_ext(dpy: EGLDisplay) -> DisplayExt { @@ -96,6 +99,9 @@ pub(crate) unsafe fn get_display_ext(dpy: EGLDisplay) -> DisplayExt { "EGL_EXT_create_context_robustness", EXT_CREATE_CONTEXT_ROBUSTNESS, ), + ("EGL_KHR_fence_sync", KHR_FENCE_SYNC), + ("EGL_KHR_wait_sync", KHR_WAIT_SYNC), + ("EGL_ANDROID_native_fence_sync", ANDROID_NATIVE_FENCE_SYNC), ]; match get_dpy_extensions(dpy) { Some(exts) => get_typed_ext(&exts, DisplayExt::none(), &map), diff --git a/src/gfx_apis/gl/renderer.rs b/src/gfx_apis/gl/renderer.rs index eaa9bc1d..9183d7b7 100644 --- a/src/gfx_apis/gl/renderer.rs +++ b/src/gfx_apis/gl/renderer.rs @@ -1,4 +1,5 @@ pub(super) mod context; pub(super) mod framebuffer; pub(super) mod image; +pub(super) mod sync; pub(super) mod texture; diff --git a/src/gfx_apis/gl/renderer/context.rs b/src/gfx_apis/gl/renderer/context.rs index bba4a6b4..8b931223 100644 --- a/src/gfx_apis/gl/renderer/context.rs +++ b/src/gfx_apis/gl/renderer/context.rs @@ -2,8 +2,8 @@ use { crate::{ format::{Format, XRGB8888}, gfx_api::{ - GfxApiOpt, GfxContext, GfxError, GfxFormat, GfxFramebuffer, GfxImage, GfxTexture, - ResetStatus, + BufferResvUser, GfxApiOpt, GfxContext, GfxError, GfxFormat, GfxFramebuffer, GfxImage, + GfxTexture, ResetStatus, }, gfx_apis::gl::{ egl::{context::EglContext, display::EglDisplay, image::EglImage}, @@ -65,6 +65,8 @@ pub(in crate::gfx_apis::gl) struct GlRenderContext { pub(crate) gfx_ops: RefCell>, pub(in crate::gfx_apis::gl) gl_state: RefCell, + + pub(in crate::gfx_apis::gl) buffer_resv_user: BufferResvUser, } impl Debug for GlRenderContext { @@ -141,6 +143,8 @@ impl GlRenderContext { gfx_ops: Default::default(), gl_state: Default::default(), + + buffer_resv_user: Default::default(), }) } diff --git a/src/gfx_apis/gl/renderer/framebuffer.rs b/src/gfx_apis/gl/renderer/framebuffer.rs index b409118c..26e00faf 100644 --- a/src/gfx_apis/gl/renderer/framebuffer.rs +++ b/src/gfx_apis/gl/renderer/framebuffer.rs @@ -1,7 +1,7 @@ use { crate::{ format::Format, - gfx_api::{GfxApiOpt, GfxError, GfxFramebuffer}, + gfx_api::{GfxApiOpt, GfxError, GfxFramebuffer, SyncFile}, gfx_apis::gl::{ gl::{ frame_buffer::GlFrameBuffer, @@ -65,7 +65,11 @@ impl Framebuffer { }); } - pub fn render(&self, ops: Vec, clear: Option<&Color>) -> Result<(), RenderError> { + pub fn render( + &self, + ops: Vec, + clear: Option<&Color>, + ) -> Result, RenderError> { let gles = self.ctx.ctx.dpy.gles; let res = self.ctx.ctx.with_current(|| { unsafe { @@ -77,11 +81,13 @@ impl Framebuffer { } (gles.glBlendFunc)(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); } - run_ops(self, &ops); - unsafe { - (gles.glFlush)(); + let fd = run_ops(self, &ops); + if fd.is_none() { + unsafe { + (gles.glFlush)(); + } } - Ok(()) + Ok(fd) }); *self.ctx.gfx_ops.borrow_mut() = ops; res @@ -103,7 +109,11 @@ impl GfxFramebuffer for Framebuffer { (self.gl.width, self.gl.height) } - fn render(&self, ops: Vec, clear: Option<&Color>) -> Result<(), GfxError> { + fn render( + &self, + ops: Vec, + clear: Option<&Color>, + ) -> Result, GfxError> { self.render(ops, clear).map_err(|e| e.into()) } diff --git a/src/gfx_apis/gl/renderer/sync.rs b/src/gfx_apis/gl/renderer/sync.rs new file mode 100644 index 00000000..588791bd --- /dev/null +++ b/src/gfx_apis/gl/renderer/sync.rs @@ -0,0 +1,107 @@ +use { + crate::{ + gfx_apis::gl::{ + egl::context::EglContext, + sys::{ + EGLBoolean, EGLSyncKHR, EGL_NONE, EGL_SYNC_NATIVE_FENCE_ANDROID, + EGL_SYNC_NATIVE_FENCE_FD_ANDROID, EGL_TRUE, + }, + RenderError, + }, + utils::errorfmt::ErrorFmt, + }, + std::rc::Rc, + uapi::OwnedFd, +}; + +pub struct EglSync { + ctx: Rc, + sync: EGLSyncKHR, +} + +impl EglContext { + pub fn export_sync_file(self: &Rc) -> Result { + self.create_sync(None)?.export_sync_file() + } + + pub fn create_sync(self: &Rc, file: Option) -> Result { + let mut attribs = [EGL_NONE; 3]; + if let Some(file) = &file { + attribs[0] = EGL_SYNC_NATIVE_FENCE_FD_ANDROID; + attribs[1] = file.raw(); + } + self.with_current(|| unsafe { + let sync = self.dpy.procs.eglCreateSyncKHR( + self.dpy.dpy, + EGL_SYNC_NATIVE_FENCE_ANDROID, + attribs.as_ptr(), + ); + if sync.is_null() { + Err(RenderError::CreateEglSync) + } else { + if let Some(file) = file { + file.unwrap(); + } + Ok(EglSync { + ctx: self.clone(), + sync, + }) + } + }) + } +} + +impl EglSync { + pub fn wait(&self) { + let res = self.ctx.with_current(|| unsafe { + let res = self + .ctx + .dpy + .procs + .eglWaitSyncKHR(self.ctx.dpy.dpy, self.sync, 0); + if res as EGLBoolean == EGL_TRUE { + Ok(()) + } else { + Err(RenderError::WaitSync) + } + }); + if let Err(e) = res { + log::warn!("Could not insert wait point: {}", ErrorFmt(e)); + } + } + + pub fn export_sync_file(&self) -> Result { + self.ctx.with_current(|| unsafe { + let fd = self + .ctx + .dpy + .procs + .eglDupNativeFenceFDANDROID(self.ctx.dpy.dpy, self.sync); + if fd == -1 { + Err(RenderError::ExportSyncFile) + } else { + Ok(OwnedFd::new(fd)) + } + }) + } +} + +impl Drop for EglSync { + fn drop(&mut self) { + let res = self.ctx.with_current(|| unsafe { + let res = self + .ctx + .dpy + .procs + .eglDestroySyncKHR(self.ctx.dpy.dpy, self.sync); + if res == EGL_TRUE { + Ok(()) + } else { + Err(RenderError::DestroyEglSync) + } + }); + if let Err(e) = res { + log::error!("{}", ErrorFmt(e)); + } + } +} diff --git a/src/gfx_apis/vulkan/image.rs b/src/gfx_apis/vulkan/image.rs index 53cb0351..ce09554e 100644 --- a/src/gfx_apis/vulkan/image.rs +++ b/src/gfx_apis/vulkan/image.rs @@ -1,7 +1,7 @@ use { crate::{ format::Format, - gfx_api::{GfxApiOpt, GfxError, GfxFramebuffer, GfxImage, GfxTexture}, + gfx_api::{GfxApiOpt, GfxError, GfxFramebuffer, GfxImage, GfxTexture, SyncFile}, gfx_apis::vulkan::{ allocator::VulkanAllocation, device::VulkanDevice, format::VulkanMaxExtents, renderer::VulkanRenderer, util::OnDrop, VulkanError, @@ -525,7 +525,11 @@ impl GfxFramebuffer for VulkanImage { (self.width as _, self.height as _) } - fn render(&self, ops: Vec, clear: Option<&Color>) -> Result<(), GfxError> { + fn render( + &self, + ops: Vec, + clear: Option<&Color>, + ) -> Result, GfxError> { self.renderer .execute(self, &ops, clear) .map_err(|e| e.into()) diff --git a/src/gfx_apis/vulkan/renderer.rs b/src/gfx_apis/vulkan/renderer.rs index 6b4cf5f4..e8529fdd 100644 --- a/src/gfx_apis/vulkan/renderer.rs +++ b/src/gfx_apis/vulkan/renderer.rs @@ -3,7 +3,8 @@ use { async_engine::SpawnedFuture, format::Format, gfx_api::{ - BufferResv, BufferResvUser, GfxApiOpt, GfxFormat, GfxFramebuffer, GfxTexture, SyncFile, + AcquireSync, BufferResv, BufferResvUser, GfxApiOpt, GfxFormat, GfxFramebuffer, + GfxTexture, ReleaseSync, SyncFile, }, gfx_apis::vulkan::{ allocator::VulkanAllocator, @@ -48,6 +49,7 @@ use { rc::Rc, slice, }, + uapi::OwnedFd, }; pub struct VulkanRenderer { @@ -70,6 +72,8 @@ pub struct VulkanRenderer { pub(super) struct UsedTexture { tex: Rc, resv: Option>, + acquire_sync: AcquireSync, + release_sync: ReleaseSync, } #[derive(Default)] @@ -185,6 +189,8 @@ impl VulkanRenderer { memory.textures.push(UsedTexture { tex, resv: c.buffer_resv.clone(), + acquire_sync: c.acquire_sync.clone(), + release_sync: c.release_sync, }); } } @@ -538,12 +544,11 @@ impl VulkanRenderer { let import = |infos: &mut Vec, semaphores: &mut Vec>, img: &VulkanImage, + sync: &AcquireSync, flag: u32| -> Result<(), VulkanError> { if let VulkanImageMemory::DmaBuf(buf) = &img.ty { - for plane in &buf.template.dmabuf.planes { - let fd = dma_buf_export_sync_file(&plane.fd, flag) - .map_err(VulkanError::IoctlExportSyncFile)?; + let mut import_sync_file = |fd: OwnedFd| -> Result<(), VulkanError> { let semaphore = self.allocate_semaphore()?; semaphore.import_sync_file(fd)?; infos.push( @@ -553,6 +558,22 @@ impl VulkanRenderer { .build(), ); semaphores.push(semaphore); + Ok(()) + }; + match sync { + AcquireSync::None => {} + AcquireSync::Implicit { .. } => { + for plane in &buf.template.dmabuf.planes { + let fd = dma_buf_export_sync_file(&plane.fd, flag) + .map_err(VulkanError::IoctlExportSyncFile)?; + import_sync_file(fd)?; + } + } + AcquireSync::SyncFile { sync_file } => { + let fd = uapi::fcntl_dupfd_cloexec(sync_file.raw(), 0) + .map_err(|e| VulkanError::Dupfd(e.into()))?; + import_sync_file(fd)?; + } } } Ok(()) @@ -562,6 +583,7 @@ impl VulkanRenderer { &mut memory.wait_semaphore_infos, &mut memory.wait_semaphores, &texture.tex, + &texture.acquire_sync, DMA_BUF_SYNC_READ, )?; } @@ -569,6 +591,7 @@ impl VulkanRenderer { &mut memory.wait_semaphore_infos, &mut memory.wait_semaphores, fb, + &AcquireSync::Implicit, DMA_BUF_SYNC_WRITE, )?; Ok(()) @@ -580,20 +603,31 @@ impl VulkanRenderer { Some(sync_file) => sync_file, _ => return, }; - let import = |img: &VulkanImage, resv: Option>, flag: u32| { - if let Some(resv) = resv { - resv.set_sync_file(self.buffer_resv_user, sync_file); - } else if let VulkanImageMemory::DmaBuf(buf) = &img.ty { - if let Err(e) = buf.template.dmabuf.import_sync_file(flag, sync_file) { - log::error!("Could not import sync file into dmabuf: {}", ErrorFmt(e)); - log::warn!("Relying on implicit sync"); + let import = + |img: &VulkanImage, sync: ReleaseSync, resv: Option>, flag: u32| { + if sync == ReleaseSync::None { + return; } - } - }; + if let Some(resv) = resv { + resv.set_sync_file(self.buffer_resv_user, sync_file); + } else if sync == ReleaseSync::Implicit { + if let VulkanImageMemory::DmaBuf(buf) = &img.ty { + if let Err(e) = buf.template.dmabuf.import_sync_file(flag, sync_file) { + log::error!("Could not import sync file into dmabuf: {}", ErrorFmt(e)); + log::warn!("Relying on implicit sync"); + } + } + } + }; for texture in &mut memory.textures { - import(&texture.tex, texture.resv.take(), DMA_BUF_SYNC_READ); + import( + &texture.tex, + texture.release_sync, + texture.resv.take(), + DMA_BUF_SYNC_READ, + ); } - import(fb, None, DMA_BUF_SYNC_WRITE); + import(fb, ReleaseSync::Implicit, None, DMA_BUF_SYNC_WRITE); } fn submit(&self, buf: CommandBuffer) -> Result<(), VulkanError> { @@ -656,7 +690,7 @@ impl VulkanRenderer { }); self.pending_frames.set(frame.point, frame.clone()); let future = self.device.instance.eng.spawn(await_release( - memory.release_sync_file.take(), + memory.release_sync_file.clone(), self.device.instance.ring.clone(), frame.clone(), self.clone(), @@ -699,7 +733,13 @@ impl VulkanRenderer { true, )?; (&*tmp_tex as &dyn GfxFramebuffer) - .copy_texture(&(tex.clone() as _), x, y) + .copy_texture( + &(tex.clone() as _), + AcquireSync::None, + ReleaseSync::None, + x, + y, + ) .map_err(VulkanError::GfxError)?; self.read_all_pixels(&tmp_tex, stride, dst) } @@ -839,9 +879,9 @@ impl VulkanRenderer { fb: &VulkanImage, opts: &[GfxApiOpt], clear: Option<&Color>, - ) -> Result<(), VulkanError> { + ) -> Result, VulkanError> { let res = self.try_execute(fb, opts, clear); - { + let sync_file = { let mut memory = self.memory.borrow_mut(); memory.flush.clear(); memory.textures.clear(); @@ -849,9 +889,9 @@ impl VulkanRenderer { memory.sample.clear(); memory.wait_semaphores.clear(); memory.release_fence.take(); - memory.release_sync_file.take(); - } - res + memory.release_sync_file.take() + }; + res.map(|_| sync_file) } fn allocate_command_buffer(&self) -> Result, VulkanError> { diff --git a/src/gfx_apis/vulkan/semaphore.rs b/src/gfx_apis/vulkan/semaphore.rs index e9b03681..0e25b801 100644 --- a/src/gfx_apis/vulkan/semaphore.rs +++ b/src/gfx_apis/vulkan/semaphore.rs @@ -47,8 +47,8 @@ impl VulkanSemaphore { .external_semaphore_fd .import_semaphore_fd(&fd_info) }; - mem::forget(sync_file); res.map_err(VulkanError::ImportSyncFile)?; + mem::forget(sync_file); Ok(()) } } diff --git a/src/ifs/wl_seat.rs b/src/ifs/wl_seat.rs index 4e18cf6e..3ed55acb 100644 --- a/src/ifs/wl_seat.rs +++ b/src/ifs/wl_seat.rs @@ -306,7 +306,8 @@ impl WlSeatGlobal { transform, ); match res { - Ok(_) => { + Ok(sync_file) => { + hc.set_sync_file(sync_file); hc.swap_buffer(); } Err(e) => { diff --git a/src/ifs/wl_surface.rs b/src/ifs/wl_surface.rs index e003ff2c..6acb2f11 100644 --- a/src/ifs/wl_surface.rs +++ b/src/ifs/wl_surface.rs @@ -16,7 +16,7 @@ use { client::{Client, ClientError, RequestParser}, drm_feedback::DrmFeedback, fixed::Fixed, - gfx_api::{BufferResv, BufferResvUser, SampleRect, SyncFile}, + gfx_api::{AcquireSync, BufferResv, BufferResvUser, SampleRect, SyncFile}, ifs::{ wl_buffer::WlBuffer, wl_callback::WlCallback, @@ -136,6 +136,7 @@ impl NodeVisitorBase for SurfaceSendPreferredTransformVisitor { pub struct SurfaceBuffer { pub buffer: Rc, sync_files: SmallMap, + pub sync: AcquireSync, } impl Drop for SurfaceBuffer { @@ -837,6 +838,7 @@ impl WlSurface { let surface_buffer = SurfaceBuffer { buffer, sync_files: Default::default(), + sync: AcquireSync::Implicit, }; self.buffer.set(Some(Rc::new(surface_buffer))); self.buf_x.fetch_add(dx); diff --git a/src/portal/ptr_gui.rs b/src/portal/ptr_gui.rs index 570d671c..02a209cf 100644 --- a/src/portal/ptr_gui.rs +++ b/src/portal/ptr_gui.rs @@ -4,7 +4,7 @@ use { cursor::KnownCursor, fixed::Fixed, format::ARGB8888, - gfx_api::{GfxContext, GfxFramebuffer}, + gfx_api::{AcquireSync, GfxContext, GfxFramebuffer, ReleaseSync}, ifs::zwlr_layer_shell_v1::OVERLAY, portal::ptl_display::{PortalDisplay, PortalOutput, PortalSeat}, renderer::renderer_base::RendererBase, @@ -223,6 +223,8 @@ impl GuiElement for Button { r.scale(), None, None, + AcquireSync::None, + ReleaseSync::None, ); } } @@ -325,6 +327,8 @@ impl GuiElement for Label { r.scale(), None, None, + AcquireSync::None, + ReleaseSync::None, ); } } diff --git a/src/renderer.rs b/src/renderer.rs index 8fe6eeee..95c14a6d 100644 --- a/src/renderer.rs +++ b/src/renderer.rs @@ -1,6 +1,6 @@ use { crate::{ - gfx_api::{GfxApiOpt, SampleRect}, + gfx_api::{AcquireSync, GfxApiOpt, ReleaseSync, SampleRect}, ifs::{ wl_callback::WlCallback, wl_surface::{ @@ -151,13 +151,33 @@ impl Renderer<'_> { let scale = output.global.persistent.scale.get(); for title in &rd.titles { let (x, y) = self.base.scale_point(x + title.tex_x, y + title.tex_y); - self.base - .render_texture(&title.tex, x, y, None, None, scale, None, None); + self.base.render_texture( + &title.tex, + x, + y, + None, + None, + scale, + None, + None, + AcquireSync::None, + ReleaseSync::None, + ); } if let Some(status) = &rd.status { let (x, y) = self.base.scale_point(x + status.tex_x, y + status.tex_y); - self.base - .render_texture(&status.tex.texture, x, y, None, None, scale, None, None); + self.base.render_texture( + &status.tex.texture, + x, + y, + None, + None, + scale, + None, + None, + AcquireSync::None, + ReleaseSync::None, + ); } } if let Some(ws) = output.workspace.get() { @@ -193,8 +213,18 @@ impl Renderer<'_> { let (tex_width, tex_height) = tex.texture.size(); let x = x + (pos.width() - tex_width) / 2; let y = y + (pos.height() - tex_height) / 2; - self.base - .render_texture(&tex.texture, x, y, None, None, self.base.scale, None, None); + self.base.render_texture( + &tex.texture, + x, + y, + None, + None, + self.base.scale, + None, + None, + AcquireSync::None, + ReleaseSync::None, + ); } } @@ -232,6 +262,8 @@ impl Renderer<'_> { self.base.scale, None, None, + AcquireSync::None, + ReleaseSync::None, ); } } @@ -354,6 +386,10 @@ impl Renderer<'_> { bounds: Option<&Rect>, ) { if let Some(tex) = buffer.buffer.texture.get() { + let release_sync = match buffer.sync { + AcquireSync::Implicit => ReleaseSync::Implicit, + AcquireSync::None | AcquireSync::SyncFile { .. } => ReleaseSync::Explicit, + }; self.base.render_texture( &tex, x, @@ -363,6 +399,8 @@ impl Renderer<'_> { self.base.scale, bounds, Some(buffer.clone()), + buffer.sync.clone(), + release_sync, ); } else if let Some(color) = &buffer.buffer.color { if let Some(rect) = Rect::new_sized(x, y, tsize.0, tsize.1) { @@ -420,6 +458,8 @@ impl Renderer<'_> { self.base.scale, None, None, + AcquireSync::None, + ReleaseSync::None, ); } let body = Rect::new_sized( diff --git a/src/renderer/renderer_base.rs b/src/renderer/renderer_base.rs index ca9c622b..759a8901 100644 --- a/src/renderer/renderer_base.rs +++ b/src/renderer/renderer_base.rs @@ -1,7 +1,8 @@ use { crate::{ gfx_api::{ - BufferResv, CopyTexture, FillRect, FramebufferRect, GfxApiOpt, GfxTexture, SampleRect, + AcquireSync, BufferResv, CopyTexture, FillRect, FramebufferRect, GfxApiOpt, GfxTexture, + ReleaseSync, SampleRect, }, rect::Rect, scale::Scale, @@ -133,6 +134,8 @@ impl RendererBase<'_> { tscale: Scale, bounds: Option<&Rect>, buffer_resv: Option>, + acquire_sync: AcquireSync, + release_sync: ReleaseSync, ) { let mut texcoord = tpoints.unwrap_or_else(SampleRect::identity); @@ -172,6 +175,8 @@ impl RendererBase<'_> { source: texcoord, target, buffer_resv, + acquire_sync, + release_sync, })); } } diff --git a/src/state.rs b/src/state.rs index 2ac63154..d365022e 100644 --- a/src/state.rs +++ b/src/state.rs @@ -17,7 +17,10 @@ use { fixed::Fixed, forker::ForkerProxy, format::Format, - gfx_api::{GfxContext, GfxError, GfxFramebuffer, GfxTexture, SampleRect}, + gfx_api::{ + AcquireSync, GfxContext, GfxError, GfxFramebuffer, GfxTexture, ReleaseSync, SampleRect, + SyncFile, + }, gfx_apis::create_gfx_context, globals::{Globals, GlobalsError, WaylandGlobal}, ifs::{ @@ -770,8 +773,8 @@ impl State { tex: &Rc, rr: &mut RenderResult, render_hw_cursor: bool, - ) -> Result<(), GfxError> { - fb.render_output( + ) -> Result, GfxError> { + let sync_file = fb.render_output( output, self, Some(output.global.pos.get()), @@ -781,7 +784,7 @@ impl State { )?; output.perform_screencopies(tex, !render_hw_cursor, 0, 0, None); rr.dispatch_frame_requests(); - Ok(()) + Ok(sync_file) } pub fn perform_screencopy( @@ -794,7 +797,7 @@ impl State { y_off: i32, size: Option<(i32, i32)>, transform: Transform, - ) -> Result<(), GfxError> { + ) -> Result, GfxError> { let mut ops = target.take_render_ops(); let mut renderer = Renderer { base: target.renderer_base(&mut ops, Scale::from_int(1), Transform::None), @@ -817,6 +820,8 @@ impl State { Scale::from_int(1), None, None, + AcquireSync::None, + ReleaseSync::Implicit, ); if render_hardware_cursors { for seat in self.globals.lock_seats().values() {