Merge pull request #137 from mahkoh/jorth/explicit-sync
Implement explicit sync
This commit is contained in:
commit
9b1a85b27d
80 changed files with 2923 additions and 592 deletions
|
|
@ -42,6 +42,7 @@ The following features have been implemented and should work:
|
|||
- Selecting the primary device in multi-GPU systems
|
||||
- An OpenGL backend
|
||||
- A Vulkan backend
|
||||
- Explicit sync
|
||||
|
||||
### Missing Features
|
||||
|
||||
|
|
|
|||
28
build/egl.rs
28
build/egl.rs
|
|
@ -80,6 +80,34 @@ fn write_egl_procs<W: Write>(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;")?;
|
||||
|
|
|
|||
|
|
@ -813,7 +813,7 @@ struct Extension {
|
|||
#[derive(Debug)]
|
||||
enum NamedType {
|
||||
Struct(Rc<Struct>),
|
||||
Bitmask(Rc<Bitmask>),
|
||||
Bitmask(#[allow(dead_code)] Rc<Bitmask>),
|
||||
Enum(Rc<Enum>),
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -810,6 +810,10 @@ impl Client {
|
|||
self.send(&ClientMessage::SetIdle { timeout })
|
||||
}
|
||||
|
||||
pub fn set_explicit_sync_enabled(&self, enabled: bool) {
|
||||
self.send(&ClientMessage::SetExplicitSyncEnabled { enabled })
|
||||
}
|
||||
|
||||
pub fn set_seat(&self, device: InputDevice, seat: Seat) {
|
||||
self.send(&ClientMessage::SetSeat { device, seat })
|
||||
}
|
||||
|
|
|
|||
|
|
@ -428,6 +428,9 @@ pub enum ClientMessage<'a> {
|
|||
workspace: WorkspaceSource,
|
||||
connector: Connector,
|
||||
},
|
||||
SetExplicitSyncEnabled {
|
||||
enabled: bool,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
|
|
|
|||
|
|
@ -222,3 +222,12 @@ pub fn workspaces() -> Vec<Workspace> {
|
|||
pub fn set_idle(timeout: Option<Duration>) {
|
||||
get!().set_idle(timeout.unwrap_or_default())
|
||||
}
|
||||
|
||||
/// Enables or disables explicit sync.
|
||||
///
|
||||
/// Calling this after the compositor has started has no effect.
|
||||
///
|
||||
/// The default is `true`.
|
||||
pub fn set_explicit_sync_enabled(enabled: bool) {
|
||||
get!().set_explicit_sync_enabled(enabled);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<dyn GfxFramebuffer>;
|
||||
fn set_position(&self, x: i32, y: i32);
|
||||
fn swap_buffer(&self);
|
||||
fn set_sync_file(&self, sync_file: Option<SyncFile>);
|
||||
fn commit(&self);
|
||||
fn size(&self) -> (i32, i32);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -114,6 +114,14 @@ pub enum MetalError {
|
|||
MissingDevModifier(&'static str),
|
||||
#[error("Device GFX API cannot read any buffers writable by the render GFX API (format {0})")]
|
||||
MissingRenderModifier(&'static str),
|
||||
#[error("Could not render the frame")]
|
||||
RenderFrame(#[source] GfxError),
|
||||
#[error("Could not copy frame to output device")]
|
||||
CopyToOutput(#[source] GfxError),
|
||||
#[error("Could not perform atomic commit")]
|
||||
Commit(#[source] DrmError),
|
||||
#[error("Could not clear framebuffer")]
|
||||
Clear(#[source] GfxError),
|
||||
}
|
||||
|
||||
pub struct MetalBackend {
|
||||
|
|
|
|||
|
|
@ -9,7 +9,10 @@ use {
|
|||
drm_feedback::DrmFeedback,
|
||||
edid::Descriptor,
|
||||
format::{Format, ARGB8888, XRGB8888},
|
||||
gfx_api::{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<Option<Rc<[RenderBuffer; 3]>>>,
|
||||
pub cursor_front_buffer: NumCell<usize>,
|
||||
pub cursor_swap_buffer: Cell<bool>,
|
||||
pub cursor_sync_file: CloneCell<Option<SyncFile>>,
|
||||
|
||||
pub drm_feedback: CloneCell<Option<Rc<DrmFeedback>>>,
|
||||
pub scanout_buffers: RefCell<AHashMap<DmaBufId, DirectScanoutCache>>,
|
||||
|
|
@ -244,6 +248,7 @@ pub struct MetalHardwareCursor {
|
|||
pub cursor_x_pending: Cell<i32>,
|
||||
pub cursor_y_pending: Cell<i32>,
|
||||
pub cursor_buffers: Rc<[RenderBuffer; 3]>,
|
||||
pub sync_file: CloneCell<Option<SyncFile>>,
|
||||
pub have_changes: Cell<bool>,
|
||||
}
|
||||
|
||||
|
|
@ -270,6 +275,11 @@ impl HardwareCursor for MetalHardwareCursor {
|
|||
self.have_changes.set(true);
|
||||
}
|
||||
|
||||
fn set_sync_file(&self, sync_file: Option<SyncFile>) {
|
||||
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,9 +361,10 @@ pub struct DirectScanoutCache {
|
|||
#[derive(Debug)]
|
||||
pub struct DirectScanoutData {
|
||||
tex: Rc<dyn GfxTexture>,
|
||||
acquire_sync: AcquireSync,
|
||||
_resv: Option<Rc<dyn BufferResv>>,
|
||||
fb: Rc<DrmFramebuffer>,
|
||||
dma_buf_id: DmaBufId,
|
||||
acquired: Cell<bool>,
|
||||
position: DirectScanoutPosition,
|
||||
}
|
||||
|
||||
|
|
@ -366,25 +378,20 @@ pub struct DirectScanoutPosition {
|
|||
pub crtc_height: i32,
|
||||
}
|
||||
|
||||
impl Drop for DirectScanoutData {
|
||||
fn drop(&mut self) {
|
||||
if self.acquired.replace(false) {
|
||||
self.tex.reservations().release();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct PresentFb {
|
||||
fb: Rc<DrmFramebuffer>,
|
||||
direct_scanout_data: Option<DirectScanoutData>,
|
||||
sync_file: Option<SyncFile>,
|
||||
}
|
||||
|
||||
impl MetalConnector {
|
||||
async fn present_loop(self: Rc<Self>) {
|
||||
loop {
|
||||
self.present_trigger.triggered().await;
|
||||
let _ = self.present(true);
|
||||
if let Err(e) = self.present(true) {
|
||||
log::error!("Could not present: {}", ErrorFmt(e));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -402,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,
|
||||
|
|
@ -480,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;
|
||||
|
|
@ -532,9 +544,10 @@ 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,
|
||||
acquired: Default::default(),
|
||||
position,
|
||||
});
|
||||
}
|
||||
|
|
@ -556,9 +569,10 @@ 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,
|
||||
acquired: Default::default(),
|
||||
position,
|
||||
}),
|
||||
Err(e) => {
|
||||
|
|
@ -593,7 +607,7 @@ impl MetalConnector {
|
|||
plane: &Rc<MetalPlane>,
|
||||
output: &OutputNode,
|
||||
try_direct_scanout: bool,
|
||||
) -> PresentFb {
|
||||
) -> Result<PresentFb, MetalError> {
|
||||
self.trim_scanout_cache();
|
||||
let buffer_fb = buffer.render_fb();
|
||||
let render_hw_cursor = !self.cursor_enabled.get();
|
||||
|
|
@ -636,25 +650,36 @@ 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 => {
|
||||
let sf = buffer_fb
|
||||
.perform_render_pass(pass)
|
||||
.map_err(MetalError::RenderFrame)?;
|
||||
sync_file = buffer.copy_to_dev(sf)?;
|
||||
self.next_buffer.fetch_add(1);
|
||||
buffer_fb.perform_render_pass(pass);
|
||||
if let Some(tex) = &buffer.dev_tex {
|
||||
buffer.dev_fb.copy_texture(tex, 0, 0);
|
||||
}
|
||||
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()),
|
||||
AcquireSync::Unnecessary => None,
|
||||
};
|
||||
fb = dsd.fb.clone();
|
||||
}
|
||||
Some(dsd) => dsd.fb.clone(),
|
||||
};
|
||||
PresentFb {
|
||||
Ok(PresentFb {
|
||||
fb,
|
||||
direct_scanout_data,
|
||||
}
|
||||
sync_file,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn present(&self, try_direct_scanout: bool) -> Result<(), ()> {
|
||||
pub fn present(&self, try_direct_scanout: bool) -> Result<(), MetalError> {
|
||||
let crtc = match self.crtc.get() {
|
||||
Some(crtc) => crtc,
|
||||
_ => return Ok(()),
|
||||
|
|
@ -684,7 +709,7 @@ impl MetalConnector {
|
|||
let buffer = &buffers[self.next_buffer.get() % buffers.len()];
|
||||
let mut rr = self.render_result.borrow_mut();
|
||||
let fb =
|
||||
self.prepare_present_fb(&mut rr, buffer, &plane, &node, try_direct_scanout);
|
||||
self.prepare_present_fb(&mut rr, buffer, &plane, &node, try_direct_scanout)?;
|
||||
rr.dispatch_frame_requests();
|
||||
let (crtc_x, crtc_y, crtc_w, crtc_h, src_width, src_height) =
|
||||
match &fb.direct_scanout_data {
|
||||
|
|
@ -705,6 +730,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);
|
||||
|
|
@ -713,24 +739,28 @@ 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() {
|
||||
let swap_buffer = self.cursor_swap_buffer.take();
|
||||
if swap_buffer {
|
||||
self.cursor_front_buffer.fetch_add(1);
|
||||
cursor_swap_buffer = self.cursor_swap_buffer.get();
|
||||
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[self.cursor_front_buffer.get() % buffers.len()];
|
||||
if swap_buffer {
|
||||
if let Some(tex) = &buffer.dev_tex {
|
||||
buffer.dev_fb.copy_texture(tex, 0, 0);
|
||||
}
|
||||
let buffer = &buffers[front_buffer % buffers.len()];
|
||||
if cursor_swap_buffer {
|
||||
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 _);
|
||||
|
|
@ -743,6 +773,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| {
|
||||
|
|
@ -752,40 +783,37 @@ impl MetalConnector {
|
|||
}
|
||||
}
|
||||
if let Err(e) = changes.commit(DRM_MODE_ATOMIC_NONBLOCK | DRM_MODE_PAGE_FLIP_EVENT, 0) {
|
||||
match e {
|
||||
DrmError::Atomic(OsError(c::EACCES)) => {
|
||||
log::debug!("Could not perform atomic commit, likely because we're no longer the DRM master");
|
||||
}
|
||||
_ => 'handle_failure: {
|
||||
if let Some(fb) = &new_fb {
|
||||
if let Some(dsd) = &fb.direct_scanout_data {
|
||||
if self.present(false).is_ok() {
|
||||
let mut cache = self.scanout_buffers.borrow_mut();
|
||||
if let Some(buffer) = cache.remove(&dsd.dma_buf_id) {
|
||||
cache.insert(
|
||||
dsd.dma_buf_id,
|
||||
DirectScanoutCache {
|
||||
tex: buffer.tex,
|
||||
fb: None,
|
||||
},
|
||||
);
|
||||
}
|
||||
break 'handle_failure;
|
||||
}
|
||||
if let DrmError::Atomic(OsError(c::EACCES)) = e {
|
||||
log::debug!("Could not perform atomic commit, likely because we're no longer the DRM master");
|
||||
return Ok(());
|
||||
}
|
||||
if let Some(fb) = &new_fb {
|
||||
if let Some(dsd) = &fb.direct_scanout_data {
|
||||
if self.present(false).is_ok() {
|
||||
let mut cache = self.scanout_buffers.borrow_mut();
|
||||
if let Some(buffer) = cache.remove(&dsd.dma_buf_id) {
|
||||
cache.insert(
|
||||
dsd.dma_buf_id,
|
||||
DirectScanoutCache {
|
||||
tex: buffer.tex,
|
||||
fb: None,
|
||||
},
|
||||
);
|
||||
}
|
||||
return Ok(());
|
||||
}
|
||||
log::error!("Could not set plane framebuffer: {}", ErrorFmt(e));
|
||||
}
|
||||
}
|
||||
Err(())
|
||||
Err(MetalError::Commit(e))
|
||||
} else {
|
||||
if let Some(fb) = new_fb {
|
||||
if let Some(dsd) = &fb.direct_scanout_data {
|
||||
dsd.tex.reservations().acquire();
|
||||
dsd.acquired.set(true);
|
||||
}
|
||||
self.next_framebuffer.set(Some(fb));
|
||||
}
|
||||
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);
|
||||
self.cursor_changed.set(false);
|
||||
|
|
@ -1036,6 +1064,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(),
|
||||
|
|
@ -2166,7 +2195,7 @@ impl MetalBackend {
|
|||
Ok(fb) => fb,
|
||||
Err(e) => return Err(MetalError::ImportFb(e)),
|
||||
};
|
||||
dev_fb.clear();
|
||||
dev_fb.clear().map_err(MetalError::Clear)?;
|
||||
let (dev_tex, render_tex, render_fb) = if dev.id == render_ctx.dev_id {
|
||||
let render_tex = match dev_img.to_texture() {
|
||||
Ok(fb) => fb,
|
||||
|
|
@ -2215,7 +2244,7 @@ impl MetalBackend {
|
|||
Ok(fb) => fb,
|
||||
Err(e) => return Err(MetalError::ImportFb(e)),
|
||||
};
|
||||
render_fb.clear();
|
||||
render_fb.clear().map_err(MetalError::Clear)?;
|
||||
let render_tex = match render_img.to_texture() {
|
||||
Ok(fb) => fb,
|
||||
Err(e) => return Err(MetalError::ImportTexture(e)),
|
||||
|
|
@ -2435,6 +2464,16 @@ impl RenderBuffer {
|
|||
.clone()
|
||||
.unwrap_or_else(|| self.dev_fb.clone())
|
||||
}
|
||||
|
||||
fn copy_to_dev(&self, sync_file: Option<SyncFile>) -> Result<Option<SyncFile>, MetalError> {
|
||||
let Some(tex) = &self.dev_tex else {
|
||||
return Ok(sync_file);
|
||||
};
|
||||
let acquire_point = AcquireSync::from_sync_file(sync_file);
|
||||
self.dev_fb
|
||||
.copy_texture(tex, acquire_point, ReleaseSync::Implicit, 0, 0)
|
||||
.map_err(MetalError::CopyToOutput)
|
||||
}
|
||||
}
|
||||
|
||||
fn modes_equal(a: &DrmModeInfo, b: &DrmModeInfo) -> bool {
|
||||
|
|
|
|||
|
|
@ -738,13 +738,17 @@ impl XBackend {
|
|||
image.last_serial.set(serial);
|
||||
|
||||
if let Some(node) = self.state.root.outputs.get(&output.id) {
|
||||
self.state.present_output(
|
||||
let res = self.state.present_output(
|
||||
&node,
|
||||
&image.fb.get(),
|
||||
&image.tex.get(),
|
||||
&mut self.render_result.borrow_mut(),
|
||||
true,
|
||||
);
|
||||
if let Err(e) = res {
|
||||
log::error!("Could not render screen: {}", ErrorFmt(e));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
let pp = PresentPixmap {
|
||||
|
|
|
|||
|
|
@ -2,7 +2,11 @@ use {
|
|||
crate::{
|
||||
async_engine::SpawnedFuture,
|
||||
client::{error::LookupError, objects::Objects},
|
||||
ifs::{wl_display::WlDisplay, wl_registry::WlRegistry, wl_surface::WlSurface},
|
||||
ifs::{
|
||||
wl_display::WlDisplay,
|
||||
wl_registry::WlRegistry,
|
||||
wl_surface::{commit_timeline::CommitTimelines, WlSurface},
|
||||
},
|
||||
leaks::Tracker,
|
||||
object::{Interface, Object, ObjectId, WL_DISPLAY_ID},
|
||||
state::State,
|
||||
|
|
@ -149,6 +153,7 @@ impl Clients {
|
|||
last_xwayland_serial: Cell::new(0),
|
||||
surfaces_by_xwayland_serial: Default::default(),
|
||||
activation_tokens: Default::default(),
|
||||
commit_timelines: Rc::new(CommitTimelines::new(&global.wait_for_sync_obj)),
|
||||
});
|
||||
track!(data, data);
|
||||
let display = Rc::new(WlDisplay::new(&data));
|
||||
|
|
@ -220,6 +225,7 @@ impl Drop for ClientHolder {
|
|||
self.data.shutdown.clear();
|
||||
self.data.surfaces_by_xwayland_serial.clear();
|
||||
self.data.remove_activation_tokens();
|
||||
self.data.commit_timelines.clear();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -260,6 +266,7 @@ pub struct Client {
|
|||
pub last_xwayland_serial: Cell<u64>,
|
||||
pub surfaces_by_xwayland_serial: CopyHashMap<u64, Rc<WlSurface>>,
|
||||
pub activation_tokens: RefCell<VecDeque<ActivationToken>>,
|
||||
pub commit_timelines: Rc<CommitTimelines>,
|
||||
}
|
||||
|
||||
pub const NUM_CACHED_SERIAL_RANGES: usize = 64;
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ use {
|
|||
xdg_surface::{xdg_toplevel::XdgToplevel, XdgSurface},
|
||||
WlSurface,
|
||||
},
|
||||
wp_linux_drm_syncobj_timeline_v1::WpLinuxDrmSyncobjTimelineV1,
|
||||
xdg_positioner::XdgPositioner,
|
||||
xdg_wm_base::XdgWmBase,
|
||||
},
|
||||
|
|
@ -29,8 +30,9 @@ use {
|
|||
},
|
||||
wire::{
|
||||
JayOutputId, JayScreencastId, JayWorkspaceId, WlBufferId, WlDataSourceId, WlOutputId,
|
||||
WlPointerId, WlRegionId, WlRegistryId, WlSeatId, WlSurfaceId, XdgPositionerId,
|
||||
XdgSurfaceId, XdgToplevelId, XdgWmBaseId, ZwpPrimarySelectionSourceV1Id,
|
||||
WlPointerId, WlRegionId, WlRegistryId, WlSeatId, WlSurfaceId,
|
||||
WpLinuxDrmSyncobjTimelineV1Id, XdgPositionerId, XdgSurfaceId, XdgToplevelId,
|
||||
XdgWmBaseId, ZwpPrimarySelectionSourceV1Id,
|
||||
},
|
||||
},
|
||||
std::{cell::RefCell, mem, rc::Rc},
|
||||
|
|
@ -56,6 +58,7 @@ pub struct Objects {
|
|||
pub xdg_wm_bases: CopyHashMap<XdgWmBaseId, Rc<XdgWmBase>>,
|
||||
pub seats: CopyHashMap<WlSeatId, Rc<WlSeat>>,
|
||||
pub screencasts: CopyHashMap<JayScreencastId, Rc<JayScreencast>>,
|
||||
pub timelines: CopyHashMap<WpLinuxDrmSyncobjTimelineV1Id, Rc<WpLinuxDrmSyncobjTimelineV1>>,
|
||||
ids: RefCell<Vec<usize>>,
|
||||
}
|
||||
|
||||
|
|
@ -83,6 +86,7 @@ impl Objects {
|
|||
xdg_wm_bases: Default::default(),
|
||||
seats: Default::default(),
|
||||
screencasts: Default::default(),
|
||||
timelines: Default::default(),
|
||||
ids: RefCell::new(vec![]),
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@ use {
|
|||
oserror::OsError, queue::AsyncQueue, refcounted::RefCounted, run_toplevel::RunToplevel,
|
||||
tri::Try,
|
||||
},
|
||||
video::drm::wait_for_sync_obj::WaitForSyncObj,
|
||||
wheel::{Wheel, WheelError},
|
||||
xkbcommon::XkbContext,
|
||||
},
|
||||
|
|
@ -221,6 +222,9 @@ fn start_compositor2(
|
|||
double_click_interval_usec: Cell::new(400 * 1000),
|
||||
double_click_distance: Cell::new(5),
|
||||
create_default_seat: Cell::new(true),
|
||||
subsurface_ids: Default::default(),
|
||||
wait_for_sync_obj: Rc::new(WaitForSyncObj::new(&ring, &engine)),
|
||||
explicit_sync_enabled: Cell::new(true),
|
||||
});
|
||||
state.tracker.register(ClientId::from_raw(0));
|
||||
create_dummy_output(&state);
|
||||
|
|
|
|||
|
|
@ -800,6 +800,10 @@ impl ConfigProxyHandler {
|
|||
self.state.idle.set_timeout(timeout);
|
||||
}
|
||||
|
||||
fn handle_set_explicit_sync_enabled(&self, enabled: bool) {
|
||||
self.state.explicit_sync_enabled.set(enabled);
|
||||
}
|
||||
|
||||
fn handle_connector_connected(&self, connector: Connector) -> Result<(), CphError> {
|
||||
let connector = self.get_connector(connector)?;
|
||||
self.respond(Response::ConnectorConnected {
|
||||
|
|
@ -1725,6 +1729,9 @@ impl ConfigProxyHandler {
|
|||
} => self
|
||||
.handle_move_to_output(workspace, connector)
|
||||
.wrn("move_to_output")?,
|
||||
ClientMessage::SetExplicitSyncEnabled { enabled } => {
|
||||
self.handle_set_explicit_sync_enabled(enabled)
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
@ -378,6 +378,9 @@ fn render_img(image: &InstantiatedCursorImage, renderer: &mut Renderer, x: Fixed
|
|||
None,
|
||||
scale,
|
||||
None,
|
||||
None,
|
||||
AcquireSync::None,
|
||||
ReleaseSync::None,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -389,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);
|
||||
renderer.base.render_texture(
|
||||
&img.tex,
|
||||
0,
|
||||
0,
|
||||
None,
|
||||
None,
|
||||
renderer.scale(),
|
||||
None,
|
||||
None,
|
||||
AcquireSync::None,
|
||||
ReleaseSync::None,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -419,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);
|
||||
renderer.base.render_texture(
|
||||
&img.tex,
|
||||
0,
|
||||
0,
|
||||
None,
|
||||
None,
|
||||
renderer.scale(),
|
||||
None,
|
||||
None,
|
||||
AcquireSync::None,
|
||||
ReleaseSync::None,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
162
src/gfx_api.rs
162
src/gfx_api.rs
|
|
@ -9,8 +9,8 @@ use {
|
|||
state::State,
|
||||
theme::Color,
|
||||
tree::{Node, OutputNode},
|
||||
utils::{numcell::NumCell, transform_ext::TransformExt},
|
||||
video::{dmabuf::DmaBuf, gbm::GbmDevice, Modifier},
|
||||
utils::{clonecell::UnsafeCellCloneSafe, transform_ext::TransformExt},
|
||||
video::{dmabuf::DmaBuf, drm::sync_obj::SyncObjCtx, gbm::GbmDevice, Modifier},
|
||||
},
|
||||
ahash::AHashMap,
|
||||
indexmap::IndexSet,
|
||||
|
|
@ -21,9 +21,12 @@ use {
|
|||
error::Error,
|
||||
ffi::CString,
|
||||
fmt::{Debug, Formatter},
|
||||
ops::Deref,
|
||||
rc::Rc,
|
||||
sync::atomic::{AtomicU64, Ordering::Relaxed},
|
||||
},
|
||||
thiserror::Error,
|
||||
uapi::OwnedFd,
|
||||
};
|
||||
|
||||
pub enum GfxApiOpt {
|
||||
|
|
@ -141,6 +144,72 @@ pub struct CopyTexture {
|
|||
pub tex: Rc<dyn GfxTexture>,
|
||||
pub source: SampleRect,
|
||||
pub target: FramebufferRect,
|
||||
pub buffer_resv: Option<Rc<dyn BufferResv>>,
|
||||
pub acquire_sync: AcquireSync,
|
||||
pub release_sync: ReleaseSync,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct SyncFile(pub Rc<OwnedFd>);
|
||||
|
||||
impl Deref for SyncFile {
|
||||
type Target = Rc<OwnedFd>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl UnsafeCellCloneSafe for SyncFile {}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum AcquireSync {
|
||||
None,
|
||||
Implicit,
|
||||
SyncFile { sync_file: SyncFile },
|
||||
Unnecessary,
|
||||
}
|
||||
|
||||
impl AcquireSync {
|
||||
pub fn from_sync_file(sync_file: Option<SyncFile>) -> 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",
|
||||
AcquireSync::Unnecessary => "Unnecessary",
|
||||
};
|
||||
f.debug_struct(name).finish_non_exhaustive()
|
||||
}
|
||||
}
|
||||
|
||||
pub trait BufferResv: Debug {
|
||||
fn set_sync_file(&self, user: BufferResvUser, sync_file: &SyncFile);
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
|
||||
pub struct BufferResvUser(u64);
|
||||
|
||||
impl Default for BufferResvUser {
|
||||
fn default() -> Self {
|
||||
static NEXT_ID: AtomicU64 = AtomicU64::new(1);
|
||||
Self(NEXT_ID.fetch_add(1, Relaxed))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||
|
|
@ -158,7 +227,11 @@ pub trait GfxFramebuffer: Debug {
|
|||
|
||||
fn physical_size(&self) -> (i32, i32);
|
||||
|
||||
fn render(&self, ops: Vec<GfxApiOpt>, clear: Option<&Color>);
|
||||
fn render(
|
||||
&self,
|
||||
ops: Vec<GfxApiOpt>,
|
||||
clear: Option<&Color>,
|
||||
) -> Result<Option<SyncFile>, GfxError>;
|
||||
|
||||
fn copy_to_shm(
|
||||
self: Rc<Self>,
|
||||
|
|
@ -175,13 +248,13 @@ pub trait GfxFramebuffer: Debug {
|
|||
}
|
||||
|
||||
impl dyn GfxFramebuffer {
|
||||
pub fn clear(&self) {
|
||||
self.clear_with(0.0, 0.0, 0.0, 0.0);
|
||||
pub fn clear(&self) -> Result<Option<SyncFile>, 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) {
|
||||
pub fn clear_with(&self, r: f32, g: f32, b: f32, a: f32) -> Result<Option<SyncFile>, GfxError> {
|
||||
let ops = self.take_render_ops();
|
||||
self.render(ops, Some(&Color { r, g, b, a }));
|
||||
self.render(ops, Some(&Color { r, g, b, a }))
|
||||
}
|
||||
|
||||
pub fn logical_size(&self, transform: Transform) -> (i32, i32) {
|
||||
|
|
@ -206,13 +279,31 @@ impl dyn GfxFramebuffer {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn copy_texture(&self, texture: &Rc<dyn GfxTexture>, x: i32, y: i32) {
|
||||
pub fn copy_texture(
|
||||
&self,
|
||||
texture: &Rc<dyn GfxTexture>,
|
||||
acquire_sync: AcquireSync,
|
||||
release_sync: ReleaseSync,
|
||||
x: i32,
|
||||
y: i32,
|
||||
) -> Result<Option<SyncFile>, 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);
|
||||
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);
|
||||
self.render(ops, clear)
|
||||
}
|
||||
|
||||
pub fn render_custom(
|
||||
|
|
@ -220,11 +311,11 @@ impl dyn GfxFramebuffer {
|
|||
scale: Scale,
|
||||
clear: Option<&Color>,
|
||||
f: &mut dyn FnMut(&mut RendererBase),
|
||||
) {
|
||||
) -> Result<Option<SyncFile>, GfxError> {
|
||||
let mut ops = self.take_render_ops();
|
||||
let mut renderer = self.renderer_base(&mut ops, scale, Transform::None);
|
||||
f(&mut renderer);
|
||||
self.render(ops, clear);
|
||||
self.render(ops, clear)
|
||||
}
|
||||
|
||||
pub fn create_render_pass(
|
||||
|
|
@ -295,7 +386,7 @@ impl dyn GfxFramebuffer {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn perform_render_pass(&self, pass: GfxRenderPass) {
|
||||
pub fn perform_render_pass(&self, pass: GfxRenderPass) -> Result<Option<SyncFile>, GfxError> {
|
||||
self.render(pass.ops, pass.clear.as_ref())
|
||||
}
|
||||
|
||||
|
|
@ -307,7 +398,7 @@ impl dyn GfxFramebuffer {
|
|||
result: Option<&mut RenderResult>,
|
||||
scale: Scale,
|
||||
render_hardware_cursor: bool,
|
||||
) {
|
||||
) -> Result<Option<SyncFile>, GfxError> {
|
||||
self.render_node(
|
||||
node,
|
||||
state,
|
||||
|
|
@ -330,7 +421,7 @@ impl dyn GfxFramebuffer {
|
|||
render_hardware_cursor: bool,
|
||||
black_background: bool,
|
||||
transform: Transform,
|
||||
) {
|
||||
) -> Result<Option<SyncFile>, GfxError> {
|
||||
let pass = self.create_render_pass(
|
||||
node,
|
||||
state,
|
||||
|
|
@ -341,7 +432,7 @@ impl dyn GfxFramebuffer {
|
|||
black_background,
|
||||
transform,
|
||||
);
|
||||
self.perform_render_pass(pass);
|
||||
self.perform_render_pass(pass)
|
||||
}
|
||||
|
||||
pub fn render_hardware_cursor(
|
||||
|
|
@ -350,7 +441,7 @@ impl dyn GfxFramebuffer {
|
|||
state: &State,
|
||||
scale: Scale,
|
||||
transform: Transform,
|
||||
) {
|
||||
) -> Result<Option<SyncFile>, GfxError> {
|
||||
let mut ops = self.take_render_ops();
|
||||
let mut renderer = Renderer {
|
||||
base: self.renderer_base(&mut ops, scale, transform),
|
||||
|
|
@ -363,7 +454,7 @@ impl dyn GfxFramebuffer {
|
|||
},
|
||||
};
|
||||
cursor.render_hardware_cursor(&mut renderer);
|
||||
self.render(ops, Some(&Color::TRANSPARENT));
|
||||
self.render(ops, Some(&Color::TRANSPARENT))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -376,38 +467,6 @@ pub trait GfxImage {
|
|||
fn height(&self) -> i32;
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct TextureReservations {
|
||||
reservations: NumCell<usize>,
|
||||
on_release: Cell<Option<Box<dyn FnOnce()>>>,
|
||||
}
|
||||
|
||||
impl TextureReservations {
|
||||
pub fn has_reservation(&self) -> bool {
|
||||
self.reservations.get() != 0
|
||||
}
|
||||
|
||||
pub fn acquire(&self) {
|
||||
self.reservations.fetch_add(1);
|
||||
}
|
||||
|
||||
pub fn release(&self) {
|
||||
if self.reservations.fetch_sub(1) == 1 {
|
||||
if let Some(cb) = self.on_release.take() {
|
||||
cb();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn on_released<C: FnOnce() + 'static>(&self, cb: C) {
|
||||
if self.has_reservation() {
|
||||
self.on_release.set(Some(Box::new(cb)));
|
||||
} else {
|
||||
cb();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait GfxTexture: Debug {
|
||||
fn size(&self) -> (i32, i32);
|
||||
fn as_any(&self) -> &dyn Any;
|
||||
|
|
@ -423,7 +482,6 @@ pub trait GfxTexture: Debug {
|
|||
shm: &[Cell<u8>],
|
||||
) -> Result<(), GfxError>;
|
||||
fn dmabuf(&self) -> Option<&DmaBuf>;
|
||||
fn reservations(&self) -> &TextureReservations;
|
||||
fn format(&self) -> &'static Format;
|
||||
}
|
||||
|
||||
|
|
@ -461,6 +519,8 @@ pub trait GfxContext: Debug {
|
|||
stride: i32,
|
||||
format: &'static Format,
|
||||
) -> Result<Rc<dyn GfxFramebuffer>, GfxError>;
|
||||
|
||||
fn sync_obj_ctx(&self) -> &Rc<SyncObjCtx>;
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
|
|
|||
|
|
@ -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<SyncFile> {
|
||||
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 | AcquireSync::Unnecessary => 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()
|
||||
|
|
|
|||
|
|
@ -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<u32, EglFormat>,
|
||||
pub gbm: Rc<GbmDevice>,
|
||||
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))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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" {
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
pub(super) mod context;
|
||||
pub(super) mod framebuffer;
|
||||
pub(super) mod image;
|
||||
pub(super) mod sync;
|
||||
pub(super) mod texture;
|
||||
|
|
|
|||
|
|
@ -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},
|
||||
|
|
@ -14,7 +14,11 @@ use {
|
|||
renderer::{framebuffer::Framebuffer, image::Image},
|
||||
GfxGlState, RenderError, Texture,
|
||||
},
|
||||
video::{dmabuf::DmaBuf, drm::Drm, gbm::GbmDevice},
|
||||
video::{
|
||||
dmabuf::DmaBuf,
|
||||
drm::{sync_obj::SyncObjCtx, Drm},
|
||||
gbm::GbmDevice,
|
||||
},
|
||||
},
|
||||
ahash::AHashMap,
|
||||
jay_config::video::GfxApi,
|
||||
|
|
@ -53,6 +57,7 @@ pub(crate) struct TexProgs {
|
|||
pub(in crate::gfx_apis::gl) struct GlRenderContext {
|
||||
pub(crate) ctx: Rc<EglContext>,
|
||||
pub gbm: Rc<GbmDevice>,
|
||||
pub sync_ctx: Rc<SyncObjCtx>,
|
||||
|
||||
pub(crate) render_node: Rc<CString>,
|
||||
|
||||
|
|
@ -65,6 +70,8 @@ pub(in crate::gfx_apis::gl) struct GlRenderContext {
|
|||
|
||||
pub(crate) gfx_ops: RefCell<Vec<GfxApiOpt>>,
|
||||
pub(in crate::gfx_apis::gl) gl_state: RefCell<GfxGlState>,
|
||||
|
||||
pub(in crate::gfx_apis::gl) buffer_resv_user: BufferResvUser,
|
||||
}
|
||||
|
||||
impl Debug for GlRenderContext {
|
||||
|
|
@ -126,6 +133,7 @@ impl GlRenderContext {
|
|||
Ok(Self {
|
||||
ctx: ctx.clone(),
|
||||
gbm: ctx.dpy.gbm.clone(),
|
||||
sync_ctx: Rc::new(SyncObjCtx::new(ctx.dpy.gbm.drm.fd())),
|
||||
|
||||
render_node: node.clone(),
|
||||
|
||||
|
|
@ -141,6 +149,8 @@ impl GlRenderContext {
|
|||
|
||||
gfx_ops: Default::default(),
|
||||
gl_state: Default::default(),
|
||||
|
||||
buffer_resv_user: Default::default(),
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -186,7 +196,6 @@ impl GlRenderContext {
|
|||
Ok(Rc::new(Texture {
|
||||
ctx: self.clone(),
|
||||
gl,
|
||||
resv: Default::default(),
|
||||
format,
|
||||
}))
|
||||
}
|
||||
|
|
@ -268,4 +277,8 @@ impl GfxContext for GlRenderContext {
|
|||
})?;
|
||||
Ok(Rc::new(Framebuffer { ctx: self, gl: fb }))
|
||||
}
|
||||
|
||||
fn sync_obj_ctx(&self) -> &Rc<SyncObjCtx> {
|
||||
&self.sync_ctx
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
@ -10,6 +10,7 @@ use {
|
|||
renderer::context::GlRenderContext,
|
||||
run_ops,
|
||||
sys::{GL_ONE, GL_ONE_MINUS_SRC_ALPHA},
|
||||
RenderError,
|
||||
},
|
||||
theme::Color,
|
||||
},
|
||||
|
|
@ -64,9 +65,13 @@ impl Framebuffer {
|
|||
});
|
||||
}
|
||||
|
||||
pub fn render(&self, ops: Vec<GfxApiOpt>, clear: Option<&Color>) {
|
||||
pub fn render(
|
||||
&self,
|
||||
mut ops: Vec<GfxApiOpt>,
|
||||
clear: Option<&Color>,
|
||||
) -> Result<Option<SyncFile>, RenderError> {
|
||||
let gles = self.ctx.ctx.dpy.gles;
|
||||
let _ = self.ctx.ctx.with_current(|| {
|
||||
let res = self.ctx.ctx.with_current(|| {
|
||||
unsafe {
|
||||
(gles.glBindFramebuffer)(GL_FRAMEBUFFER, self.gl.fbo);
|
||||
(gles.glViewport)(0, 0, self.gl.width, self.gl.height);
|
||||
|
|
@ -76,13 +81,17 @@ 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)
|
||||
});
|
||||
ops.clear();
|
||||
*self.ctx.gfx_ops.borrow_mut() = ops;
|
||||
res
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -92,17 +101,19 @@ impl GfxFramebuffer for Framebuffer {
|
|||
}
|
||||
|
||||
fn take_render_ops(&self) -> Vec<GfxApiOpt> {
|
||||
let mut ops = mem::take(&mut *self.ctx.gfx_ops.borrow_mut());
|
||||
ops.clear();
|
||||
ops
|
||||
mem::take(&mut *self.ctx.gfx_ops.borrow_mut())
|
||||
}
|
||||
|
||||
fn physical_size(&self) -> (i32, i32) {
|
||||
(self.gl.width, self.gl.height)
|
||||
}
|
||||
|
||||
fn render(&self, ops: Vec<GfxApiOpt>, clear: Option<&Color>) {
|
||||
self.render(ops, clear);
|
||||
fn render(
|
||||
&self,
|
||||
ops: Vec<GfxApiOpt>,
|
||||
clear: Option<&Color>,
|
||||
) -> Result<Option<SyncFile>, GfxError> {
|
||||
self.render(ops, clear).map_err(|e| e.into())
|
||||
}
|
||||
|
||||
fn copy_to_shm(
|
||||
|
|
|
|||
|
|
@ -27,7 +27,6 @@ impl Image {
|
|||
Ok(Rc::new(Texture {
|
||||
ctx: self.ctx.clone(),
|
||||
gl: GlTexture::import_img(&self.ctx.ctx, &self.gl)?,
|
||||
resv: Default::default(),
|
||||
format: self.gl.dmabuf.format,
|
||||
}))
|
||||
}
|
||||
|
|
|
|||
107
src/gfx_apis/gl/renderer/sync.rs
Normal file
107
src/gfx_apis/gl/renderer/sync.rs
Normal file
|
|
@ -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<EglContext>,
|
||||
sync: EGLSyncKHR,
|
||||
}
|
||||
|
||||
impl EglContext {
|
||||
pub fn export_sync_file(self: &Rc<Self>) -> Result<OwnedFd, RenderError> {
|
||||
self.create_sync(None)?.export_sync_file()
|
||||
}
|
||||
|
||||
pub fn create_sync(self: &Rc<Self>, file: Option<OwnedFd>) -> Result<EglSync, RenderError> {
|
||||
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<OwnedFd, RenderError> {
|
||||
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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
use {
|
||||
crate::{
|
||||
format::Format,
|
||||
gfx_api::{GfxError, GfxTexture, TextureReservations},
|
||||
gfx_api::{GfxError, GfxTexture},
|
||||
gfx_apis::gl::{
|
||||
gl::texture::GlTexture,
|
||||
renderer::{context::GlRenderContext, framebuffer::Framebuffer},
|
||||
|
|
@ -20,7 +20,6 @@ use {
|
|||
pub struct Texture {
|
||||
pub(in crate::gfx_apis::gl) ctx: Rc<GlRenderContext>,
|
||||
pub(in crate::gfx_apis::gl) gl: GlTexture,
|
||||
pub(in crate::gfx_apis::gl) resv: TextureReservations,
|
||||
pub(in crate::gfx_apis::gl) format: &'static Format,
|
||||
}
|
||||
|
||||
|
|
@ -79,10 +78,6 @@ impl GfxTexture for Texture {
|
|||
self.gl.img.as_ref().map(|i| &i.dmabuf)
|
||||
}
|
||||
|
||||
fn reservations(&self) -> &TextureReservations {
|
||||
&self.resv
|
||||
}
|
||||
|
||||
fn format(&self) -> &'static Format {
|
||||
self.format
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ use {
|
|||
utils::oserror::OsError,
|
||||
video::{
|
||||
dmabuf::DmaBuf,
|
||||
drm::{Drm, DrmError},
|
||||
drm::{sync_obj::SyncObjCtx, Drm, DrmError},
|
||||
gbm::{GbmDevice, GbmError},
|
||||
},
|
||||
},
|
||||
|
|
@ -93,7 +93,7 @@ pub enum VulkanError {
|
|||
LoadImageProperties(#[source] vk::Result),
|
||||
#[error("Device does not support rending and texturing from the XRGB8888 format")]
|
||||
XRGB8888,
|
||||
#[error("Device does not support syncobj import")]
|
||||
#[error("Device does not support sync obj import")]
|
||||
SyncobjImport,
|
||||
#[error("Could not start a command buffer")]
|
||||
BeginCommandBuffer(vk::Result),
|
||||
|
|
@ -153,8 +153,6 @@ pub enum VulkanError {
|
|||
IoctlExportSyncFile(#[source] OsError),
|
||||
#[error("Could not import a sync file into a semaphore")]
|
||||
ImportSyncFile(#[source] vk::Result),
|
||||
#[error("Could not import a sync file into a dma-buf")]
|
||||
IoctlImportSyncFile(#[source] OsError),
|
||||
#[error("Could not export a sync file from a semaphore")]
|
||||
ExportSyncFile(#[source] vk::Result),
|
||||
#[error("Could not fetch the render node of the device")]
|
||||
|
|
@ -175,6 +173,8 @@ pub enum VulkanError {
|
|||
height: i32,
|
||||
stride: i32,
|
||||
},
|
||||
#[error(transparent)]
|
||||
GfxError(GfxError),
|
||||
}
|
||||
|
||||
impl From<VulkanError> for GfxError {
|
||||
|
|
@ -270,6 +270,10 @@ impl GfxContext for Context {
|
|||
.create_shm_texture(format, width, height, stride, &[], true)?;
|
||||
Ok(fb)
|
||||
}
|
||||
|
||||
fn sync_obj_ctx(&self) -> &Rc<SyncObjCtx> {
|
||||
&self.0.device.sync_ctx
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Context {
|
||||
|
|
|
|||
|
|
@ -10,7 +10,10 @@ use {
|
|||
util::OnDrop,
|
||||
VulkanError,
|
||||
},
|
||||
video::{drm::Drm, gbm::GbmDevice},
|
||||
video::{
|
||||
drm::{sync_obj::SyncObjCtx, Drm},
|
||||
gbm::GbmDevice,
|
||||
},
|
||||
},
|
||||
ahash::AHashMap,
|
||||
arrayvec::ArrayVec,
|
||||
|
|
@ -43,6 +46,7 @@ pub struct VulkanDevice {
|
|||
pub(super) physical_device: PhysicalDevice,
|
||||
pub(super) render_node: Rc<CString>,
|
||||
pub(super) gbm: GbmDevice,
|
||||
pub(super) sync_ctx: Rc<SyncObjCtx>,
|
||||
pub(super) instance: Rc<VulkanInstance>,
|
||||
pub(super) device: Device,
|
||||
pub(super) external_memory_fd: ExternalMemoryFd,
|
||||
|
|
@ -279,6 +283,7 @@ impl VulkanInstance {
|
|||
Ok(Rc::new(VulkanDevice {
|
||||
physical_device: phy_dev,
|
||||
render_node,
|
||||
sync_ctx: Rc::new(SyncObjCtx::new(gbm.drm.fd())),
|
||||
gbm,
|
||||
instance: self.clone(),
|
||||
device,
|
||||
|
|
|
|||
|
|
@ -1,5 +1,8 @@
|
|||
use {
|
||||
crate::gfx_apis::vulkan::{device::VulkanDevice, VulkanError},
|
||||
crate::{
|
||||
gfx_api::SyncFile,
|
||||
gfx_apis::vulkan::{device::VulkanDevice, VulkanError},
|
||||
},
|
||||
ash::vk::{
|
||||
ExportFenceCreateInfo, ExternalFenceHandleTypeFlags, Fence, FenceCreateInfo,
|
||||
FenceGetFdInfoKHR,
|
||||
|
|
@ -38,12 +41,12 @@ impl VulkanDevice {
|
|||
}
|
||||
|
||||
impl VulkanFence {
|
||||
pub fn export_syncfile(&self) -> Result<Rc<OwnedFd>, VulkanError> {
|
||||
pub fn export_sync_file(&self) -> Result<SyncFile, VulkanError> {
|
||||
let info = FenceGetFdInfoKHR::builder()
|
||||
.fence(self.fence)
|
||||
.handle_type(ExternalFenceHandleTypeFlags::SYNC_FD);
|
||||
let res = unsafe { self.device.external_fence_fd.get_fence_fd(&info) };
|
||||
res.map_err(VulkanError::ExportSyncFile)
|
||||
.map(|fd| Rc::new(OwnedFd::new(fd)))
|
||||
.map(|fd| SyncFile(Rc::new(OwnedFd::new(fd))))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
use {
|
||||
crate::{
|
||||
format::Format,
|
||||
gfx_api::{GfxApiOpt, GfxError, GfxFramebuffer, GfxImage, GfxTexture, TextureReservations},
|
||||
gfx_api::{GfxApiOpt, GfxError, GfxFramebuffer, GfxImage, GfxTexture, SyncFile},
|
||||
gfx_apis::vulkan::{
|
||||
allocator::VulkanAllocation, device::VulkanDevice, format::VulkanMaxExtents,
|
||||
renderer::VulkanRenderer, util::OnDrop, VulkanError,
|
||||
|
|
@ -53,7 +53,6 @@ pub struct VulkanImage {
|
|||
pub(super) is_undefined: Cell<bool>,
|
||||
pub(super) ty: VulkanImageMemory,
|
||||
pub(super) render_ops: CloneCell<Vec<GfxApiOpt>>,
|
||||
pub(super) resv: TextureReservations,
|
||||
}
|
||||
|
||||
pub enum VulkanImageMemory {
|
||||
|
|
@ -212,7 +211,6 @@ impl VulkanRenderer {
|
|||
is_undefined: Cell::new(true),
|
||||
ty: VulkanImageMemory::Internal(shm),
|
||||
render_ops: Default::default(),
|
||||
resv: Default::default(),
|
||||
}))
|
||||
}
|
||||
|
||||
|
|
@ -482,7 +480,6 @@ impl VulkanDmaBufImageTemplate {
|
|||
}),
|
||||
format: self.dmabuf.format,
|
||||
is_undefined: Cell::new(true),
|
||||
resv: Default::default(),
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
|
@ -528,8 +525,14 @@ impl GfxFramebuffer for VulkanImage {
|
|||
(self.width as _, self.height as _)
|
||||
}
|
||||
|
||||
fn render(&self, ops: Vec<GfxApiOpt>, clear: Option<&Color>) {
|
||||
self.renderer.execute(self, &ops, clear).unwrap();
|
||||
fn render(
|
||||
&self,
|
||||
ops: Vec<GfxApiOpt>,
|
||||
clear: Option<&Color>,
|
||||
) -> Result<Option<SyncFile>, GfxError> {
|
||||
self.renderer
|
||||
.execute(self, &ops, clear)
|
||||
.map_err(|e| e.into())
|
||||
}
|
||||
|
||||
fn copy_to_shm(
|
||||
|
|
@ -587,10 +590,6 @@ impl GfxTexture for VulkanImage {
|
|||
}
|
||||
}
|
||||
|
||||
fn reservations(&self) -> &TextureReservations {
|
||||
&self.resv
|
||||
}
|
||||
|
||||
fn format(&self) -> &'static Format {
|
||||
self.format
|
||||
}
|
||||
|
|
|
|||
|
|
@ -146,8 +146,7 @@ impl Drop for VulkanInstance {
|
|||
|
||||
const REQUIRED_INSTANCE_EXTENSIONS: &[&CStr] = &[ExtDebugUtilsFn::name()];
|
||||
|
||||
const VALIDATION_LAYER: &CStr =
|
||||
unsafe { CStr::from_bytes_with_nul_unchecked(b"VK_LAYER_KHRONOS_validation\0") };
|
||||
const VALIDATION_LAYER: &CStr = c"VK_LAYER_KHRONOS_validation";
|
||||
|
||||
pub type Extensions = AHashMap<CString, u32>;
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,10 @@ use {
|
|||
crate::{
|
||||
async_engine::SpawnedFuture,
|
||||
format::Format,
|
||||
gfx_api::{GfxApiOpt, GfxFormat, GfxFramebuffer, GfxTexture},
|
||||
gfx_api::{
|
||||
AcquireSync, BufferResv, BufferResvUser, GfxApiOpt, GfxFormat, GfxFramebuffer,
|
||||
GfxTexture, ReleaseSync, SyncFile,
|
||||
},
|
||||
gfx_apis::vulkan::{
|
||||
allocator::VulkanAllocator,
|
||||
command::{VulkanCommandBuffer, VulkanCommandPool},
|
||||
|
|
@ -21,10 +24,7 @@ use {
|
|||
io_uring::IoUring,
|
||||
theme::Color,
|
||||
utils::{copyhashmap::CopyHashMap, errorfmt::ErrorFmt, numcell::NumCell, stack::Stack},
|
||||
video::dmabuf::{
|
||||
dma_buf_export_sync_file, dma_buf_import_sync_file, DMA_BUF_SYNC_READ,
|
||||
DMA_BUF_SYNC_WRITE,
|
||||
},
|
||||
video::dmabuf::{dma_buf_export_sync_file, DMA_BUF_SYNC_READ, DMA_BUF_SYNC_WRITE},
|
||||
},
|
||||
ahash::AHashMap,
|
||||
ash::{
|
||||
|
|
@ -66,6 +66,14 @@ pub struct VulkanRenderer {
|
|||
pub(super) pending_frames: CopyHashMap<u64, Rc<PendingFrame>>,
|
||||
pub(super) allocator: Rc<VulkanAllocator>,
|
||||
pub(super) last_point: NumCell<u64>,
|
||||
pub(super) buffer_resv_user: BufferResvUser,
|
||||
}
|
||||
|
||||
pub(super) struct UsedTexture {
|
||||
tex: Rc<VulkanImage>,
|
||||
resv: Option<Rc<dyn BufferResv>>,
|
||||
acquire_sync: AcquireSync,
|
||||
release_sync: ReleaseSync,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
|
|
@ -73,20 +81,20 @@ pub(super) struct Memory {
|
|||
sample: Vec<Rc<VulkanImage>>,
|
||||
flush: Vec<Rc<VulkanImage>>,
|
||||
flush_staging: Vec<(Rc<VulkanImage>, VulkanStagingBuffer)>,
|
||||
textures: Vec<Rc<VulkanImage>>,
|
||||
textures: Vec<UsedTexture>,
|
||||
image_barriers: Vec<ImageMemoryBarrier2>,
|
||||
shm_barriers: Vec<BufferMemoryBarrier2>,
|
||||
wait_semaphores: Vec<Rc<VulkanSemaphore>>,
|
||||
wait_semaphore_infos: Vec<SemaphoreSubmitInfo>,
|
||||
release_fence: Option<Rc<VulkanFence>>,
|
||||
release_syncfile: Option<Rc<OwnedFd>>,
|
||||
release_sync_file: Option<SyncFile>,
|
||||
}
|
||||
|
||||
pub(super) struct PendingFrame {
|
||||
point: u64,
|
||||
renderer: Rc<VulkanRenderer>,
|
||||
cmd: Cell<Option<Rc<VulkanCommandBuffer>>>,
|
||||
_textures: Vec<Rc<VulkanImage>>,
|
||||
_textures: Vec<UsedTexture>,
|
||||
_staging: Vec<(Rc<VulkanImage>, VulkanStagingBuffer)>,
|
||||
wait_semaphores: Cell<Vec<Rc<VulkanSemaphore>>>,
|
||||
waiter: Cell<Option<SpawnedFuture<()>>>,
|
||||
|
|
@ -157,6 +165,7 @@ impl VulkanDevice {
|
|||
pending_frames: Default::default(),
|
||||
allocator,
|
||||
last_point: Default::default(),
|
||||
buffer_resv_user: Default::default(),
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
|
@ -177,7 +186,12 @@ impl VulkanRenderer {
|
|||
}
|
||||
}
|
||||
}
|
||||
memory.textures.push(tex);
|
||||
memory.textures.push(UsedTexture {
|
||||
tex,
|
||||
resv: c.buffer_resv.clone(),
|
||||
acquire_sync: c.acquire_sync.clone(),
|
||||
release_sync: c.release_sync,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -530,14 +544,13 @@ impl VulkanRenderer {
|
|||
let import = |infos: &mut Vec<SemaphoreSubmitInfoKHR>,
|
||||
semaphores: &mut Vec<Rc<VulkanSemaphore>>,
|
||||
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_syncfile(fd)?;
|
||||
semaphore.import_sync_file(fd)?;
|
||||
infos.push(
|
||||
SemaphoreSubmitInfo::builder()
|
||||
.semaphore(semaphore.semaphore)
|
||||
|
|
@ -545,6 +558,23 @@ 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)?;
|
||||
}
|
||||
AcquireSync::Unnecessary => {}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
|
|
@ -553,7 +583,8 @@ impl VulkanRenderer {
|
|||
import(
|
||||
&mut memory.wait_semaphore_infos,
|
||||
&mut memory.wait_semaphores,
|
||||
texture,
|
||||
&texture.tex,
|
||||
&texture.acquire_sync,
|
||||
DMA_BUF_SYNC_READ,
|
||||
)?;
|
||||
}
|
||||
|
|
@ -561,33 +592,43 @@ impl VulkanRenderer {
|
|||
&mut memory.wait_semaphore_infos,
|
||||
&mut memory.wait_semaphores,
|
||||
fb,
|
||||
&AcquireSync::Implicit,
|
||||
DMA_BUF_SYNC_WRITE,
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn import_release_semaphore(&self, fb: &VulkanImage) {
|
||||
let memory = self.memory.borrow();
|
||||
let syncfile = match memory.release_syncfile.as_ref() {
|
||||
Some(syncfile) => syncfile,
|
||||
let memory = &mut *self.memory.borrow_mut();
|
||||
let sync_file = match memory.release_sync_file.as_ref() {
|
||||
Some(sync_file) => sync_file,
|
||||
_ => return,
|
||||
};
|
||||
let import = |img: &VulkanImage, flag: u32| {
|
||||
if let VulkanImageMemory::DmaBuf(buf) = &img.ty {
|
||||
for plane in &buf.template.dmabuf.planes {
|
||||
let res = dma_buf_import_sync_file(&plane.fd, flag, &syncfile)
|
||||
.map_err(VulkanError::IoctlImportSyncFile);
|
||||
if let Err(e) = res {
|
||||
log::error!("Could not import syncfile into dmabuf: {}", ErrorFmt(e));
|
||||
log::warn!("Relying on implicit sync");
|
||||
let import =
|
||||
|img: &VulkanImage, sync: ReleaseSync, resv: Option<Rc<dyn BufferResv>>, 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 &memory.textures {
|
||||
import(texture, DMA_BUF_SYNC_WRITE);
|
||||
};
|
||||
for texture in &mut memory.textures {
|
||||
import(
|
||||
&texture.tex,
|
||||
texture.release_sync,
|
||||
texture.resv.take(),
|
||||
DMA_BUF_SYNC_READ,
|
||||
);
|
||||
}
|
||||
import(fb, DMA_BUF_SYNC_READ | DMA_BUF_SYNC_WRITE);
|
||||
import(fb, ReleaseSync::Implicit, None, DMA_BUF_SYNC_WRITE);
|
||||
}
|
||||
|
||||
fn submit(&self, buf: CommandBuffer) -> Result<(), VulkanError> {
|
||||
|
|
@ -610,15 +651,15 @@ impl VulkanRenderer {
|
|||
)
|
||||
.map_err(VulkanError::Submit)?;
|
||||
}
|
||||
let release_syncfile = match release_fence.export_syncfile() {
|
||||
let release_sync_file = match release_fence.export_sync_file() {
|
||||
Ok(s) => Some(s),
|
||||
Err(e) => {
|
||||
log::error!("Could not export syncfile from fence: {}", ErrorFmt(e));
|
||||
log::error!("Could not export sync file from fence: {}", ErrorFmt(e));
|
||||
None
|
||||
}
|
||||
};
|
||||
memory.release_fence = Some(release_fence);
|
||||
memory.release_syncfile = release_syncfile;
|
||||
memory.release_sync_file = release_sync_file;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
@ -650,7 +691,7 @@ impl VulkanRenderer {
|
|||
});
|
||||
self.pending_frames.set(frame.point, frame.clone());
|
||||
let future = self.device.instance.eng.spawn(await_release(
|
||||
memory.release_syncfile.take(),
|
||||
memory.release_sync_file.clone(),
|
||||
self.device.instance.ring.clone(),
|
||||
frame.clone(),
|
||||
self.clone(),
|
||||
|
|
@ -692,7 +733,15 @@ impl VulkanRenderer {
|
|||
&[],
|
||||
true,
|
||||
)?;
|
||||
(&*tmp_tex as &dyn GfxFramebuffer).copy_texture(&(tex.clone() as _), x, y);
|
||||
(&*tmp_tex as &dyn GfxFramebuffer)
|
||||
.copy_texture(
|
||||
&(tex.clone() as _),
|
||||
AcquireSync::None,
|
||||
ReleaseSync::None,
|
||||
x,
|
||||
y,
|
||||
)
|
||||
.map_err(VulkanError::GfxError)?;
|
||||
self.read_all_pixels(&tmp_tex, stride, dst)
|
||||
}
|
||||
|
||||
|
|
@ -769,7 +818,7 @@ impl VulkanRenderer {
|
|||
let fd = dma_buf_export_sync_file(&plane.fd, DMA_BUF_SYNC_READ)
|
||||
.map_err(VulkanError::IoctlExportSyncFile)?;
|
||||
let semaphore = self.allocate_semaphore()?;
|
||||
semaphore.import_syncfile(fd)?;
|
||||
semaphore.import_sync_file(fd)?;
|
||||
let semaphore_info = SemaphoreSubmitInfo::builder()
|
||||
.semaphore(semaphore.semaphore)
|
||||
.stage_mask(PipelineStageFlags2::TOP_OF_PIPE)
|
||||
|
|
@ -831,9 +880,9 @@ impl VulkanRenderer {
|
|||
fb: &VulkanImage,
|
||||
opts: &[GfxApiOpt],
|
||||
clear: Option<&Color>,
|
||||
) -> Result<(), VulkanError> {
|
||||
) -> Result<Option<SyncFile>, 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();
|
||||
|
|
@ -841,9 +890,9 @@ impl VulkanRenderer {
|
|||
memory.sample.clear();
|
||||
memory.wait_semaphores.clear();
|
||||
memory.release_fence.take();
|
||||
memory.release_syncfile.take();
|
||||
}
|
||||
res
|
||||
memory.release_sync_file.take()
|
||||
};
|
||||
res.map(|_| sync_file)
|
||||
}
|
||||
|
||||
fn allocate_command_buffer(&self) -> Result<Rc<VulkanCommandBuffer>, VulkanError> {
|
||||
|
|
@ -964,14 +1013,14 @@ fn image_barrier() -> ImageMemoryBarrier2Builder<'static> {
|
|||
}
|
||||
|
||||
async fn await_release(
|
||||
syncfile: Option<Rc<OwnedFd>>,
|
||||
sync_file: Option<SyncFile>,
|
||||
ring: Rc<IoUring>,
|
||||
frame: Rc<PendingFrame>,
|
||||
renderer: Rc<VulkanRenderer>,
|
||||
) {
|
||||
let mut is_released = false;
|
||||
if let Some(syncfile) = syncfile {
|
||||
if let Err(e) = ring.readable(&syncfile).await {
|
||||
if let Some(sync_file) = sync_file {
|
||||
if let Err(e) = ring.readable(&sync_file).await {
|
||||
log::error!(
|
||||
"Could not wait for release semaphore to be signaled: {}",
|
||||
ErrorFmt(e)
|
||||
|
|
|
|||
|
|
@ -36,9 +36,9 @@ impl VulkanDevice {
|
|||
}
|
||||
|
||||
impl VulkanSemaphore {
|
||||
pub fn import_syncfile(&self, syncfile: OwnedFd) -> Result<(), VulkanError> {
|
||||
pub fn import_sync_file(&self, sync_file: OwnedFd) -> Result<(), VulkanError> {
|
||||
let fd_info = ImportSemaphoreFdInfoKHR::builder()
|
||||
.fd(syncfile.raw())
|
||||
.fd(sync_file.raw())
|
||||
.flags(SemaphoreImportFlags::TEMPORARY)
|
||||
.handle_type(ExternalSemaphoreHandleTypeFlags::SYNC_FD)
|
||||
.semaphore(self.semaphore);
|
||||
|
|
@ -47,8 +47,8 @@ impl VulkanSemaphore {
|
|||
.external_semaphore_fd
|
||||
.import_semaphore_fd(&fd_info)
|
||||
};
|
||||
mem::forget(syncfile);
|
||||
res.map_err(VulkanError::ImportSyncFile)?;
|
||||
mem::forget(sync_file);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -38,6 +38,8 @@ pub mod wp_content_type_v1;
|
|||
pub mod wp_cursor_shape_device_v1;
|
||||
pub mod wp_cursor_shape_manager_v1;
|
||||
pub mod wp_fractional_scale_manager_v1;
|
||||
pub mod wp_linux_drm_syncobj_manager_v1;
|
||||
pub mod wp_linux_drm_syncobj_timeline_v1;
|
||||
pub mod wp_presentation;
|
||||
pub mod wp_presentation_feedback;
|
||||
pub mod wp_single_pixel_buffer_manager_v1;
|
||||
|
|
|
|||
|
|
@ -173,7 +173,7 @@ impl JayScreencast {
|
|||
let mut buffer = self.buffers.borrow_mut();
|
||||
for (idx, buffer) in buffer.deref_mut().iter_mut().enumerate() {
|
||||
if buffer.free {
|
||||
self.client.state.perform_screencopy(
|
||||
let res = self.client.state.perform_screencopy(
|
||||
texture,
|
||||
&buffer.fb,
|
||||
on.global.pos.get(),
|
||||
|
|
@ -183,12 +183,20 @@ impl JayScreencast {
|
|||
size,
|
||||
on.global.persistent.transform.get(),
|
||||
);
|
||||
self.client.event(Ready {
|
||||
self_id: self.id,
|
||||
idx: idx as _,
|
||||
});
|
||||
buffer.free = false;
|
||||
return;
|
||||
match res {
|
||||
Ok(_) => {
|
||||
self.client.event(Ready {
|
||||
self_id: self.id,
|
||||
idx: idx as _,
|
||||
});
|
||||
buffer.free = false;
|
||||
return;
|
||||
}
|
||||
Err(e) => {
|
||||
log::error!("Could not perform screencopy: {}", ErrorFmt(e));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
self.missed_frame.set(true);
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ pub struct WlBuffer {
|
|||
pub client: Rc<Client>,
|
||||
pub rect: Rect,
|
||||
pub format: &'static Format,
|
||||
dmabuf: Option<DmaBuf>,
|
||||
pub dmabuf: Option<DmaBuf>,
|
||||
render_ctx_version: Cell<u32>,
|
||||
pub storage: RefCell<Option<WlBufferStorage>>,
|
||||
pub color: Option<Color>,
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ use {
|
|||
buffd::{MsgParser, MsgParserError},
|
||||
clonecell::CloneCell,
|
||||
copyhashmap::CopyHashMap,
|
||||
errorfmt::ErrorFmt,
|
||||
linkedlist::LinkedList,
|
||||
transform_ext::TransformExt,
|
||||
},
|
||||
|
|
@ -243,7 +244,7 @@ impl WlOutputGlobal {
|
|||
if let Some(WlBufferStorage::Shm { mem, stride }) =
|
||||
wl_buffer.storage.borrow_mut().deref()
|
||||
{
|
||||
self.state.perform_shm_screencopy(
|
||||
let res = self.state.perform_shm_screencopy(
|
||||
tex,
|
||||
self.pos.get(),
|
||||
x_off,
|
||||
|
|
@ -255,6 +256,11 @@ impl WlOutputGlobal {
|
|||
wl_buffer.format,
|
||||
Transform::None,
|
||||
);
|
||||
if let Err(e) = res {
|
||||
log::warn!("Could not perform shm screencopy: {}", ErrorFmt(e));
|
||||
capture.send_failed();
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
let fb = match wl_buffer.famebuffer.get() {
|
||||
Some(fb) => fb,
|
||||
|
|
@ -264,7 +270,7 @@ impl WlOutputGlobal {
|
|||
continue;
|
||||
}
|
||||
};
|
||||
self.state.perform_screencopy(
|
||||
let res = self.state.perform_screencopy(
|
||||
tex,
|
||||
&fb,
|
||||
self.pos.get(),
|
||||
|
|
@ -274,6 +280,11 @@ impl WlOutputGlobal {
|
|||
size,
|
||||
Transform::None,
|
||||
);
|
||||
if let Err(e) = res {
|
||||
log::warn!("Could not perform screencopy: {}", ErrorFmt(e));
|
||||
capture.send_failed();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if capture.with_damage.get() {
|
||||
capture.send_damage();
|
||||
|
|
|
|||
|
|
@ -299,13 +299,21 @@ impl WlSeatGlobal {
|
|||
if extents.intersects(&Rect::new_sized(-x_rel, -y_rel, width, height).unwrap()) {
|
||||
if render {
|
||||
let buffer = hc.get_buffer();
|
||||
buffer.render_hardware_cursor(
|
||||
let res = buffer.render_hardware_cursor(
|
||||
cursor.deref(),
|
||||
&self.state,
|
||||
scale,
|
||||
transform,
|
||||
);
|
||||
hc.swap_buffer();
|
||||
match res {
|
||||
Ok(sync_file) => {
|
||||
hc.set_sync_file(sync_file);
|
||||
hc.swap_buffer();
|
||||
}
|
||||
Err(e) => {
|
||||
log::error!("Could not render hardware cursor: {}", ErrorFmt(e));
|
||||
}
|
||||
}
|
||||
}
|
||||
hc.set_enabled(true);
|
||||
let mode = output.global.mode.get();
|
||||
|
|
|
|||
|
|
@ -1,7 +1,9 @@
|
|||
pub mod commit_timeline;
|
||||
pub mod cursor;
|
||||
pub mod ext_session_lock_surface_v1;
|
||||
pub mod wl_subsurface;
|
||||
pub mod wp_fractional_scale_v1;
|
||||
pub mod wp_linux_drm_syncobj_surface_v1;
|
||||
pub mod wp_tearing_control_v1;
|
||||
pub mod wp_viewport;
|
||||
pub mod x_surface;
|
||||
|
|
@ -16,7 +18,7 @@ use {
|
|||
client::{Client, ClientError, RequestParser},
|
||||
drm_feedback::DrmFeedback,
|
||||
fixed::Fixed,
|
||||
gfx_api::SampleRect,
|
||||
gfx_api::{AcquireSync, BufferResv, BufferResvUser, ReleaseSync, SampleRect, SyncFile},
|
||||
ifs::{
|
||||
wl_buffer::WlBuffer,
|
||||
wl_callback::WlCallback,
|
||||
|
|
@ -25,11 +27,16 @@ use {
|
|||
NodeSeatState, SeatId, WlSeatGlobal,
|
||||
},
|
||||
wl_surface::{
|
||||
cursor::CursorSurface, wl_subsurface::WlSubsurface,
|
||||
commit_timeline::{ClearReason, CommitTimeline, CommitTimelineError},
|
||||
cursor::CursorSurface,
|
||||
wl_subsurface::{PendingSubsurfaceData, SubsurfaceId, WlSubsurface},
|
||||
wp_fractional_scale_v1::WpFractionalScaleV1,
|
||||
wp_tearing_control_v1::WpTearingControlV1, wp_viewport::WpViewport,
|
||||
x_surface::XSurface, xdg_surface::XdgSurfaceError,
|
||||
zwlr_layer_surface_v1::ZwlrLayerSurfaceV1Error,
|
||||
wp_linux_drm_syncobj_surface_v1::WpLinuxDrmSyncobjSurfaceV1,
|
||||
wp_tearing_control_v1::WpTearingControlV1,
|
||||
wp_viewport::WpViewport,
|
||||
x_surface::XSurface,
|
||||
xdg_surface::{PendingXdgSurfaceData, XdgSurfaceError},
|
||||
zwlr_layer_surface_v1::{PendingLayerSurfaceData, ZwlrLayerSurfaceV1Error},
|
||||
},
|
||||
wp_content_type_v1::ContentType,
|
||||
wp_presentation_feedback::WpPresentationFeedback,
|
||||
|
|
@ -48,11 +55,16 @@ use {
|
|||
cell_ext::CellExt,
|
||||
clonecell::CloneCell,
|
||||
copyhashmap::CopyHashMap,
|
||||
errorfmt::ErrorFmt,
|
||||
linkedlist::LinkedList,
|
||||
numcell::NumCell,
|
||||
smallmap::SmallMap,
|
||||
transform_ext::TransformExt,
|
||||
},
|
||||
video::{
|
||||
dmabuf::DMA_BUF_SYNC_READ,
|
||||
drm::sync_obj::{SyncObj, SyncObjPoint},
|
||||
},
|
||||
wire::{
|
||||
wl_surface::*, WlOutputId, WlSurfaceId, ZwpIdleInhibitorV1Id,
|
||||
ZwpLinuxDmabufFeedbackV1Id,
|
||||
|
|
@ -61,9 +73,11 @@ use {
|
|||
xwayland::XWaylandEvent,
|
||||
},
|
||||
ahash::AHashMap,
|
||||
isnt::std_1::primitive::IsntSliceExt,
|
||||
jay_config::video::Transform,
|
||||
std::{
|
||||
cell::{Cell, RefCell},
|
||||
collections::hash_map::{Entry, OccupiedEntry},
|
||||
fmt::{Debug, Formatter},
|
||||
mem,
|
||||
ops::{Deref, DerefMut},
|
||||
|
|
@ -127,13 +141,78 @@ impl NodeVisitorBase for SurfaceSendPreferredTransformVisitor {
|
|||
}
|
||||
}
|
||||
|
||||
struct SurfaceBufferExplicitRelease {
|
||||
sync_obj: Rc<SyncObj>,
|
||||
point: SyncObjPoint,
|
||||
}
|
||||
|
||||
pub struct SurfaceBuffer {
|
||||
pub buffer: Rc<WlBuffer>,
|
||||
sync_files: SmallMap<BufferResvUser, SyncFile, 1>,
|
||||
pub sync: AcquireSync,
|
||||
pub release_sync: ReleaseSync,
|
||||
release: Option<SurfaceBufferExplicitRelease>,
|
||||
}
|
||||
|
||||
impl Drop for SurfaceBuffer {
|
||||
fn drop(&mut self) {
|
||||
let sync_files = self.sync_files.take();
|
||||
if let Some(release) = &self.release {
|
||||
let Some(ctx) = self.buffer.client.state.render_ctx.get() else {
|
||||
log::error!("Cannot signal release point because there is no render context");
|
||||
return;
|
||||
};
|
||||
let ctx = ctx.sync_obj_ctx();
|
||||
if sync_files.is_not_empty() {
|
||||
let res = ctx.import_sync_files(
|
||||
&release.sync_obj,
|
||||
release.point,
|
||||
sync_files.iter().map(|f| &f.1),
|
||||
);
|
||||
match res {
|
||||
Ok(_) => return,
|
||||
Err(e) => {
|
||||
log::error!("Could not import sync files into sync obj: {}", ErrorFmt(e));
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Err(e) = ctx.signal(&release.sync_obj, release.point) {
|
||||
log::error!("Could not signal release point: {}", ErrorFmt(e));
|
||||
}
|
||||
return;
|
||||
}
|
||||
if let Some(dmabuf) = &self.buffer.dmabuf {
|
||||
for (_, sync_file) in &sync_files {
|
||||
if let Err(e) = dmabuf.import_sync_file(DMA_BUF_SYNC_READ, sync_file) {
|
||||
log::error!("Could not import sync file: {}", ErrorFmt(e));
|
||||
}
|
||||
}
|
||||
}
|
||||
if !self.buffer.destroyed() {
|
||||
self.buffer.send_release();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for SurfaceBuffer {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("SurfaceBuffer").finish_non_exhaustive()
|
||||
}
|
||||
}
|
||||
|
||||
impl BufferResv for SurfaceBuffer {
|
||||
fn set_sync_file(&self, user: BufferResvUser, sync_file: &SyncFile) {
|
||||
self.sync_files.insert(user, sync_file.clone());
|
||||
}
|
||||
}
|
||||
|
||||
pub struct WlSurface {
|
||||
pub id: WlSurfaceId,
|
||||
pub node_id: SurfaceNodeId,
|
||||
pub client: Rc<Client>,
|
||||
visible: Cell<bool>,
|
||||
role: Cell<SurfaceRole>,
|
||||
pending: PendingState,
|
||||
pending: RefCell<Box<PendingState>>,
|
||||
input_region: CloneCell<Option<Rc<Region>>>,
|
||||
opaque_region: Cell<Option<Rc<Region>>>,
|
||||
buffer_points: RefCell<BufferPoints>,
|
||||
|
|
@ -145,7 +224,7 @@ pub struct WlSurface {
|
|||
pub extents: Cell<Rect>,
|
||||
pub buffer_abs_pos: Cell<Rect>,
|
||||
pub need_extents_update: Cell<bool>,
|
||||
pub buffer: CloneCell<Option<Rc<WlBuffer>>>,
|
||||
pub buffer: CloneCell<Option<Rc<SurfaceBuffer>>>,
|
||||
pub buf_x: NumCell<i32>,
|
||||
pub buf_y: NumCell<i32>,
|
||||
pub children: RefCell<Option<Box<ParentData>>>,
|
||||
|
|
@ -169,6 +248,9 @@ pub struct WlSurface {
|
|||
pub has_content_type_manager: Cell<bool>,
|
||||
content_type: Cell<Option<ContentType>>,
|
||||
pub drm_feedback: CopyHashMap<ZwpLinuxDmabufFeedbackV1Id, Rc<ZwpLinuxDmabufFeedbackV1>>,
|
||||
sync_obj_surface: CloneCell<Option<Rc<WpLinuxDrmSyncobjSurfaceV1>>>,
|
||||
destroyed: Cell<bool>,
|
||||
commit_timeline: CommitTimeline,
|
||||
}
|
||||
|
||||
impl Debug for WlSurface {
|
||||
|
|
@ -185,12 +267,6 @@ struct BufferPoints {
|
|||
y2: f32,
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
enum CommitContext {
|
||||
RootCommit,
|
||||
ChildCommit,
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
enum CommitAction {
|
||||
ContinueCommit,
|
||||
|
|
@ -198,13 +274,21 @@ enum CommitAction {
|
|||
}
|
||||
|
||||
trait SurfaceExt {
|
||||
fn pre_commit(self: Rc<Self>, ctx: CommitContext) -> Result<CommitAction, WlSurfaceError> {
|
||||
let _ = ctx;
|
||||
Ok(CommitAction::ContinueCommit)
|
||||
fn commit_requested(self: Rc<Self>, pending: &mut Box<PendingState>) -> CommitAction {
|
||||
let _ = pending;
|
||||
CommitAction::ContinueCommit
|
||||
}
|
||||
|
||||
fn post_commit(self: Rc<Self>) {
|
||||
// nothing
|
||||
fn before_apply_commit(
|
||||
self: Rc<Self>,
|
||||
pending: &mut PendingState,
|
||||
) -> Result<(), WlSurfaceError> {
|
||||
let _ = pending;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn after_apply_commit(self: Rc<Self>, pending: &mut PendingState) {
|
||||
let _ = pending;
|
||||
}
|
||||
|
||||
fn is_some(&self) -> bool {
|
||||
|
|
@ -246,6 +330,17 @@ trait SurfaceExt {
|
|||
fn into_xsurface(self: Rc<Self>) -> Option<Rc<XSurface>> {
|
||||
None
|
||||
}
|
||||
|
||||
fn consume_pending_child(
|
||||
&self,
|
||||
surface: &WlSurface,
|
||||
child: SubsurfaceId,
|
||||
consume: &mut dyn FnMut(
|
||||
OccupiedEntry<SubsurfaceId, CommittedSubsurface>,
|
||||
) -> Result<(), WlSurfaceError>,
|
||||
) -> Result<(), WlSurfaceError> {
|
||||
surface.pending.borrow_mut().consume_child(child, consume)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct NoneSurfaceExt;
|
||||
|
|
@ -258,20 +353,122 @@ impl SurfaceExt for NoneSurfaceExt {
|
|||
|
||||
#[derive(Default)]
|
||||
struct PendingState {
|
||||
buffer: Cell<Option<Option<Rc<WlBuffer>>>>,
|
||||
offset: Cell<(i32, i32)>,
|
||||
opaque_region: Cell<Option<Option<Rc<Region>>>>,
|
||||
input_region: Cell<Option<Option<Rc<Region>>>>,
|
||||
frame_request: RefCell<Vec<Rc<WlCallback>>>,
|
||||
damage: Cell<bool>,
|
||||
presentation_feedback: RefCell<Vec<Rc<WpPresentationFeedback>>>,
|
||||
src_rect: Cell<Option<Option<[Fixed; 4]>>>,
|
||||
dst_size: Cell<Option<Option<(i32, i32)>>>,
|
||||
scale: Cell<Option<i32>>,
|
||||
transform: Cell<Option<Transform>>,
|
||||
xwayland_serial: Cell<Option<u64>>,
|
||||
tearing: Cell<Option<bool>>,
|
||||
content_type: Cell<Option<Option<ContentType>>>,
|
||||
buffer: Option<Option<Rc<WlBuffer>>>,
|
||||
offset: (i32, i32),
|
||||
opaque_region: Option<Option<Rc<Region>>>,
|
||||
input_region: Option<Option<Rc<Region>>>,
|
||||
frame_request: Vec<Rc<WlCallback>>,
|
||||
damage: bool,
|
||||
presentation_feedback: Vec<Rc<WpPresentationFeedback>>,
|
||||
src_rect: Option<Option<[Fixed; 4]>>,
|
||||
dst_size: Option<Option<(i32, i32)>>,
|
||||
scale: Option<i32>,
|
||||
transform: Option<Transform>,
|
||||
xwayland_serial: Option<u64>,
|
||||
tearing: Option<bool>,
|
||||
content_type: Option<Option<ContentType>>,
|
||||
subsurface: Option<Box<PendingSubsurfaceData>>,
|
||||
xdg_surface: Option<Box<PendingXdgSurfaceData>>,
|
||||
layer_surface: Option<Box<PendingLayerSurfaceData>>,
|
||||
subsurfaces: AHashMap<SubsurfaceId, CommittedSubsurface>,
|
||||
acquire_point: Option<(Rc<SyncObj>, SyncObjPoint)>,
|
||||
release_point: Option<(Rc<SyncObj>, SyncObjPoint)>,
|
||||
explicit_sync: bool,
|
||||
}
|
||||
|
||||
struct CommittedSubsurface {
|
||||
subsurface: Rc<WlSubsurface>,
|
||||
state: Box<PendingState>,
|
||||
}
|
||||
|
||||
impl PendingState {
|
||||
fn merge(&mut self, next: &mut Self, client: &Rc<Client>) {
|
||||
// discard state
|
||||
|
||||
if next.buffer.is_some() {
|
||||
if let Some((sync_obj, point)) = self.release_point.take() {
|
||||
client.state.signal_point(&sync_obj, point);
|
||||
} else if let Some(Some(prev)) = self.buffer.take() {
|
||||
if !prev.destroyed() {
|
||||
prev.send_release();
|
||||
}
|
||||
}
|
||||
}
|
||||
for fb in self.presentation_feedback.drain(..) {
|
||||
fb.send_discarded();
|
||||
let _ = client.remove_obj(&*fb);
|
||||
}
|
||||
|
||||
// overwrite state
|
||||
|
||||
macro_rules! opt {
|
||||
($name:ident) => {
|
||||
if let Some(n) = next.$name.take() {
|
||||
self.$name = Some(n);
|
||||
}
|
||||
};
|
||||
}
|
||||
opt!(buffer);
|
||||
opt!(opaque_region);
|
||||
opt!(input_region);
|
||||
opt!(src_rect);
|
||||
opt!(dst_size);
|
||||
opt!(scale);
|
||||
opt!(transform);
|
||||
opt!(xwayland_serial);
|
||||
opt!(tearing);
|
||||
opt!(content_type);
|
||||
opt!(acquire_point);
|
||||
opt!(release_point);
|
||||
{
|
||||
let (dx1, dy1) = self.offset;
|
||||
let (dx2, dy2) = mem::take(&mut next.offset);
|
||||
self.offset = (dx1 + dx2, dy1 + dy2);
|
||||
}
|
||||
self.frame_request.append(&mut next.frame_request);
|
||||
self.damage |= mem::take(&mut next.damage);
|
||||
mem::swap(
|
||||
&mut self.presentation_feedback,
|
||||
&mut next.presentation_feedback,
|
||||
);
|
||||
macro_rules! merge_ext {
|
||||
($name:ident) => {
|
||||
if let Some(e) = &mut self.$name {
|
||||
if let Some(n) = &mut next.$name {
|
||||
e.merge(n);
|
||||
}
|
||||
} else {
|
||||
self.$name = next.$name.take();
|
||||
}
|
||||
};
|
||||
}
|
||||
merge_ext!(subsurface);
|
||||
merge_ext!(xdg_surface);
|
||||
merge_ext!(layer_surface);
|
||||
for (id, mut state) in next.subsurfaces.drain() {
|
||||
match self.subsurfaces.entry(id) {
|
||||
Entry::Occupied(mut o) => {
|
||||
o.get_mut().state.merge(&mut state.state, client);
|
||||
}
|
||||
Entry::Vacant(v) => {
|
||||
v.insert(state);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn consume_child(
|
||||
&mut self,
|
||||
child: SubsurfaceId,
|
||||
consume: impl FnOnce(
|
||||
OccupiedEntry<SubsurfaceId, CommittedSubsurface>,
|
||||
) -> Result<(), WlSurfaceError>,
|
||||
) -> Result<(), WlSurfaceError> {
|
||||
match self.subsurfaces.entry(child) {
|
||||
Entry::Occupied(oe) => consume(oe),
|
||||
_ => Ok(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
|
|
@ -330,6 +527,9 @@ impl WlSurface {
|
|||
has_content_type_manager: Default::default(),
|
||||
content_type: Default::default(),
|
||||
drm_feedback: Default::default(),
|
||||
sync_obj_surface: Default::default(),
|
||||
destroyed: Cell::new(false),
|
||||
commit_timeline: client.commit_timelines.create_timeline(),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -400,8 +600,8 @@ impl WlSurface {
|
|||
|
||||
pub fn add_presentation_feedback(&self, fb: &Rc<WpPresentationFeedback>) {
|
||||
self.pending
|
||||
.presentation_feedback
|
||||
.borrow_mut()
|
||||
.presentation_feedback
|
||||
.push(fb.clone());
|
||||
}
|
||||
|
||||
|
|
@ -558,6 +758,7 @@ impl WlSurface {
|
|||
|
||||
fn destroy(&self, parser: MsgParser<'_, '_>) -> Result<(), WlSurfaceError> {
|
||||
let _req: Destroy = self.parse(parser)?;
|
||||
self.commit_timeline.clear(ClearReason::Destroy);
|
||||
self.unset_dnd_icons();
|
||||
self.unset_cursors();
|
||||
self.ext.get().on_surface_destroy()?;
|
||||
|
|
@ -571,11 +772,7 @@ impl WlSurface {
|
|||
}
|
||||
*children = None;
|
||||
}
|
||||
if let Some(buffer) = self.buffer.set(None) {
|
||||
if !buffer.destroyed() {
|
||||
buffer.send_release();
|
||||
}
|
||||
}
|
||||
self.buffer.set(None);
|
||||
if let Some(xwayland_serial) = self.xwayland_serial.get() {
|
||||
self.client
|
||||
.surfaces_by_xwayland_serial
|
||||
|
|
@ -586,30 +783,32 @@ impl WlSurface {
|
|||
self.client.remove_obj(self)?;
|
||||
self.idle_inhibitors.clear();
|
||||
self.constraints.take();
|
||||
self.destroyed.set(true);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn attach(self: &Rc<Self>, parser: MsgParser<'_, '_>) -> Result<(), WlSurfaceError> {
|
||||
let req: Attach = self.parse(parser)?;
|
||||
let pending = &mut *self.pending.borrow_mut();
|
||||
if self.version >= OFFSET_SINCE {
|
||||
if req.x != 0 || req.y != 0 {
|
||||
return Err(WlSurfaceError::OffsetInAttach);
|
||||
}
|
||||
} else {
|
||||
self.pending.offset.set((req.x, req.y));
|
||||
pending.offset = (req.x, req.y);
|
||||
}
|
||||
let buf = if req.buffer.is_some() {
|
||||
Some(self.client.lookup(req.buffer)?)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
self.pending.buffer.set(Some(buf));
|
||||
pending.buffer = Some(buf);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn damage(&self, parser: MsgParser<'_, '_>) -> Result<(), WlSurfaceError> {
|
||||
let _req: Damage = self.parse(parser)?;
|
||||
self.pending.damage.set(true);
|
||||
self.pending.borrow_mut().damage = true;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
@ -618,7 +817,7 @@ impl WlSurface {
|
|||
let cb = Rc::new(WlCallback::new(req.callback, &self.client));
|
||||
track!(self.client, cb);
|
||||
self.client.add_client_obj(&cb)?;
|
||||
self.pending.frame_request.borrow_mut().push(cb);
|
||||
self.pending.borrow_mut().frame_request.push(cb);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
@ -629,7 +828,7 @@ impl WlSurface {
|
|||
} else {
|
||||
None
|
||||
};
|
||||
self.pending.opaque_region.set(Some(region));
|
||||
self.pending.borrow_mut().opaque_region = Some(region);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
@ -640,39 +839,37 @@ impl WlSurface {
|
|||
} else {
|
||||
None
|
||||
};
|
||||
self.pending.input_region.set(Some(region));
|
||||
self.pending.borrow_mut().input_region = Some(region);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn do_commit(self: &Rc<Self>, ctx: CommitContext) -> Result<(), WlSurfaceError> {
|
||||
let ext = self.ext.get();
|
||||
if ext.clone().pre_commit(ctx)? == CommitAction::AbortCommit {
|
||||
fn apply_state(self: &Rc<Self>, pending: &mut PendingState) -> Result<(), WlSurfaceError> {
|
||||
for (_, mut subsurface) in pending.subsurfaces.drain() {
|
||||
subsurface
|
||||
.subsurface
|
||||
.surface
|
||||
.apply_state(&mut subsurface.state)?;
|
||||
}
|
||||
if self.destroyed.get() {
|
||||
return Ok(());
|
||||
}
|
||||
{
|
||||
let children = self.children.borrow();
|
||||
if let Some(children) = children.deref() {
|
||||
for child in children.subsurfaces.values() {
|
||||
child.surface.do_commit(CommitContext::ChildCommit)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
self.ext.get().before_apply_commit(pending)?;
|
||||
let mut scale_changed = false;
|
||||
if let Some(scale) = self.pending.scale.take() {
|
||||
if let Some(scale) = pending.scale.take() {
|
||||
scale_changed = true;
|
||||
self.buffer_scale.set(scale);
|
||||
}
|
||||
let mut buffer_transform_changed = false;
|
||||
if let Some(transform) = self.pending.transform.take() {
|
||||
if let Some(transform) = pending.transform.take() {
|
||||
buffer_transform_changed = true;
|
||||
self.buffer_transform.set(transform);
|
||||
}
|
||||
let mut viewport_changed = false;
|
||||
if let Some(dst_size) = self.pending.dst_size.take() {
|
||||
if let Some(dst_size) = pending.dst_size.take() {
|
||||
viewport_changed = true;
|
||||
self.dst_size.set(dst_size);
|
||||
}
|
||||
if let Some(src_rect) = self.pending.src_rect.take() {
|
||||
if let Some(src_rect) = pending.src_rect.take() {
|
||||
viewport_changed = true;
|
||||
self.src_rect.set(src_rect);
|
||||
}
|
||||
|
|
@ -687,34 +884,30 @@ impl WlSurface {
|
|||
}
|
||||
let mut buffer_changed = false;
|
||||
let mut old_raw_size = None;
|
||||
let (dx, dy) = self.pending.offset.take();
|
||||
if let Some(buffer_change) = self.pending.buffer.take() {
|
||||
let (dx, dy) = mem::take(&mut pending.offset);
|
||||
if let Some(buffer_change) = pending.buffer.take() {
|
||||
buffer_changed = true;
|
||||
if let Some(buffer) = self.buffer.take() {
|
||||
old_raw_size = Some(buffer.rect);
|
||||
if !buffer.destroyed() {
|
||||
'handle_release: {
|
||||
if let Some(tex) = buffer.texture.get() {
|
||||
let resv = tex.reservations();
|
||||
if resv.has_reservation() {
|
||||
let buffer = Rc::downgrade(&buffer);
|
||||
resv.on_released(move || {
|
||||
if let Some(buffer) = buffer.upgrade() {
|
||||
if !buffer.destroyed() {
|
||||
buffer.send_release();
|
||||
}
|
||||
}
|
||||
});
|
||||
break 'handle_release;
|
||||
}
|
||||
}
|
||||
buffer.send_release();
|
||||
}
|
||||
}
|
||||
old_raw_size = Some(buffer.buffer.rect);
|
||||
}
|
||||
if let Some(buffer) = buffer_change {
|
||||
buffer.update_texture_or_log();
|
||||
self.buffer.set(Some(buffer));
|
||||
let (sync, release_sync) = match pending.explicit_sync {
|
||||
false => (AcquireSync::Implicit, ReleaseSync::Implicit),
|
||||
true => (AcquireSync::Unnecessary, ReleaseSync::Explicit),
|
||||
};
|
||||
let release = pending
|
||||
.release_point
|
||||
.take()
|
||||
.map(|(sync_obj, point)| SurfaceBufferExplicitRelease { sync_obj, point });
|
||||
let surface_buffer = SurfaceBuffer {
|
||||
buffer,
|
||||
sync_files: Default::default(),
|
||||
sync,
|
||||
release_sync,
|
||||
release,
|
||||
};
|
||||
self.buffer.set(Some(Rc::new(surface_buffer)));
|
||||
self.buf_x.fetch_add(dx);
|
||||
self.buf_y.fetch_add(dy);
|
||||
if (dx, dy) != (0, 0) {
|
||||
|
|
@ -764,8 +957,10 @@ impl WlSurface {
|
|||
}
|
||||
if let Some(buffer) = self.buffer.get() {
|
||||
if new_size.is_none() {
|
||||
let (mut width, mut height) =
|
||||
self.buffer_transform.get().maybe_swap(buffer.rect.size());
|
||||
let (mut width, mut height) = self
|
||||
.buffer_transform
|
||||
.get()
|
||||
.maybe_swap(buffer.buffer.rect.size());
|
||||
let scale = self.buffer_scale.get();
|
||||
if scale != 1 {
|
||||
width = (width + scale - 1) / scale;
|
||||
|
|
@ -773,12 +968,14 @@ impl WlSurface {
|
|||
}
|
||||
new_size = Some((width, height));
|
||||
}
|
||||
if transform_changed || Some(buffer.rect) != old_raw_size {
|
||||
if transform_changed || Some(buffer.buffer.rect) != old_raw_size {
|
||||
let (x1, y1, x2, y2) = if self.src_rect.is_none() {
|
||||
(0.0, 0.0, 1.0, 1.0)
|
||||
} else {
|
||||
let (width, height) =
|
||||
self.buffer_transform.get().maybe_swap(buffer.rect.size());
|
||||
let (width, height) = self
|
||||
.buffer_transform
|
||||
.get()
|
||||
.maybe_swap(buffer.buffer.rect.size());
|
||||
let width = width as f32;
|
||||
let height = height as f32;
|
||||
let x1 = buffer_points.x1 / width;
|
||||
|
|
@ -806,34 +1003,32 @@ impl WlSurface {
|
|||
self.buffer_abs_pos
|
||||
.set(self.buffer_abs_pos.get().with_size(width, height).unwrap());
|
||||
}
|
||||
{
|
||||
let mut pfr = self.pending.frame_request.borrow_mut();
|
||||
self.frame_requests.borrow_mut().extend(pfr.drain(..));
|
||||
}
|
||||
self.frame_requests
|
||||
.borrow_mut()
|
||||
.extend(pending.frame_request.drain(..));
|
||||
{
|
||||
let mut fbs = self.presentation_feedback.borrow_mut();
|
||||
for fb in fbs.drain(..) {
|
||||
fb.send_discarded();
|
||||
let _ = self.client.remove_obj(&*fb);
|
||||
}
|
||||
let mut pfbs = self.pending.presentation_feedback.borrow_mut();
|
||||
mem::swap(fbs.deref_mut(), pfbs.deref_mut());
|
||||
mem::swap(fbs.deref_mut(), &mut pending.presentation_feedback);
|
||||
}
|
||||
{
|
||||
if let Some(region) = self.pending.input_region.take() {
|
||||
if let Some(region) = pending.input_region.take() {
|
||||
self.input_region.set(region);
|
||||
}
|
||||
if let Some(region) = self.pending.opaque_region.take() {
|
||||
if let Some(region) = pending.opaque_region.take() {
|
||||
self.opaque_region.set(region);
|
||||
}
|
||||
}
|
||||
if let Some(tearing) = self.pending.tearing.take() {
|
||||
if let Some(tearing) = pending.tearing.take() {
|
||||
self.tearing.set(tearing);
|
||||
}
|
||||
if let Some(content_type) = self.pending.content_type.take() {
|
||||
if let Some(content_type) = pending.content_type.take() {
|
||||
self.content_type.set(content_type);
|
||||
}
|
||||
if let Some(xwayland_serial) = self.pending.xwayland_serial.take() {
|
||||
if let Some(xwayland_serial) = pending.xwayland_serial.take() {
|
||||
self.xwayland_serial.set(Some(xwayland_serial));
|
||||
self.client
|
||||
.surfaces_by_xwayland_serial
|
||||
|
|
@ -853,23 +1048,49 @@ impl WlSurface {
|
|||
cursor.update_hardware_cursor();
|
||||
}
|
||||
}
|
||||
ext.post_commit();
|
||||
self.ext.get().after_apply_commit(pending);
|
||||
self.client.state.damage();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn commit(self: &Rc<Self>, parser: MsgParser<'_, '_>) -> Result<(), WlSurfaceError> {
|
||||
let _req: Commit = self.parse(parser)?;
|
||||
self.do_commit(CommitContext::RootCommit)?;
|
||||
let ext = self.ext.get();
|
||||
let pending = &mut *self.pending.borrow_mut();
|
||||
self.verify_explicit_sync(pending)?;
|
||||
if ext.commit_requested(pending) == CommitAction::ContinueCommit {
|
||||
self.commit_timeline.commit(self, pending)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn verify_explicit_sync(&self, pending: &mut PendingState) -> Result<(), WlSurfaceError> {
|
||||
pending.explicit_sync = self.sync_obj_surface.is_some();
|
||||
if !pending.explicit_sync {
|
||||
return Ok(());
|
||||
}
|
||||
let have_new_buffer = match &pending.buffer {
|
||||
None => false,
|
||||
Some(b) => b.is_some(),
|
||||
};
|
||||
match (
|
||||
pending.release_point.is_some(),
|
||||
pending.acquire_point.is_some(),
|
||||
have_new_buffer,
|
||||
) {
|
||||
(true, true, true) => Ok(()),
|
||||
(false, false, false) => Ok(()),
|
||||
(_, _, true) => Err(WlSurfaceError::MissingSyncPoints),
|
||||
(_, _, false) => Err(WlSurfaceError::UnexpectedSyncPoints),
|
||||
}
|
||||
}
|
||||
|
||||
fn set_buffer_transform(&self, parser: MsgParser<'_, '_>) -> Result<(), WlSurfaceError> {
|
||||
let req: SetBufferTransform = self.parse(parser)?;
|
||||
let Some(tf) = Transform::from_wl(req.transform) else {
|
||||
return Err(WlSurfaceError::UnknownBufferTransform(req.transform));
|
||||
};
|
||||
self.pending.transform.set(Some(tf));
|
||||
self.pending.borrow_mut().transform = Some(tf);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
@ -878,19 +1099,19 @@ impl WlSurface {
|
|||
if req.scale < 1 {
|
||||
return Err(WlSurfaceError::NonPositiveBufferScale);
|
||||
}
|
||||
self.pending.scale.set(Some(req.scale));
|
||||
self.pending.borrow_mut().scale = Some(req.scale);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn damage_buffer(&self, parser: MsgParser<'_, '_>) -> Result<(), WlSurfaceError> {
|
||||
let _req: DamageBuffer = self.parse(parser)?;
|
||||
self.pending.damage.set(true);
|
||||
self.pending.borrow_mut().damage = true;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn offset(&self, parser: MsgParser<'_, '_>) -> Result<(), WlSurfaceError> {
|
||||
let req: Offset = self.parse(parser)?;
|
||||
self.pending.offset.set((req.x, req.y));
|
||||
self.pending.borrow_mut().offset = (req.x, req.y);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
@ -1021,7 +1242,7 @@ impl WlSurface {
|
|||
}
|
||||
|
||||
pub fn set_content_type(&self, content_type: Option<ContentType>) {
|
||||
self.pending.content_type.set(Some(content_type));
|
||||
self.pending.borrow_mut().content_type = Some(content_type);
|
||||
}
|
||||
|
||||
pub fn request_activation(&self) {
|
||||
|
|
@ -1035,6 +1256,18 @@ impl WlSurface {
|
|||
consumer.send_feedback(fb);
|
||||
}
|
||||
}
|
||||
|
||||
fn consume_pending_child(
|
||||
&self,
|
||||
child: SubsurfaceId,
|
||||
mut consume: impl FnMut(
|
||||
OccupiedEntry<SubsurfaceId, CommittedSubsurface>,
|
||||
) -> Result<(), WlSurfaceError>,
|
||||
) -> Result<(), WlSurfaceError> {
|
||||
self.ext
|
||||
.get()
|
||||
.consume_pending_child(self, child, &mut consume)
|
||||
}
|
||||
}
|
||||
|
||||
object_base! {
|
||||
|
|
@ -1064,13 +1297,14 @@ impl Object for WlSurface {
|
|||
self.buffer.set(None);
|
||||
self.toplevel.set(None);
|
||||
self.idle_inhibitors.clear();
|
||||
self.pending.presentation_feedback.borrow_mut().clear();
|
||||
mem::take(self.pending.borrow_mut().deref_mut());
|
||||
self.presentation_feedback.borrow_mut().clear();
|
||||
self.viewporter.take();
|
||||
self.fractional_scale.take();
|
||||
self.tearing_control.take();
|
||||
self.constraints.clear();
|
||||
self.drm_feedback.clear();
|
||||
self.commit_timeline.clear(ClearReason::BreakLoops);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1237,8 +1471,15 @@ pub enum WlSurfaceError {
|
|||
ViewportOutsideBuffer,
|
||||
#[error("attach request must not contain offset")]
|
||||
OffsetInAttach,
|
||||
#[error(transparent)]
|
||||
CommitTimelineError(Box<CommitTimelineError>),
|
||||
#[error("Explicit sync buffer is attached but acquire or release points are not set")]
|
||||
MissingSyncPoints,
|
||||
#[error("No buffer is attached but acquire or release point is set")]
|
||||
UnexpectedSyncPoints,
|
||||
}
|
||||
efrom!(WlSurfaceError, ClientError);
|
||||
efrom!(WlSurfaceError, XdgSurfaceError);
|
||||
efrom!(WlSurfaceError, ZwlrLayerSurfaceV1Error);
|
||||
efrom!(WlSurfaceError, MsgParserError);
|
||||
efrom!(WlSurfaceError, CommitTimelineError);
|
||||
|
|
|
|||
295
src/ifs/wl_surface/commit_timeline.rs
Normal file
295
src/ifs/wl_surface/commit_timeline.rs
Normal file
|
|
@ -0,0 +1,295 @@
|
|||
use {
|
||||
crate::{
|
||||
ifs::wl_surface::{PendingState, WlSurface, WlSurfaceError},
|
||||
utils::{
|
||||
clonecell::CloneCell,
|
||||
copyhashmap::CopyHashMap,
|
||||
linkedlist::{LinkedList, LinkedNode, NodeRef},
|
||||
numcell::NumCell,
|
||||
},
|
||||
video::drm::{
|
||||
sync_obj::{SyncObj, SyncObjPoint},
|
||||
wait_for_sync_obj::{SyncObjWaiter, WaitForSyncObj, WaitForSyncObjHandle},
|
||||
DrmError,
|
||||
},
|
||||
},
|
||||
isnt::std_1::primitive::IsntSliceExt,
|
||||
smallvec::SmallVec,
|
||||
std::{
|
||||
cell::{Cell, RefCell},
|
||||
mem,
|
||||
ops::{Deref, DerefMut},
|
||||
rc::Rc,
|
||||
},
|
||||
thiserror::Error,
|
||||
};
|
||||
|
||||
const MAX_TIMELINE_DEPTH: usize = 256;
|
||||
|
||||
linear_ids!(CommitTimelineIds, CommitTimelineId, u64);
|
||||
|
||||
pub struct CommitTimelines {
|
||||
next_id: CommitTimelineIds,
|
||||
wfs: Rc<WaitForSyncObj>,
|
||||
depth: NumCell<usize>,
|
||||
gc: CopyHashMap<CommitTimelineId, LinkedList<Entry>>,
|
||||
}
|
||||
|
||||
pub struct CommitTimeline {
|
||||
shared: Rc<CommitTimelines>,
|
||||
own_timeline: Rc<Inner>,
|
||||
effective_timeline: CloneCell<Rc<Inner>>,
|
||||
effective_timeline_id: Cell<CommitTimelineId>,
|
||||
}
|
||||
|
||||
struct Inner {
|
||||
id: CommitTimelineId,
|
||||
entries: LinkedList<Entry>,
|
||||
}
|
||||
|
||||
fn add_entry(
|
||||
list: &LinkedList<Entry>,
|
||||
shared: &Rc<CommitTimelines>,
|
||||
kind: EntryKind,
|
||||
) -> NodeRef<Entry> {
|
||||
shared.depth.fetch_add(1);
|
||||
let link = list.add_last(Entry {
|
||||
link: Cell::new(None),
|
||||
shared: shared.clone(),
|
||||
kind,
|
||||
});
|
||||
let noderef = link.to_ref();
|
||||
noderef.link.set(Some(link));
|
||||
noderef
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum CommitTimelineError {
|
||||
#[error(transparent)]
|
||||
ImmediateCommit(WlSurfaceError),
|
||||
#[error("Could not apply a delayed commit")]
|
||||
DelayedCommit(#[source] WlSurfaceError),
|
||||
#[error("Could not register a wait")]
|
||||
RegisterWait(#[source] DrmError),
|
||||
#[error("Syncobj wait failed")]
|
||||
Wait(#[source] DrmError),
|
||||
#[error("The client has too many pending commits")]
|
||||
Depth,
|
||||
}
|
||||
|
||||
impl CommitTimelines {
|
||||
pub fn new(wfs: &Rc<WaitForSyncObj>) -> Self {
|
||||
Self {
|
||||
next_id: Default::default(),
|
||||
depth: NumCell::new(0),
|
||||
wfs: wfs.clone(),
|
||||
gc: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_timeline(self: &Rc<Self>) -> CommitTimeline {
|
||||
let id = self.next_id.next();
|
||||
let timeline = Rc::new(Inner {
|
||||
id,
|
||||
entries: Default::default(),
|
||||
});
|
||||
CommitTimeline {
|
||||
shared: self.clone(),
|
||||
own_timeline: timeline.clone(),
|
||||
effective_timeline: CloneCell::new(timeline),
|
||||
effective_timeline_id: Cell::new(id),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn clear(&self) {
|
||||
for (_, list) in self.gc.lock().drain() {
|
||||
break_loops(&list);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub enum ClearReason {
|
||||
BreakLoops,
|
||||
Destroy,
|
||||
}
|
||||
|
||||
fn break_loops(list: &LinkedList<Entry>) {
|
||||
for entry in list.iter() {
|
||||
entry.link.take();
|
||||
}
|
||||
}
|
||||
|
||||
impl CommitTimeline {
|
||||
pub fn clear(&self, reason: ClearReason) {
|
||||
match reason {
|
||||
ClearReason::BreakLoops => break_loops(&self.own_timeline.entries),
|
||||
ClearReason::Destroy => {
|
||||
if self.own_timeline.entries.is_not_empty() {
|
||||
let list = LinkedList::new();
|
||||
list.append_all(&self.own_timeline.entries);
|
||||
add_entry(&list, &self.shared, EntryKind::Gc(self.own_timeline.id));
|
||||
self.shared.gc.set(self.own_timeline.id, list);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn commit(
|
||||
&self,
|
||||
surface: &Rc<WlSurface>,
|
||||
pending: &mut Box<PendingState>,
|
||||
) -> Result<(), CommitTimelineError> {
|
||||
let mut points = SmallVec::new();
|
||||
consume_acquire_points(pending, &mut points);
|
||||
if points.is_empty() && self.own_timeline.entries.is_empty() {
|
||||
return surface
|
||||
.apply_state(pending)
|
||||
.map_err(CommitTimelineError::ImmediateCommit);
|
||||
}
|
||||
if self.shared.depth.get() >= MAX_TIMELINE_DEPTH {
|
||||
return Err(CommitTimelineError::Depth);
|
||||
}
|
||||
set_effective_timeline(self, pending, &self.own_timeline);
|
||||
let noderef = add_entry(
|
||||
&self.own_timeline.entries,
|
||||
&self.shared,
|
||||
EntryKind::Commit(Commit {
|
||||
surface: surface.clone(),
|
||||
pending: RefCell::new(mem::take(pending)),
|
||||
sync_obj: NumCell::new(points.len()),
|
||||
wait_handles: Cell::new(Default::default()),
|
||||
}),
|
||||
);
|
||||
if points.is_not_empty() {
|
||||
let mut wait_handles = SmallVec::new();
|
||||
let noderef = Rc::new(noderef);
|
||||
for (sync_obj, point) in points {
|
||||
let handle = self
|
||||
.shared
|
||||
.wfs
|
||||
.wait(&sync_obj, point, true, noderef.clone())
|
||||
.map_err(CommitTimelineError::RegisterWait)?;
|
||||
wait_handles.push(handle);
|
||||
}
|
||||
let EntryKind::Commit(commit) = &noderef.kind else {
|
||||
unreachable!();
|
||||
};
|
||||
commit.wait_handles.set(wait_handles);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl SyncObjWaiter for NodeRef<Entry> {
|
||||
fn done(self: Rc<Self>, result: Result<(), DrmError>) {
|
||||
let EntryKind::Commit(commit) = &self.kind else {
|
||||
unreachable!();
|
||||
};
|
||||
if let Err(e) = result {
|
||||
commit.surface.client.error(CommitTimelineError::Wait(e));
|
||||
return;
|
||||
}
|
||||
commit.sync_obj.fetch_sub(1);
|
||||
if let Err(e) = flush_from(self.deref().clone()) {
|
||||
commit
|
||||
.surface
|
||||
.client
|
||||
.error(CommitTimelineError::DelayedCommit(e));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct Entry {
|
||||
link: Cell<Option<LinkedNode<Entry>>>,
|
||||
shared: Rc<CommitTimelines>,
|
||||
kind: EntryKind,
|
||||
}
|
||||
|
||||
enum EntryKind {
|
||||
Commit(Commit),
|
||||
Wait(Cell<bool>),
|
||||
Signal(NodeRef<Entry>),
|
||||
Gc(CommitTimelineId),
|
||||
}
|
||||
|
||||
struct Commit {
|
||||
surface: Rc<WlSurface>,
|
||||
pending: RefCell<Box<PendingState>>,
|
||||
sync_obj: NumCell<usize>,
|
||||
wait_handles: Cell<SmallVec<[WaitForSyncObjHandle; 1]>>,
|
||||
}
|
||||
|
||||
fn flush_from(mut point: NodeRef<Entry>) -> Result<(), WlSurfaceError> {
|
||||
let mut gc_list = None;
|
||||
while point.maybe_apply(&mut gc_list)? {
|
||||
point.shared.depth.fetch_sub(1);
|
||||
let _link = point.link.take();
|
||||
match point.next() {
|
||||
None => break,
|
||||
Some(n) => point = n,
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
impl NodeRef<Entry> {
|
||||
fn maybe_apply(&self, gc_list: &mut Option<LinkedList<Entry>>) -> Result<bool, WlSurfaceError> {
|
||||
if self.prev().is_some() {
|
||||
return Ok(false);
|
||||
}
|
||||
match &self.kind {
|
||||
EntryKind::Commit(c) => {
|
||||
if c.sync_obj.get() > 0 {
|
||||
return Ok(false);
|
||||
}
|
||||
c.surface.apply_state(c.pending.borrow_mut().deref_mut())?;
|
||||
Ok(true)
|
||||
}
|
||||
EntryKind::Wait(signaled) => Ok(signaled.get()),
|
||||
EntryKind::Signal(s) => match &s.kind {
|
||||
EntryKind::Wait(signaled) => {
|
||||
signaled.set(true);
|
||||
flush_from(s.clone())?;
|
||||
Ok(true)
|
||||
}
|
||||
_ => unreachable!(),
|
||||
},
|
||||
EntryKind::Gc(id) => {
|
||||
*gc_list = self.shared.gc.remove(id);
|
||||
Ok(true)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type Point = (Rc<SyncObj>, SyncObjPoint);
|
||||
|
||||
fn consume_acquire_points(pending: &mut PendingState, points: &mut SmallVec<[Point; 1]>) {
|
||||
if let Some(point) = pending.acquire_point.take() {
|
||||
points.push(point);
|
||||
}
|
||||
for ss in pending.subsurfaces.values_mut() {
|
||||
consume_acquire_points(&mut ss.state, points);
|
||||
}
|
||||
}
|
||||
|
||||
fn set_effective_timeline(
|
||||
timeline: &CommitTimeline,
|
||||
pending: &PendingState,
|
||||
effective: &Rc<Inner>,
|
||||
) {
|
||||
if timeline.effective_timeline_id.replace(effective.id) != effective.id {
|
||||
let prev = timeline.effective_timeline.set(effective.clone());
|
||||
if prev.entries.is_not_empty() {
|
||||
let noderef = add_entry(
|
||||
&effective.entries,
|
||||
&timeline.shared,
|
||||
EntryKind::Wait(Cell::new(false)),
|
||||
);
|
||||
add_entry(&prev.entries, &timeline.shared, EntryKind::Signal(noderef));
|
||||
}
|
||||
}
|
||||
for ss in pending.subsurfaces.values() {
|
||||
set_effective_timeline(&ss.subsurface.surface.commit_timeline, &ss.state, effective);
|
||||
}
|
||||
}
|
||||
|
|
@ -2,21 +2,25 @@ use {
|
|||
crate::{
|
||||
client::ClientError,
|
||||
ifs::wl_surface::{
|
||||
CommitAction, CommitContext, StackElement, SurfaceExt, SurfaceRole, WlSurface,
|
||||
WlSurfaceError, WlSurfaceId,
|
||||
CommitAction, CommittedSubsurface, PendingState, StackElement, SurfaceExt, SurfaceRole,
|
||||
WlSurface, WlSurfaceError, WlSurfaceId,
|
||||
},
|
||||
leaks::Tracker,
|
||||
object::Object,
|
||||
rect::Rect,
|
||||
utils::{
|
||||
buffd::{MsgParser, MsgParserError},
|
||||
linkedlist::LinkedNode,
|
||||
clonecell::CloneCell,
|
||||
linkedlist::{LinkedNode, NodeRef},
|
||||
numcell::NumCell,
|
||||
option_ext::OptionExt,
|
||||
},
|
||||
wire::{wl_subsurface::*, WlSubsurfaceId},
|
||||
},
|
||||
std::{
|
||||
cell::{Cell, RefCell},
|
||||
cell::{Cell, RefCell, RefMut},
|
||||
collections::hash_map::{Entry, OccupiedEntry},
|
||||
mem,
|
||||
ops::Deref,
|
||||
rc::Rc,
|
||||
},
|
||||
|
|
@ -28,36 +32,39 @@ const BAD_SURFACE: u32 = 0;
|
|||
|
||||
const MAX_SUBSURFACE_DEPTH: u32 = 100;
|
||||
|
||||
linear_ids!(SubsurfaceIds, SubsurfaceId, u64);
|
||||
|
||||
pub struct WlSubsurface {
|
||||
id: WlSubsurfaceId,
|
||||
unique_id: SubsurfaceId,
|
||||
pub surface: Rc<WlSurface>,
|
||||
pub(super) parent: Rc<WlSurface>,
|
||||
pub position: Cell<Rect>,
|
||||
sync_requested: Cell<bool>,
|
||||
sync_ancestor: Cell<bool>,
|
||||
node: RefCell<Option<LinkedNode<StackElement>>>,
|
||||
latest_node: CloneCell<Option<NodeRef<StackElement>>>,
|
||||
depth: NumCell<u32>,
|
||||
pending: PendingSubsurfaceData,
|
||||
pub tracker: Tracker<Self>,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct PendingSubsurfaceData {
|
||||
node: RefCell<Option<LinkedNode<StackElement>>>,
|
||||
position: Cell<Option<(i32, i32)>>,
|
||||
pub struct PendingSubsurfaceData {
|
||||
node: Option<LinkedNode<StackElement>>,
|
||||
position: Option<(i32, i32)>,
|
||||
}
|
||||
|
||||
fn update_children_sync(surface: &WlSubsurface, sync: bool) {
|
||||
let children = surface.surface.children.borrow();
|
||||
if let Some(children) = &*children {
|
||||
for child in children.subsurfaces.values() {
|
||||
let was_sync = child.sync();
|
||||
child.sync_ancestor.set(sync);
|
||||
let is_sync = child.sync();
|
||||
if was_sync != is_sync {
|
||||
update_children_sync(child, sync);
|
||||
}
|
||||
impl PendingSubsurfaceData {
|
||||
pub fn merge(&mut self, next: &mut Self) {
|
||||
macro_rules! opt {
|
||||
($name:ident) => {
|
||||
if let Some(n) = next.$name.take() {
|
||||
self.$name = Some(n);
|
||||
}
|
||||
};
|
||||
}
|
||||
opt!(node);
|
||||
opt!(position);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -85,18 +92,25 @@ impl WlSubsurface {
|
|||
pub fn new(id: WlSubsurfaceId, surface: &Rc<WlSurface>, parent: &Rc<WlSurface>) -> Self {
|
||||
Self {
|
||||
id,
|
||||
unique_id: surface.client.state.subsurface_ids.next(),
|
||||
surface: surface.clone(),
|
||||
parent: parent.clone(),
|
||||
position: Cell::new(Default::default()),
|
||||
sync_requested: Cell::new(false),
|
||||
sync_requested: Cell::new(true),
|
||||
sync_ancestor: Cell::new(false),
|
||||
node: RefCell::new(None),
|
||||
latest_node: Default::default(),
|
||||
depth: NumCell::new(0),
|
||||
pending: Default::default(),
|
||||
tracker: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
fn pending(&self) -> RefMut<Box<PendingSubsurfaceData>> {
|
||||
RefMut::map(self.surface.pending.borrow_mut(), |m| {
|
||||
m.subsurface.get_or_insert_default_ext()
|
||||
})
|
||||
}
|
||||
|
||||
pub fn install(self: &Rc<Self>) -> Result<(), WlSubsurfaceError> {
|
||||
if self.surface.id == self.parent.id {
|
||||
return Err(WlSubsurfaceError::OwnParent(self.surface.id));
|
||||
|
|
@ -128,7 +142,8 @@ impl WlSubsurface {
|
|||
sub_surface: self.clone(),
|
||||
})
|
||||
};
|
||||
*self.pending.node.borrow_mut() = Some(node);
|
||||
self.latest_node.set(Some(node.to_ref()));
|
||||
self.pending().node = Some(node);
|
||||
self.surface.set_toplevel(self.parent.toplevel.get());
|
||||
self.sync_ancestor.set(sync_ancestor);
|
||||
self.depth.set(depth);
|
||||
|
|
@ -140,8 +155,12 @@ impl WlSubsurface {
|
|||
fn destroy(&self, parser: MsgParser<'_, '_>) -> Result<(), WlSubsurfaceError> {
|
||||
let _req: Destroy = self.surface.client.parse(self, parser)?;
|
||||
self.surface.unset_ext();
|
||||
*self.pending.node.borrow_mut() = None;
|
||||
self.parent.consume_pending_child(self.unique_id, |oe| {
|
||||
self.surface.apply_state(&mut oe.remove().state)
|
||||
})?;
|
||||
self.surface.pending.borrow_mut().subsurface.take();
|
||||
*self.node.borrow_mut() = None;
|
||||
self.latest_node.take();
|
||||
{
|
||||
let mut children = self.parent.children.borrow_mut();
|
||||
if let Some(children) = &mut *children {
|
||||
|
|
@ -164,7 +183,7 @@ impl WlSubsurface {
|
|||
|
||||
fn set_position(&self, parser: MsgParser<'_, '_>) -> Result<(), WlSubsurfaceError> {
|
||||
let req: SetPosition = self.surface.client.parse(self, parser)?;
|
||||
self.pending.position.set(Some((req.x, req.y)));
|
||||
self.pending().position = Some((req.x, req.y));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
@ -188,19 +207,17 @@ impl WlSubsurface {
|
|||
Some(s) => s,
|
||||
_ => return Err(WlSubsurfaceError::NotASibling(sibling, self.surface.id)),
|
||||
};
|
||||
let node = match sibling.pending.node.borrow().deref() {
|
||||
Some(n) => n.to_ref(),
|
||||
_ => match sibling.node.borrow().deref() {
|
||||
Some(n) => n.to_ref(),
|
||||
_ => return Ok(()),
|
||||
},
|
||||
let sibling_node = match sibling.latest_node.get() {
|
||||
Some(n) => n,
|
||||
_ => return Ok(()),
|
||||
};
|
||||
match above {
|
||||
true => node.append(element),
|
||||
_ => node.prepend(element),
|
||||
true => sibling_node.append(element),
|
||||
_ => sibling_node.prepend(element),
|
||||
}
|
||||
};
|
||||
self.pending.node.borrow_mut().replace(node);
|
||||
self.latest_node.set(Some(node.to_ref()));
|
||||
self.pending().node.replace(node);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
|
@ -221,24 +238,56 @@ impl WlSubsurface {
|
|||
self.sync_requested.get() || self.sync_ancestor.get()
|
||||
}
|
||||
|
||||
fn update_sync(&self, sync: bool) {
|
||||
fn update_sync(&self, sync: bool) -> Result<(), WlSurfaceError> {
|
||||
let was_sync = self.sync();
|
||||
self.sync_requested.set(sync);
|
||||
let is_sync = self.sync();
|
||||
if was_sync != is_sync {
|
||||
update_children_sync(self, is_sync);
|
||||
self.handle_sync_change(is_sync)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_sync_change(&self, is_sync: bool) -> Result<(), WlSurfaceError> {
|
||||
if !is_sync {
|
||||
self.on_desync()?;
|
||||
}
|
||||
let children = self.surface.children.borrow();
|
||||
if let Some(children) = &*children {
|
||||
for child in children.subsurfaces.values() {
|
||||
let was_sync = child.sync();
|
||||
child.sync_ancestor.set(is_sync);
|
||||
let is_sync = child.sync();
|
||||
if was_sync != is_sync {
|
||||
child.handle_sync_change(is_sync)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn on_desync(&self) -> Result<(), WlSurfaceError> {
|
||||
let committed = self
|
||||
.parent
|
||||
.pending
|
||||
.borrow_mut()
|
||||
.subsurfaces
|
||||
.remove(&self.unique_id);
|
||||
if let Some(mut ps) = committed {
|
||||
self.surface.apply_state(&mut ps.state)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn set_sync(&self, parser: MsgParser<'_, '_>) -> Result<(), WlSubsurfaceError> {
|
||||
let _req: SetSync = self.surface.client.parse(self, parser)?;
|
||||
self.update_sync(true);
|
||||
self.update_sync(true)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn set_desync(&self, parser: MsgParser<'_, '_>) -> Result<(), WlSubsurfaceError> {
|
||||
let _req: SetDesync = self.surface.client.parse(self, parser)?;
|
||||
self.update_sync(false);
|
||||
self.update_sync(false)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
@ -256,31 +305,44 @@ object_base! {
|
|||
|
||||
impl Object for WlSubsurface {
|
||||
fn break_loops(&self) {
|
||||
*self.pending.node.borrow_mut() = None;
|
||||
*self.node.borrow_mut() = None;
|
||||
self.latest_node.take();
|
||||
}
|
||||
}
|
||||
|
||||
simple_add_obj!(WlSubsurface);
|
||||
|
||||
impl SurfaceExt for WlSubsurface {
|
||||
fn pre_commit(self: Rc<Self>, ctx: CommitContext) -> Result<CommitAction, WlSurfaceError> {
|
||||
if ctx == CommitContext::RootCommit && self.sync() {
|
||||
log::debug!("Aborting commit due to sync");
|
||||
return Ok(CommitAction::AbortCommit);
|
||||
fn commit_requested(self: Rc<Self>, pending: &mut Box<PendingState>) -> CommitAction {
|
||||
if self.sync() {
|
||||
let mut parent_pending = self.parent.pending.borrow_mut();
|
||||
match parent_pending.subsurfaces.entry(self.unique_id) {
|
||||
Entry::Occupied(mut o) => {
|
||||
o.get_mut().state.merge(pending, &self.surface.client);
|
||||
}
|
||||
Entry::Vacant(v) => {
|
||||
v.insert(CommittedSubsurface {
|
||||
subsurface: self.clone(),
|
||||
state: mem::take(&mut *pending),
|
||||
});
|
||||
}
|
||||
}
|
||||
return CommitAction::AbortCommit;
|
||||
}
|
||||
Ok(CommitAction::ContinueCommit)
|
||||
CommitAction::ContinueCommit
|
||||
}
|
||||
|
||||
fn post_commit(self: Rc<Self>) {
|
||||
if let Some(v) = self.pending.node.take() {
|
||||
v.pending.set(false);
|
||||
self.node.borrow_mut().replace(v);
|
||||
}
|
||||
if let Some((x, y)) = self.pending.position.take() {
|
||||
self.position
|
||||
.set(self.surface.buffer_abs_pos.get().at_point(x, y));
|
||||
self.parent.need_extents_update.set(true);
|
||||
fn after_apply_commit(self: Rc<Self>, pending: &mut PendingState) {
|
||||
if let Some(pending) = &mut pending.subsurface {
|
||||
if let Some(v) = pending.node.take() {
|
||||
v.pending.set(false);
|
||||
self.node.borrow_mut().replace(v);
|
||||
}
|
||||
if let Some((x, y)) = pending.position.take() {
|
||||
self.position
|
||||
.set(self.surface.buffer_abs_pos.get().at_point(x, y));
|
||||
self.parent.need_extents_update.set(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -295,6 +357,21 @@ impl SurfaceExt for WlSubsurface {
|
|||
fn into_subsurface(self: Rc<Self>) -> Option<Rc<WlSubsurface>> {
|
||||
Some(self)
|
||||
}
|
||||
|
||||
fn consume_pending_child(
|
||||
&self,
|
||||
surface: &WlSurface,
|
||||
child: SubsurfaceId,
|
||||
consume: &mut dyn FnMut(
|
||||
OccupiedEntry<SubsurfaceId, CommittedSubsurface>,
|
||||
) -> Result<(), WlSurfaceError>,
|
||||
) -> Result<(), WlSurfaceError> {
|
||||
self.parent
|
||||
.consume_pending_child(self.unique_id, |mut oe| {
|
||||
oe.get_mut().state.consume_child(child, &mut *consume)
|
||||
})?;
|
||||
surface.pending.borrow_mut().consume_child(child, consume)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
|
|
|
|||
103
src/ifs/wl_surface/wp_linux_drm_syncobj_surface_v1.rs
Normal file
103
src/ifs/wl_surface/wp_linux_drm_syncobj_surface_v1.rs
Normal file
|
|
@ -0,0 +1,103 @@
|
|||
use {
|
||||
crate::{
|
||||
client::{Client, ClientError},
|
||||
ifs::wl_surface::WlSurface,
|
||||
leaks::Tracker,
|
||||
object::Object,
|
||||
utils::buffd::{MsgParser, MsgParserError},
|
||||
video::drm::sync_obj::SyncObjPoint,
|
||||
wire::{wp_linux_drm_syncobj_surface_v1::*, WpLinuxDrmSyncobjSurfaceV1Id},
|
||||
},
|
||||
std::rc::Rc,
|
||||
thiserror::Error,
|
||||
};
|
||||
|
||||
pub struct WpLinuxDrmSyncobjSurfaceV1 {
|
||||
id: WpLinuxDrmSyncobjSurfaceV1Id,
|
||||
client: Rc<Client>,
|
||||
surface: Rc<WlSurface>,
|
||||
pub tracker: Tracker<Self>,
|
||||
}
|
||||
|
||||
impl WpLinuxDrmSyncobjSurfaceV1 {
|
||||
pub fn new(
|
||||
id: WpLinuxDrmSyncobjSurfaceV1Id,
|
||||
client: &Rc<Client>,
|
||||
surface: &Rc<WlSurface>,
|
||||
) -> Self {
|
||||
Self {
|
||||
id,
|
||||
client: client.clone(),
|
||||
tracker: Default::default(),
|
||||
surface: surface.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn install(self: &Rc<Self>) -> Result<(), WpLinuxDrmSyncobjSurfaceV1Error> {
|
||||
if self.surface.sync_obj_surface.is_some() {
|
||||
return Err(WpLinuxDrmSyncobjSurfaceV1Error::Exists);
|
||||
}
|
||||
self.surface.sync_obj_surface.set(Some(self.clone()));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn destroy(&self, parser: MsgParser<'_, '_>) -> Result<(), WpLinuxDrmSyncobjSurfaceV1Error> {
|
||||
let _req: Destroy = self.client.parse(self, parser)?;
|
||||
self.surface.sync_obj_surface.take();
|
||||
let pending = &mut *self.surface.pending.borrow_mut();
|
||||
pending.release_point.take();
|
||||
pending.acquire_point.take();
|
||||
self.client.remove_obj(self)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn set_acquire_point(
|
||||
&self,
|
||||
parser: MsgParser<'_, '_>,
|
||||
) -> Result<(), WpLinuxDrmSyncobjSurfaceV1Error> {
|
||||
let req: SetAcquirePoint = self.client.parse(self, parser)?;
|
||||
let point = point(req.point_hi, req.point_lo);
|
||||
let timeline = self.client.lookup(req.timeline)?;
|
||||
self.surface.pending.borrow_mut().acquire_point = Some((timeline.sync_obj.clone(), point));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn set_release_point(
|
||||
&self,
|
||||
parser: MsgParser<'_, '_>,
|
||||
) -> Result<(), WpLinuxDrmSyncobjSurfaceV1Error> {
|
||||
let req: SetReleasePoint = self.client.parse(self, parser)?;
|
||||
let point = point(req.point_hi, req.point_lo);
|
||||
let timeline = self.client.lookup(req.timeline)?;
|
||||
self.surface.pending.borrow_mut().release_point = Some((timeline.sync_obj.clone(), point));
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn point(hi: u32, lo: u32) -> SyncObjPoint {
|
||||
SyncObjPoint((hi as u64) << 32 | (lo as u64))
|
||||
}
|
||||
|
||||
object_base! {
|
||||
self = WpLinuxDrmSyncobjSurfaceV1;
|
||||
|
||||
DESTROY => destroy,
|
||||
SET_ACQUIRE_POINT => set_acquire_point,
|
||||
SET_RELEASE_POINT => set_release_point,
|
||||
}
|
||||
|
||||
impl Object for WpLinuxDrmSyncobjSurfaceV1 {}
|
||||
|
||||
simple_add_obj!(WpLinuxDrmSyncobjSurfaceV1);
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum WpLinuxDrmSyncobjSurfaceV1Error {
|
||||
#[error("Parsing failed")]
|
||||
MsgParserError(#[source] Box<MsgParserError>),
|
||||
#[error(transparent)]
|
||||
ClientError(Box<ClientError>),
|
||||
#[error("The surface already has a syncobj extension attached")]
|
||||
Exists,
|
||||
}
|
||||
efrom!(WpLinuxDrmSyncobjSurfaceV1Error, MsgParserError);
|
||||
efrom!(WpLinuxDrmSyncobjSurfaceV1Error, ClientError);
|
||||
|
|
@ -39,13 +39,13 @@ impl WpTearingControlV1 {
|
|||
ASYNC => true,
|
||||
_ => return Err(WpTearingControlV1Error::UnknownPresentationHint(req.hint)),
|
||||
};
|
||||
self.surface.pending.tearing.set(Some(tearing));
|
||||
self.surface.pending.borrow_mut().tearing = Some(tearing);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn destroy(&self, parser: MsgParser<'_, '_>) -> Result<(), WpTearingControlV1Error> {
|
||||
let _req: Destroy = self.surface.client.parse(self, parser)?;
|
||||
self.surface.pending.tearing.set(Some(false));
|
||||
self.surface.pending.borrow_mut().tearing = Some(false);
|
||||
self.surface.tearing_control.take();
|
||||
self.surface.client.remove_obj(self)?;
|
||||
Ok(())
|
||||
|
|
|
|||
|
|
@ -38,8 +38,9 @@ impl WpViewport {
|
|||
|
||||
fn destroy(&self, msg: MsgParser<'_, '_>) -> Result<(), WpViewportError> {
|
||||
let _req: Destroy = self.client.parse(self, msg)?;
|
||||
self.surface.pending.src_rect.set(Some(None));
|
||||
self.surface.pending.dst_size.set(Some(None));
|
||||
let pending = &mut *self.surface.pending.borrow_mut();
|
||||
pending.src_rect = Some(None);
|
||||
pending.dst_size = Some(None);
|
||||
self.surface.viewporter.take();
|
||||
self.client.remove_obj(self)?;
|
||||
Ok(())
|
||||
|
|
@ -56,7 +57,7 @@ impl WpViewport {
|
|||
}
|
||||
Some([req.x, req.y, req.width, req.height])
|
||||
};
|
||||
self.surface.pending.src_rect.set(Some(rect));
|
||||
self.surface.pending.borrow_mut().src_rect = Some(rect);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
@ -69,7 +70,7 @@ impl WpViewport {
|
|||
} else {
|
||||
Some((req.width, req.height))
|
||||
};
|
||||
self.surface.pending.dst_size.set(Some(size));
|
||||
self.surface.pending.borrow_mut().dst_size = Some(size);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ use {
|
|||
crate::{
|
||||
ifs::wl_surface::{
|
||||
x_surface::{xwayland_surface_v1::XwaylandSurfaceV1, xwindow::Xwindow},
|
||||
SurfaceExt, WlSurface, WlSurfaceError,
|
||||
PendingState, SurfaceExt, WlSurface, WlSurfaceError,
|
||||
},
|
||||
leaks::Tracker,
|
||||
tree::ToplevelNode,
|
||||
|
|
@ -23,7 +23,7 @@ pub struct XSurface {
|
|||
}
|
||||
|
||||
impl SurfaceExt for XSurface {
|
||||
fn post_commit(self: Rc<Self>) {
|
||||
fn after_apply_commit(self: Rc<Self>, _pending: &mut PendingState) {
|
||||
if let Some(xwindow) = self.xwindow.get() {
|
||||
xwindow.map_status_changed();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ impl XwaylandSurfaceV1 {
|
|||
return Err(XwaylandSurfaceV1Error::NonMonotonicSerial);
|
||||
}
|
||||
self.client.last_xwayland_serial.set(serial);
|
||||
self.x.surface.pending.xwayland_serial.set(Some(serial));
|
||||
self.x.surface.pending.borrow_mut().xwayland_serial = Some(serial);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ use {
|
|||
xdg_popup::{XdgPopup, XdgPopupError},
|
||||
xdg_toplevel::{XdgToplevel, WM_CAPABILITIES_SINCE},
|
||||
},
|
||||
CommitAction, CommitContext, SurfaceExt, SurfaceRole, WlSurface, WlSurfaceError,
|
||||
PendingState, SurfaceExt, SurfaceRole, WlSurface, WlSurfaceError,
|
||||
},
|
||||
xdg_wm_base::XdgWmBase,
|
||||
},
|
||||
|
|
@ -23,10 +23,15 @@ use {
|
|||
clonecell::CloneCell,
|
||||
copyhashmap::CopyHashMap,
|
||||
numcell::NumCell,
|
||||
option_ext::OptionExt,
|
||||
},
|
||||
wire::{xdg_surface::*, WlSurfaceId, XdgPopupId, XdgSurfaceId},
|
||||
},
|
||||
std::{cell::Cell, fmt::Debug, rc::Rc},
|
||||
std::{
|
||||
cell::{Cell, RefMut},
|
||||
fmt::Debug,
|
||||
rc::Rc,
|
||||
},
|
||||
thiserror::Error,
|
||||
};
|
||||
|
||||
|
|
@ -65,14 +70,26 @@ pub struct XdgSurface {
|
|||
pub absolute_desired_extents: Cell<Rect>,
|
||||
ext: CloneCell<Option<Rc<dyn XdgSurfaceExt>>>,
|
||||
popups: CopyHashMap<XdgPopupId, Rc<XdgPopup>>,
|
||||
pending: PendingXdgSurfaceData,
|
||||
pub workspace: CloneCell<Option<Rc<WorkspaceNode>>>,
|
||||
pub tracker: Tracker<Self>,
|
||||
}
|
||||
|
||||
#[derive(Default, Debug)]
|
||||
struct PendingXdgSurfaceData {
|
||||
geometry: Cell<Option<Rect>>,
|
||||
pub struct PendingXdgSurfaceData {
|
||||
geometry: Option<Rect>,
|
||||
}
|
||||
|
||||
impl PendingXdgSurfaceData {
|
||||
pub fn merge(&mut self, next: &mut Self) {
|
||||
macro_rules! opt {
|
||||
($name:ident) => {
|
||||
if let Some(n) = next.$name.take() {
|
||||
self.$name = Some(n);
|
||||
}
|
||||
};
|
||||
}
|
||||
opt!(geometry);
|
||||
}
|
||||
}
|
||||
|
||||
pub trait XdgSurfaceExt: Debug {
|
||||
|
|
@ -103,7 +120,6 @@ impl XdgSurface {
|
|||
absolute_desired_extents: Cell::new(Default::default()),
|
||||
ext: Default::default(),
|
||||
popups: Default::default(),
|
||||
pending: Default::default(),
|
||||
workspace: Default::default(),
|
||||
tracker: Default::default(),
|
||||
}
|
||||
|
|
@ -270,6 +286,12 @@ impl XdgSurface {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn pending(&self) -> RefMut<Box<PendingXdgSurfaceData>> {
|
||||
RefMut::map(self.surface.pending.borrow_mut(), |p| {
|
||||
p.xdg_surface.get_or_insert_default_ext()
|
||||
})
|
||||
}
|
||||
|
||||
fn set_window_geometry(&self, parser: MsgParser<'_, '_>) -> Result<(), XdgSurfaceError> {
|
||||
let req: SetWindowGeometry = self.surface.client.parse(self, parser)?;
|
||||
if req.height == 0 && req.width == 0 {
|
||||
|
|
@ -280,7 +302,7 @@ impl XdgSurface {
|
|||
return Err(XdgSurfaceError::NonPositiveWidthHeight);
|
||||
}
|
||||
let extents = Rect::new_sized(req.x, req.y, req.width, req.height).unwrap();
|
||||
self.pending.geometry.set(Some(extents));
|
||||
self.pending().geometry = Some(extents);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
@ -357,7 +379,10 @@ impl Object for XdgSurface {
|
|||
dedicated_add_obj!(XdgSurface, XdgSurfaceId, xdg_surfaces);
|
||||
|
||||
impl SurfaceExt for XdgSurface {
|
||||
fn pre_commit(self: Rc<Self>, _ctx: CommitContext) -> Result<CommitAction, WlSurfaceError> {
|
||||
fn before_apply_commit(
|
||||
self: Rc<Self>,
|
||||
pending: &mut PendingState,
|
||||
) -> Result<(), WlSurfaceError> {
|
||||
{
|
||||
let ase = self.acked_serial.get();
|
||||
let rse = self.requested_serial.get();
|
||||
|
|
@ -368,17 +393,18 @@ impl SurfaceExt for XdgSurface {
|
|||
}
|
||||
self.send_configure(rse);
|
||||
}
|
||||
// return CommitAction::AbortCommit;
|
||||
}
|
||||
}
|
||||
if let Some(geometry) = self.pending.geometry.take() {
|
||||
self.geometry.set(Some(geometry));
|
||||
self.update_extents();
|
||||
if let Some(pending) = &mut pending.xdg_surface {
|
||||
if let Some(geometry) = pending.geometry.take() {
|
||||
self.geometry.set(Some(geometry));
|
||||
self.update_extents();
|
||||
}
|
||||
}
|
||||
Ok(CommitAction::ContinueCommit)
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn post_commit(self: Rc<Self>) {
|
||||
fn after_apply_commit(self: Rc<Self>, _pending: &mut PendingState) {
|
||||
if let Some(ext) = self.ext.get() {
|
||||
ext.post_commit();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -646,7 +646,8 @@ impl ToplevelNodeBase for XdgToplevel {
|
|||
|
||||
impl XdgSurfaceExt for XdgToplevel {
|
||||
fn initial_configure(self: Rc<Self>) -> Result<(), XdgSurfaceError> {
|
||||
self.send_configure(0, 0);
|
||||
let rect = self.xdg.absolute_desired_extents.get();
|
||||
self.send_configure(rect.width(), rect.height());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3,9 +3,7 @@ use {
|
|||
client::{Client, ClientError},
|
||||
ifs::{
|
||||
wl_seat::NodeSeatState,
|
||||
wl_surface::{
|
||||
CommitAction, CommitContext, SurfaceExt, SurfaceRole, WlSurface, WlSurfaceError,
|
||||
},
|
||||
wl_surface::{PendingState, SurfaceExt, SurfaceRole, WlSurface, WlSurfaceError},
|
||||
zwlr_layer_shell_v1::{ZwlrLayerShellV1, OVERLAY},
|
||||
},
|
||||
leaks::Tracker,
|
||||
|
|
@ -19,10 +17,16 @@ use {
|
|||
cell_ext::CellExt,
|
||||
linkedlist::LinkedNode,
|
||||
numcell::NumCell,
|
||||
option_ext::OptionExt,
|
||||
},
|
||||
wire::{zwlr_layer_surface_v1::*, WlSurfaceId, ZwlrLayerSurfaceV1Id},
|
||||
},
|
||||
std::{cell::Cell, ops::Deref, rc::Rc},
|
||||
std::{
|
||||
cell::{Cell, RefMut},
|
||||
mem,
|
||||
ops::Deref,
|
||||
rc::Rc,
|
||||
},
|
||||
thiserror::Error,
|
||||
};
|
||||
|
||||
|
|
@ -50,7 +54,6 @@ pub struct ZwlrLayerSurfaceV1 {
|
|||
pos: Cell<Rect>,
|
||||
mapped: Cell<bool>,
|
||||
layer: Cell<u32>,
|
||||
pending: Pending,
|
||||
requested_serial: NumCell<u32>,
|
||||
acked_serial: Cell<Option<u32>>,
|
||||
size: Cell<(i32, i32)>,
|
||||
|
|
@ -63,14 +66,33 @@ pub struct ZwlrLayerSurfaceV1 {
|
|||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct Pending {
|
||||
size: Cell<Option<(i32, i32)>>,
|
||||
anchor: Cell<Option<u32>>,
|
||||
exclusive_zone: Cell<Option<i32>>,
|
||||
margin: Cell<Option<(i32, i32, i32, i32)>>,
|
||||
keyboard_interactivity: Cell<Option<u32>>,
|
||||
layer: Cell<Option<u32>>,
|
||||
any: Cell<bool>,
|
||||
pub struct PendingLayerSurfaceData {
|
||||
size: Option<(i32, i32)>,
|
||||
anchor: Option<u32>,
|
||||
exclusive_zone: Option<i32>,
|
||||
margin: Option<(i32, i32, i32, i32)>,
|
||||
keyboard_interactivity: Option<u32>,
|
||||
layer: Option<u32>,
|
||||
any: bool,
|
||||
}
|
||||
|
||||
impl PendingLayerSurfaceData {
|
||||
pub fn merge(&mut self, next: &mut Self) {
|
||||
macro_rules! opt {
|
||||
($name:ident) => {
|
||||
if let Some(n) = next.$name.take() {
|
||||
self.$name = Some(n);
|
||||
}
|
||||
};
|
||||
}
|
||||
opt!(size);
|
||||
opt!(anchor);
|
||||
opt!(exclusive_zone);
|
||||
opt!(margin);
|
||||
opt!(keyboard_interactivity);
|
||||
opt!(layer);
|
||||
self.any |= mem::take(&mut next.any);
|
||||
}
|
||||
}
|
||||
|
||||
impl ZwlrLayerSurfaceV1 {
|
||||
|
|
@ -95,7 +117,6 @@ impl ZwlrLayerSurfaceV1 {
|
|||
pos: Default::default(),
|
||||
mapped: Cell::new(false),
|
||||
layer: Cell::new(layer),
|
||||
pending: Default::default(),
|
||||
requested_serial: Default::default(),
|
||||
acked_serial: Cell::new(None),
|
||||
size: Cell::new((0, 0)),
|
||||
|
|
@ -131,15 +152,20 @@ impl ZwlrLayerSurfaceV1 {
|
|||
self.client.event(Closed { self_id: self.id });
|
||||
}
|
||||
|
||||
fn pending(&self) -> RefMut<Box<PendingLayerSurfaceData>> {
|
||||
RefMut::map(self.surface.pending.borrow_mut(), |m| {
|
||||
m.layer_surface.get_or_insert_default_ext()
|
||||
})
|
||||
}
|
||||
|
||||
fn set_size(&self, parser: MsgParser<'_, '_>) -> Result<(), ZwlrLayerSurfaceV1Error> {
|
||||
let req: SetSize = self.client.parse(self, parser)?;
|
||||
if req.width > u16::MAX as u32 || req.height > u16::MAX as u32 {
|
||||
return Err(ZwlrLayerSurfaceV1Error::ExcessiveSize);
|
||||
}
|
||||
self.pending
|
||||
.size
|
||||
.set(Some((req.width as _, req.height as _)));
|
||||
self.pending.any.set(true);
|
||||
let mut pending = self.pending();
|
||||
pending.size = Some((req.width as _, req.height as _));
|
||||
pending.any = true;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
@ -148,24 +174,25 @@ impl ZwlrLayerSurfaceV1 {
|
|||
if req.anchor & !(LEFT | RIGHT | TOP | BOTTOM) != 0 {
|
||||
return Err(ZwlrLayerSurfaceV1Error::UnknownAnchor(req.anchor));
|
||||
}
|
||||
self.pending.anchor.set(Some(req.anchor));
|
||||
self.pending.any.set(true);
|
||||
let mut pending = self.pending();
|
||||
pending.anchor = Some(req.anchor);
|
||||
pending.any = true;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn set_exclusive_zone(&self, parser: MsgParser<'_, '_>) -> Result<(), ZwlrLayerSurfaceV1Error> {
|
||||
let req: SetExclusiveZone = self.client.parse(self, parser)?;
|
||||
self.pending.exclusive_zone.set(Some(req.zone));
|
||||
self.pending.any.set(true);
|
||||
let mut pending = self.pending();
|
||||
pending.exclusive_zone = Some(req.zone);
|
||||
pending.any = true;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn set_margin(&self, parser: MsgParser<'_, '_>) -> Result<(), ZwlrLayerSurfaceV1Error> {
|
||||
let req: SetMargin = self.client.parse(self, parser)?;
|
||||
self.pending
|
||||
.margin
|
||||
.set(Some((req.top, req.right, req.bottom, req.left)));
|
||||
self.pending.any.set(true);
|
||||
let mut pending = self.pending();
|
||||
pending.margin = Some((req.top, req.right, req.bottom, req.left));
|
||||
pending.any = true;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
@ -179,10 +206,9 @@ impl ZwlrLayerSurfaceV1 {
|
|||
req.keyboard_interactivity,
|
||||
));
|
||||
}
|
||||
self.pending
|
||||
.keyboard_interactivity
|
||||
.set(Some(req.keyboard_interactivity));
|
||||
self.pending.any.set(true);
|
||||
let mut pending = self.pending();
|
||||
pending.keyboard_interactivity = Some(req.keyboard_interactivity);
|
||||
pending.any = true;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
@ -210,29 +236,31 @@ impl ZwlrLayerSurfaceV1 {
|
|||
if req.layer > OVERLAY {
|
||||
return Err(ZwlrLayerSurfaceV1Error::UnknownLayer(req.layer));
|
||||
}
|
||||
self.pending.layer.set(Some(req.layer));
|
||||
self.pending.any.set(true);
|
||||
let mut pending = self.pending();
|
||||
pending.layer = Some(req.layer);
|
||||
pending.any = true;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn pre_commit(&self) -> Result<(), ZwlrLayerSurfaceV1Error> {
|
||||
let mut send_configure = self.pending.any.replace(false);
|
||||
if let Some(size) = self.pending.size.take() {
|
||||
fn pre_commit(&self, pending: &mut PendingState) -> Result<(), ZwlrLayerSurfaceV1Error> {
|
||||
let pending = pending.layer_surface.get_or_insert_default_ext();
|
||||
let mut send_configure = mem::replace(&mut pending.any, false);
|
||||
if let Some(size) = pending.size.take() {
|
||||
self.size.set(size);
|
||||
}
|
||||
if let Some(anchor) = self.pending.anchor.take() {
|
||||
if let Some(anchor) = pending.anchor.take() {
|
||||
self.anchor.set(anchor);
|
||||
}
|
||||
if let Some(ez) = self.pending.exclusive_zone.take() {
|
||||
if let Some(ez) = pending.exclusive_zone.take() {
|
||||
self.exclusive_zone.set(ez);
|
||||
}
|
||||
if let Some(margin) = self.pending.margin.take() {
|
||||
if let Some(margin) = pending.margin.take() {
|
||||
self.margin.set(margin);
|
||||
}
|
||||
if let Some(ki) = self.pending.keyboard_interactivity.take() {
|
||||
if let Some(ki) = pending.keyboard_interactivity.take() {
|
||||
self.keyboard_interactivity.set(ki);
|
||||
}
|
||||
if let Some(layer) = self.pending.layer.take() {
|
||||
if let Some(layer) = pending.layer.take() {
|
||||
self.layer.set(layer);
|
||||
}
|
||||
{
|
||||
|
|
@ -313,15 +341,18 @@ impl ZwlrLayerSurfaceV1 {
|
|||
}
|
||||
|
||||
impl SurfaceExt for ZwlrLayerSurfaceV1 {
|
||||
fn pre_commit(self: Rc<Self>, _ctx: CommitContext) -> Result<CommitAction, WlSurfaceError> {
|
||||
self.deref().pre_commit()?;
|
||||
Ok(CommitAction::ContinueCommit)
|
||||
fn before_apply_commit(
|
||||
self: Rc<Self>,
|
||||
pending: &mut PendingState,
|
||||
) -> Result<(), WlSurfaceError> {
|
||||
self.deref().pre_commit(pending)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn post_commit(self: Rc<Self>) {
|
||||
let buffer = self.surface.buffer.get();
|
||||
fn after_apply_commit(self: Rc<Self>, _pending: &mut PendingState) {
|
||||
let buffer_is_some = self.surface.buffer.is_some();
|
||||
if self.mapped.get() {
|
||||
if buffer.is_none() {
|
||||
if !buffer_is_some {
|
||||
self.destroy_node();
|
||||
} else {
|
||||
let pos = self.pos.get();
|
||||
|
|
@ -330,7 +361,7 @@ impl SurfaceExt for ZwlrLayerSurfaceV1 {
|
|||
self.compute_position();
|
||||
}
|
||||
}
|
||||
} else if buffer.is_some() {
|
||||
} else if buffer_is_some {
|
||||
let layer = &self.output.layers[self.layer.get() as usize];
|
||||
self.link.set(Some(layer.add_last(self.clone())));
|
||||
self.mapped.set(true);
|
||||
|
|
|
|||
130
src/ifs/wp_linux_drm_syncobj_manager_v1.rs
Normal file
130
src/ifs/wp_linux_drm_syncobj_manager_v1.rs
Normal file
|
|
@ -0,0 +1,130 @@
|
|||
use {
|
||||
crate::{
|
||||
client::{Client, ClientError},
|
||||
globals::{Global, GlobalName},
|
||||
ifs::{
|
||||
wl_surface::wp_linux_drm_syncobj_surface_v1::{
|
||||
WpLinuxDrmSyncobjSurfaceV1, WpLinuxDrmSyncobjSurfaceV1Error,
|
||||
},
|
||||
wp_linux_drm_syncobj_timeline_v1::WpLinuxDrmSyncobjTimelineV1,
|
||||
},
|
||||
leaks::Tracker,
|
||||
object::Object,
|
||||
utils::buffd::{MsgParser, MsgParserError},
|
||||
video::drm::sync_obj::SyncObj,
|
||||
wire::{wp_linux_drm_syncobj_manager_v1::*, WpLinuxDrmSyncobjManagerV1Id},
|
||||
},
|
||||
std::rc::Rc,
|
||||
thiserror::Error,
|
||||
};
|
||||
|
||||
pub struct WpLinuxDrmSyncobjManagerV1Global {
|
||||
pub name: GlobalName,
|
||||
}
|
||||
|
||||
pub struct WpLinuxDrmSyncobjManagerV1 {
|
||||
pub id: WpLinuxDrmSyncobjManagerV1Id,
|
||||
pub client: Rc<Client>,
|
||||
pub tracker: Tracker<Self>,
|
||||
}
|
||||
|
||||
impl WpLinuxDrmSyncobjManagerV1Global {
|
||||
pub fn new(name: GlobalName) -> Self {
|
||||
Self { name }
|
||||
}
|
||||
|
||||
fn bind_(
|
||||
self: Rc<Self>,
|
||||
id: WpLinuxDrmSyncobjManagerV1Id,
|
||||
client: &Rc<Client>,
|
||||
_version: u32,
|
||||
) -> Result<(), WpLinuxDrmSyncobjManagerV1Error> {
|
||||
let obj = Rc::new(WpLinuxDrmSyncobjManagerV1 {
|
||||
id,
|
||||
client: client.clone(),
|
||||
tracker: Default::default(),
|
||||
});
|
||||
track!(client, obj);
|
||||
client.add_client_obj(&obj)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
global_base!(
|
||||
WpLinuxDrmSyncobjManagerV1Global,
|
||||
WpLinuxDrmSyncobjManagerV1,
|
||||
WpLinuxDrmSyncobjManagerV1Error
|
||||
);
|
||||
|
||||
impl Global for WpLinuxDrmSyncobjManagerV1Global {
|
||||
fn singleton(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn version(&self) -> u32 {
|
||||
1
|
||||
}
|
||||
}
|
||||
|
||||
simple_add_global!(WpLinuxDrmSyncobjManagerV1Global);
|
||||
|
||||
impl WpLinuxDrmSyncobjManagerV1 {
|
||||
fn destroy(&self, msg: MsgParser<'_, '_>) -> Result<(), WpLinuxDrmSyncobjManagerV1Error> {
|
||||
let _req: Destroy = self.client.parse(self, msg)?;
|
||||
self.client.remove_obj(self)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn get_surface(&self, msg: MsgParser<'_, '_>) -> Result<(), WpLinuxDrmSyncobjManagerV1Error> {
|
||||
let req: GetSurface = self.client.parse(self, msg)?;
|
||||
let surface = self.client.lookup(req.surface)?;
|
||||
let sync = Rc::new(WpLinuxDrmSyncobjSurfaceV1::new(
|
||||
req.id,
|
||||
&self.client,
|
||||
&surface,
|
||||
));
|
||||
track!(self.client, sync);
|
||||
sync.install()?;
|
||||
self.client.add_client_obj(&sync)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn import_timeline(
|
||||
&self,
|
||||
msg: MsgParser<'_, '_>,
|
||||
) -> Result<(), WpLinuxDrmSyncobjManagerV1Error> {
|
||||
let req: ImportTimeline = self.client.parse(self, msg)?;
|
||||
let sync_obj = Rc::new(SyncObj::new(&req.fd));
|
||||
let sync = Rc::new(WpLinuxDrmSyncobjTimelineV1::new(
|
||||
req.id,
|
||||
&self.client,
|
||||
&sync_obj,
|
||||
));
|
||||
self.client.add_client_obj(&sync)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
object_base! {
|
||||
self = WpLinuxDrmSyncobjManagerV1;
|
||||
|
||||
DESTROY => destroy,
|
||||
GET_SURFACE => get_surface,
|
||||
IMPORT_TIMELINE => import_timeline,
|
||||
}
|
||||
|
||||
impl Object for WpLinuxDrmSyncobjManagerV1 {}
|
||||
|
||||
simple_add_obj!(WpLinuxDrmSyncobjManagerV1);
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum WpLinuxDrmSyncobjManagerV1Error {
|
||||
#[error("Parsing failed")]
|
||||
MsgParserError(#[source] Box<MsgParserError>),
|
||||
#[error(transparent)]
|
||||
ClientError(Box<ClientError>),
|
||||
#[error(transparent)]
|
||||
WpLinuxDrmSyncobjSurfaceV1Error(#[from] WpLinuxDrmSyncobjSurfaceV1Error),
|
||||
}
|
||||
efrom!(WpLinuxDrmSyncobjManagerV1Error, MsgParserError);
|
||||
efrom!(WpLinuxDrmSyncobjManagerV1Error, ClientError);
|
||||
64
src/ifs/wp_linux_drm_syncobj_timeline_v1.rs
Normal file
64
src/ifs/wp_linux_drm_syncobj_timeline_v1.rs
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
use {
|
||||
crate::{
|
||||
client::{Client, ClientError},
|
||||
leaks::Tracker,
|
||||
object::Object,
|
||||
utils::buffd::{MsgParser, MsgParserError},
|
||||
video::drm::sync_obj::SyncObj,
|
||||
wire::{wp_linux_drm_syncobj_timeline_v1::*, WpLinuxDrmSyncobjTimelineV1Id},
|
||||
},
|
||||
std::rc::Rc,
|
||||
thiserror::Error,
|
||||
};
|
||||
|
||||
pub struct WpLinuxDrmSyncobjTimelineV1 {
|
||||
id: WpLinuxDrmSyncobjTimelineV1Id,
|
||||
client: Rc<Client>,
|
||||
pub sync_obj: Rc<SyncObj>,
|
||||
pub tracker: Tracker<Self>,
|
||||
}
|
||||
|
||||
impl WpLinuxDrmSyncobjTimelineV1 {
|
||||
pub fn new(
|
||||
id: WpLinuxDrmSyncobjTimelineV1Id,
|
||||
client: &Rc<Client>,
|
||||
sync_obj: &Rc<SyncObj>,
|
||||
) -> Self {
|
||||
Self {
|
||||
id,
|
||||
client: client.clone(),
|
||||
tracker: Default::default(),
|
||||
sync_obj: sync_obj.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
fn destroy(&self, parser: MsgParser<'_, '_>) -> Result<(), WpLinuxDrmSyncobjTimelineV1Error> {
|
||||
let _destroy: Destroy = self.client.parse(self, parser)?;
|
||||
self.client.remove_obj(self)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
object_base! {
|
||||
self = WpLinuxDrmSyncobjTimelineV1;
|
||||
|
||||
DESTROY => destroy,
|
||||
}
|
||||
|
||||
impl Object for WpLinuxDrmSyncobjTimelineV1 {}
|
||||
|
||||
dedicated_add_obj!(
|
||||
WpLinuxDrmSyncobjTimelineV1,
|
||||
WpLinuxDrmSyncobjTimelineV1Id,
|
||||
timelines
|
||||
);
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum WpLinuxDrmSyncobjTimelineV1Error {
|
||||
#[error("Parsing failed")]
|
||||
MsgParserError(#[source] Box<MsgParserError>),
|
||||
#[error(transparent)]
|
||||
ClientError(Box<ClientError>),
|
||||
}
|
||||
efrom!(WpLinuxDrmSyncobjTimelineV1Error, MsgParserError);
|
||||
efrom!(WpLinuxDrmSyncobjTimelineV1Error, ClientError);
|
||||
|
|
@ -71,7 +71,7 @@ impl XdgToplevelDragV1 {
|
|||
return Err(XdgToplevelDragV1Error::AlreadyDragged);
|
||||
}
|
||||
if let Some(prev) = self.toplevel.set(Some(toplevel)) {
|
||||
if prev.xdg.surface.buffer.get().is_some() {
|
||||
if prev.xdg.surface.buffer.is_some() {
|
||||
return Err(XdgToplevelDragV1Error::ToplevelAttached);
|
||||
}
|
||||
if prev.id != req.toplevel {
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@ async fn test(run: Rc<TestRun>) -> Result<(), TestError> {
|
|||
buffer.fill(Color::from_rgba_straight(255, 255, 255, 255));
|
||||
|
||||
child.attach(buffer.id)?;
|
||||
child.commit()?;
|
||||
|
||||
parent.map().await?;
|
||||
|
||||
|
|
@ -45,10 +46,12 @@ async fn test(run: Rc<TestRun>) -> Result<(), TestError> {
|
|||
client.compare_screenshot("1").await?;
|
||||
|
||||
sub.place_below(parent.surface.id)?;
|
||||
child.commit()?;
|
||||
parent.map().await?;
|
||||
client.compare_screenshot("2").await?;
|
||||
|
||||
sub.place_above(parent.surface.id)?;
|
||||
child.commit()?;
|
||||
parent.map().await?;
|
||||
client.compare_screenshot("1").await?;
|
||||
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ async fn test(run: Rc<TestRun>) -> TestResult {
|
|||
nss.set_position(100, 100)?;
|
||||
let buffer = client.shm.create_buffer(100, 100)?;
|
||||
ns.attach(buffer.id)?;
|
||||
ns.commit()?;
|
||||
|
||||
run.cfg.set_fullscreen(ds.seat.id(), true)?;
|
||||
client.sync().await;
|
||||
|
|
|
|||
|
|
@ -21,15 +21,14 @@ use {
|
|||
ptl_screencast::{add_screencast_dbus_members, ScreencastSession},
|
||||
},
|
||||
utils::{
|
||||
buf::Buf,
|
||||
clone3::{fork_with_pidfd, Forked},
|
||||
copyhashmap::CopyHashMap,
|
||||
errorfmt::ErrorFmt,
|
||||
line_logger::log_lines,
|
||||
numcell::NumCell,
|
||||
oserror::OsError,
|
||||
process_name::set_process_name,
|
||||
run_toplevel::RunToplevel,
|
||||
vecdeque_ext::VecDequeExt,
|
||||
xrd::xrd,
|
||||
},
|
||||
video::dmabuf::DmaBufIds,
|
||||
|
|
@ -38,7 +37,6 @@ use {
|
|||
},
|
||||
log::Level,
|
||||
std::{
|
||||
collections::VecDeque,
|
||||
rc::{Rc, Weak},
|
||||
sync::Arc,
|
||||
},
|
||||
|
|
@ -104,25 +102,14 @@ impl PortalStartup {
|
|||
let ring = ring.clone();
|
||||
let logger = logger.clone();
|
||||
async move {
|
||||
let mut buf = VecDeque::<u8>::new();
|
||||
let mut buf2 = Buf::new(1024);
|
||||
let mut done = false;
|
||||
while !done {
|
||||
match ring.read(&self.logs, buf2.clone()).await {
|
||||
Ok(n) if n > 0 => buf.extend(&buf2[..n]),
|
||||
Ok(_) => done = true,
|
||||
Err(e) => {
|
||||
log::error!("Could not read portal logs: {}", ErrorFmt(e));
|
||||
return;
|
||||
}
|
||||
};
|
||||
while let Some(pos) = buf.iter().position(|b| b == &b'\n') {
|
||||
let (left, right) = buf.get_slices(..pos);
|
||||
logger.write_raw(left);
|
||||
logger.write_raw(right);
|
||||
logger.write_raw(b" (portal)\n");
|
||||
buf.drain(..=pos);
|
||||
}
|
||||
let res = log_lines(&ring, &self.logs, |left, right| {
|
||||
logger.write_raw(left);
|
||||
logger.write_raw(right);
|
||||
logger.write_raw(b" (portal)\n");
|
||||
})
|
||||
.await;
|
||||
if let Err(e) = res {
|
||||
log::error!("Could not read portal logs: {}", ErrorFmt(e));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
@ -222,6 +222,9 @@ impl GuiElement for Button {
|
|||
None,
|
||||
r.scale(),
|
||||
None,
|
||||
None,
|
||||
AcquireSync::None,
|
||||
ReleaseSync::None,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -323,6 +326,9 @@ impl GuiElement for Label {
|
|||
None,
|
||||
r.scale(),
|
||||
None,
|
||||
None,
|
||||
AcquireSync::None,
|
||||
ReleaseSync::None,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -626,6 +632,19 @@ impl WindowData {
|
|||
}
|
||||
return;
|
||||
};
|
||||
|
||||
let res = buf
|
||||
.fb
|
||||
.render_custom(self.scale.get(), Some(&Color::from_gray(0)), &mut |r| {
|
||||
if let Some(content) = self.content.get() {
|
||||
content.render_at(r, 0.0, 0.0)
|
||||
}
|
||||
});
|
||||
if let Err(e) = res {
|
||||
log::error!("Could not render frame: {}", ErrorFmt(e));
|
||||
return;
|
||||
}
|
||||
|
||||
self.frame_missed.set(false);
|
||||
|
||||
self.surface.frame({
|
||||
|
|
@ -641,13 +660,6 @@ impl WindowData {
|
|||
self.have_frame.set(false);
|
||||
buf.free.set(false);
|
||||
|
||||
buf.fb
|
||||
.render_custom(self.scale.get(), Some(&Color::from_gray(0)), &mut |r| {
|
||||
if let Some(content) = self.content.get() {
|
||||
content.render_at(r, 0.0, 0.0)
|
||||
}
|
||||
});
|
||||
|
||||
self.surface.attach(&buf.wl);
|
||||
self.surface.commit();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
use {
|
||||
crate::{
|
||||
gfx_api::{GfxApiOpt, SampleRect},
|
||||
gfx_api::{AcquireSync, GfxApiOpt, ReleaseSync, SampleRect},
|
||||
ifs::{
|
||||
wl_buffer::WlBuffer,
|
||||
wl_callback::WlCallback,
|
||||
wl_surface::{
|
||||
xdg_surface::XdgSurface, zwlr_layer_surface_v1::ZwlrLayerSurfaceV1, WlSurface,
|
||||
xdg_surface::XdgSurface, zwlr_layer_surface_v1::ZwlrLayerSurfaceV1, SurfaceBuffer,
|
||||
WlSurface,
|
||||
},
|
||||
wp_presentation_feedback::WpPresentationFeedback,
|
||||
},
|
||||
|
|
@ -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);
|
||||
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);
|
||||
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);
|
||||
self.base.render_texture(
|
||||
&tex.texture,
|
||||
x,
|
||||
y,
|
||||
None,
|
||||
None,
|
||||
self.base.scale,
|
||||
None,
|
||||
None,
|
||||
AcquireSync::None,
|
||||
ReleaseSync::None,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -231,6 +261,9 @@ impl Renderer<'_> {
|
|||
None,
|
||||
self.base.scale,
|
||||
None,
|
||||
None,
|
||||
AcquireSync::None,
|
||||
ReleaseSync::None,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -345,14 +378,14 @@ impl Renderer<'_> {
|
|||
|
||||
pub fn render_buffer(
|
||||
&mut self,
|
||||
buffer: &WlBuffer,
|
||||
buffer: &Rc<SurfaceBuffer>,
|
||||
x: i32,
|
||||
y: i32,
|
||||
tpoints: SampleRect,
|
||||
tsize: (i32, i32),
|
||||
bounds: Option<&Rect>,
|
||||
) {
|
||||
if let Some(tex) = buffer.texture.get() {
|
||||
if let Some(tex) = buffer.buffer.texture.get() {
|
||||
self.base.render_texture(
|
||||
&tex,
|
||||
x,
|
||||
|
|
@ -361,8 +394,11 @@ impl Renderer<'_> {
|
|||
Some(tsize),
|
||||
self.base.scale,
|
||||
bounds,
|
||||
Some(buffer.clone()),
|
||||
buffer.sync.clone(),
|
||||
buffer.release_sync,
|
||||
);
|
||||
} else if let Some(color) = &buffer.color {
|
||||
} else if let Some(color) = &buffer.buffer.color {
|
||||
if let Some(rect) = Rect::new_sized(x, y, tsize.0, tsize.1) {
|
||||
let rect = match bounds {
|
||||
None => rect,
|
||||
|
|
@ -409,8 +445,18 @@ impl Renderer<'_> {
|
|||
self.base.fill_boxes(&title_underline, &uc);
|
||||
if let Some(title) = floating.title_textures.get(&self.base.scale) {
|
||||
let (x, y) = self.base.scale_point(x + bw, y + bw);
|
||||
self.base
|
||||
.render_texture(&title.texture, x, y, None, None, self.base.scale, None);
|
||||
self.base.render_texture(
|
||||
&title.texture,
|
||||
x,
|
||||
y,
|
||||
None,
|
||||
None,
|
||||
self.base.scale,
|
||||
None,
|
||||
None,
|
||||
AcquireSync::None,
|
||||
ReleaseSync::None,
|
||||
);
|
||||
}
|
||||
let body = Rect::new_sized(
|
||||
x + bw,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,9 @@
|
|||
use {
|
||||
crate::{
|
||||
gfx_api::{CopyTexture, FillRect, FramebufferRect, GfxApiOpt, GfxTexture, SampleRect},
|
||||
gfx_api::{
|
||||
AcquireSync, BufferResv, CopyTexture, FillRect, FramebufferRect, GfxApiOpt, GfxTexture,
|
||||
ReleaseSync, SampleRect,
|
||||
},
|
||||
rect::Rect,
|
||||
scale::Scale,
|
||||
theme::Color,
|
||||
|
|
@ -130,6 +133,9 @@ impl RendererBase<'_> {
|
|||
tsize: Option<(i32, i32)>,
|
||||
tscale: Scale,
|
||||
bounds: Option<&Rect>,
|
||||
buffer_resv: Option<Rc<dyn BufferResv>>,
|
||||
acquire_sync: AcquireSync,
|
||||
release_sync: ReleaseSync,
|
||||
) {
|
||||
let mut texcoord = tpoints.unwrap_or_else(SampleRect::identity);
|
||||
|
||||
|
|
@ -154,18 +160,23 @@ impl RendererBase<'_> {
|
|||
}
|
||||
}
|
||||
|
||||
let target = FramebufferRect::new(
|
||||
target_x[0] as f32,
|
||||
target_y[0] as f32,
|
||||
target_x[1] as f32,
|
||||
target_y[1] as f32,
|
||||
self.transform,
|
||||
self.fb_width,
|
||||
self.fb_height,
|
||||
);
|
||||
|
||||
self.ops.push(GfxApiOpt::CopyTexture(CopyTexture {
|
||||
tex: texture.clone(),
|
||||
source: texcoord,
|
||||
target: FramebufferRect::new(
|
||||
target_x[0] as f32,
|
||||
target_y[0] as f32,
|
||||
target_x[1] as f32,
|
||||
target_y[1] as f32,
|
||||
self.transform,
|
||||
self.fb_width,
|
||||
self.fb_height,
|
||||
),
|
||||
target,
|
||||
buffer_resv,
|
||||
acquire_sync,
|
||||
release_sync,
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -78,7 +78,7 @@ pub fn take_screenshot(state: &State) -> Result<Screenshot, ScreenshooterError>
|
|||
true,
|
||||
false,
|
||||
Transform::None,
|
||||
);
|
||||
)?;
|
||||
let drm = gbm.drm.dup_render()?.fd().clone();
|
||||
Ok(Screenshot { drm, bo })
|
||||
}
|
||||
|
|
|
|||
109
src/state.rs
109
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::{
|
||||
|
|
@ -30,9 +33,11 @@ use {
|
|||
wl_output::{OutputId, PersistentOutputState},
|
||||
wl_seat::{SeatIds, WlSeatGlobal},
|
||||
wl_surface::{
|
||||
wl_subsurface::SubsurfaceIds,
|
||||
zwp_idle_inhibitor_v1::{IdleInhibitorId, IdleInhibitorIds, ZwpIdleInhibitorV1},
|
||||
NoneSurfaceExt, WlSurface,
|
||||
},
|
||||
wp_linux_drm_syncobj_manager_v1::WpLinuxDrmSyncobjManagerV1Global,
|
||||
zwlr_screencopy_frame_v1::ZwlrScreencopyFrameV1,
|
||||
zwp_linux_dmabuf_feedback_v1::ZwpLinuxDmabufFeedbackV1,
|
||||
zwp_linux_dmabuf_v1::ZwpLinuxDmabufV1Global,
|
||||
|
|
@ -55,7 +60,14 @@ use {
|
|||
linkedlist::LinkedList, numcell::NumCell, queue::AsyncQueue, refcounted::RefCounted,
|
||||
run_toplevel::RunToplevel,
|
||||
},
|
||||
video::{dmabuf::DmaBufIds, drm::Drm},
|
||||
video::{
|
||||
dmabuf::DmaBufIds,
|
||||
drm::{
|
||||
sync_obj::{SyncObj, SyncObjPoint},
|
||||
wait_for_sync_obj::WaitForSyncObj,
|
||||
Drm,
|
||||
},
|
||||
},
|
||||
wheel::Wheel,
|
||||
wire::{
|
||||
ExtForeignToplevelListV1Id, JayRenderCtxId, JaySeatEventsId, JayWorkspaceWatcherId,
|
||||
|
|
@ -80,6 +92,7 @@ use {
|
|||
sync::Arc,
|
||||
time::Duration,
|
||||
},
|
||||
thiserror::Error,
|
||||
};
|
||||
|
||||
pub struct State {
|
||||
|
|
@ -157,6 +170,9 @@ pub struct State {
|
|||
pub double_click_interval_usec: Cell<u64>,
|
||||
pub double_click_distance: Cell<i32>,
|
||||
pub create_default_seat: Cell<bool>,
|
||||
pub subsurface_ids: SubsurfaceIds,
|
||||
pub wait_for_sync_obj: Rc<WaitForSyncObj>,
|
||||
pub explicit_sync_enabled: Cell<bool>,
|
||||
}
|
||||
|
||||
// impl Drop for State {
|
||||
|
|
@ -361,6 +377,8 @@ impl State {
|
|||
self.render_ctx_version.fetch_add(1);
|
||||
self.cursors.set(None);
|
||||
self.drm_feedback.set(None);
|
||||
self.wait_for_sync_obj
|
||||
.set_ctx(ctx.as_ref().map(|c| c.sync_obj_ctx().clone()));
|
||||
|
||||
'handle_new_feedback: {
|
||||
if let Some(ctx) = &ctx {
|
||||
|
|
@ -406,7 +424,7 @@ impl State {
|
|||
}
|
||||
fn visit_surface(&mut self, node: &Rc<WlSurface>) {
|
||||
if let Some(buffer) = node.buffer.get() {
|
||||
buffer.handle_gfx_context_change();
|
||||
buffer.buffer.handle_gfx_context_change();
|
||||
}
|
||||
node.node_visit_children(self);
|
||||
}
|
||||
|
|
@ -429,11 +447,18 @@ impl State {
|
|||
seat.render_ctx_changed();
|
||||
}
|
||||
|
||||
if ctx.is_some() && !self.render_ctx_ever_initialized.replace(true) {
|
||||
self.add_global(&Rc::new(WlDrmGlobal::new(self.globals.name())));
|
||||
self.add_global(&Rc::new(ZwpLinuxDmabufV1Global::new(self.globals.name())));
|
||||
if let Some(config) = self.config.get() {
|
||||
config.graphics_initialized();
|
||||
if let Some(ctx) = &ctx {
|
||||
if !self.render_ctx_ever_initialized.replace(true) {
|
||||
self.add_global(&Rc::new(WlDrmGlobal::new(self.globals.name())));
|
||||
self.add_global(&Rc::new(ZwpLinuxDmabufV1Global::new(self.globals.name())));
|
||||
if ctx.sync_obj_ctx().supports_async_wait() && self.explicit_sync_enabled.get() {
|
||||
self.add_global(&Rc::new(WpLinuxDrmSyncobjManagerV1Global::new(
|
||||
self.globals.name(),
|
||||
)));
|
||||
}
|
||||
if let Some(config) = self.config.get() {
|
||||
config.graphics_initialized();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -767,17 +792,18 @@ impl State {
|
|||
tex: &Rc<dyn GfxTexture>,
|
||||
rr: &mut RenderResult,
|
||||
render_hw_cursor: bool,
|
||||
) {
|
||||
fb.render_output(
|
||||
) -> Result<Option<SyncFile>, GfxError> {
|
||||
let sync_file = fb.render_output(
|
||||
output,
|
||||
self,
|
||||
Some(output.global.pos.get()),
|
||||
Some(rr),
|
||||
output.global.persistent.scale.get(),
|
||||
render_hw_cursor,
|
||||
);
|
||||
)?;
|
||||
output.perform_screencopies(tex, !render_hw_cursor, 0, 0, None);
|
||||
rr.dispatch_frame_requests();
|
||||
Ok(sync_file)
|
||||
}
|
||||
|
||||
pub fn perform_screencopy(
|
||||
|
|
@ -790,7 +816,7 @@ impl State {
|
|||
y_off: i32,
|
||||
size: Option<(i32, i32)>,
|
||||
transform: Transform,
|
||||
) {
|
||||
) -> Result<Option<SyncFile>, GfxError> {
|
||||
let mut ops = target.take_render_ops();
|
||||
let mut renderer = Renderer {
|
||||
base: target.renderer_base(&mut ops, Scale::from_int(1), Transform::None),
|
||||
|
|
@ -812,6 +838,9 @@ impl State {
|
|||
size,
|
||||
Scale::from_int(1),
|
||||
None,
|
||||
None,
|
||||
AcquireSync::None,
|
||||
ReleaseSync::Implicit,
|
||||
);
|
||||
if render_hardware_cursors {
|
||||
for seat in self.globals.lock_seats().values() {
|
||||
|
|
@ -825,7 +854,7 @@ impl State {
|
|||
}
|
||||
}
|
||||
}
|
||||
target.render(ops, Some(&Color::SOLID_BLACK));
|
||||
target.render(ops, Some(&Color::SOLID_BLACK))
|
||||
}
|
||||
|
||||
fn have_hardware_cursor(&self) -> bool {
|
||||
|
|
@ -851,7 +880,7 @@ impl State {
|
|||
stride: i32,
|
||||
format: &'static Format,
|
||||
transform: Transform,
|
||||
) {
|
||||
) -> Result<(), ShmScreencopyError> {
|
||||
let (src_width, src_height) = src.size();
|
||||
let mut needs_copy = capture.rect.x1() < x_off
|
||||
|| capture.rect.x2() > x_off + src_width
|
||||
|
|
@ -866,20 +895,11 @@ impl State {
|
|||
}
|
||||
let acc = if needs_copy {
|
||||
let Some(ctx) = self.render_ctx.get() else {
|
||||
log::warn!("Cannot perform shm screencopy because there is no render context");
|
||||
return;
|
||||
return Err(ShmScreencopyError::NoRenderContext);
|
||||
};
|
||||
let fb =
|
||||
match ctx.create_fb(capture.rect.width(), capture.rect.height(), stride, format) {
|
||||
Ok(f) => f,
|
||||
Err(e) => {
|
||||
log::warn!(
|
||||
"Could not create temporary fb for screencopy: {}",
|
||||
ErrorFmt(e)
|
||||
);
|
||||
return;
|
||||
}
|
||||
};
|
||||
let fb = ctx
|
||||
.create_fb(capture.rect.width(), capture.rect.height(), stride, format)
|
||||
.map_err(ShmScreencopyError::CreateTemporaryFb)?;
|
||||
self.perform_screencopy(
|
||||
src,
|
||||
&fb,
|
||||
|
|
@ -889,7 +909,8 @@ impl State {
|
|||
y_off - capture.rect.y1(),
|
||||
size,
|
||||
transform,
|
||||
);
|
||||
)
|
||||
.map_err(ShmScreencopyError::CopyToTemporary)?;
|
||||
mem.access(|mem| {
|
||||
fb.copy_to_shm(
|
||||
0,
|
||||
|
|
@ -914,16 +935,12 @@ impl State {
|
|||
)
|
||||
})
|
||||
};
|
||||
let res = match acc {
|
||||
Ok(res) => res,
|
||||
match acc {
|
||||
Ok(res) => res.map_err(ShmScreencopyError::ReadPixels),
|
||||
Err(e) => {
|
||||
capture.client.error(e);
|
||||
return;
|
||||
Ok(())
|
||||
}
|
||||
};
|
||||
if let Err(e) = res {
|
||||
log::warn!("Could not read texture to memory: {}", ErrorFmt(e));
|
||||
capture.send_failed();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -933,4 +950,26 @@ impl State {
|
|||
self.globals.add_global(self, &seat);
|
||||
seat
|
||||
}
|
||||
|
||||
pub fn signal_point(&self, sync_obj: &SyncObj, point: SyncObjPoint) {
|
||||
let Some(ctx) = self.render_ctx.get() else {
|
||||
log::error!("Cannot signal sync obj point because there is no render context");
|
||||
return;
|
||||
};
|
||||
if let Err(e) = ctx.sync_obj_ctx().signal(sync_obj, point) {
|
||||
log::error!("Could not signal sync obj: {}", ErrorFmt(e));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum ShmScreencopyError {
|
||||
#[error("There is no render context")]
|
||||
NoRenderContext,
|
||||
#[error("Could not create a bridge framebuffer")]
|
||||
CreateTemporaryFb(#[source] GfxError),
|
||||
#[error("Could not copy texture to bridge framebuffer")]
|
||||
CopyToTemporary(#[source] GfxError),
|
||||
#[error("Could not read pixels from texture")]
|
||||
ReadPixels(#[source] GfxError),
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ pub mod errorfmt;
|
|||
pub mod fdcloser;
|
||||
pub mod gfx_api_ext;
|
||||
pub mod hex;
|
||||
pub mod line_logger;
|
||||
pub mod linkedlist;
|
||||
pub mod log_on_drop;
|
||||
pub mod mmap;
|
||||
|
|
|
|||
36
src/utils/line_logger.rs
Normal file
36
src/utils/line_logger.rs
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
use {
|
||||
crate::{
|
||||
io_uring::{IoUring, IoUringError},
|
||||
utils::{buf::Buf, vecdeque_ext::VecDequeExt},
|
||||
},
|
||||
isnt::std_1::collections::IsntVecDequeExt,
|
||||
std::{collections::VecDeque, rc::Rc},
|
||||
uapi::OwnedFd,
|
||||
};
|
||||
|
||||
pub async fn log_lines(
|
||||
ring: &IoUring,
|
||||
fd: &Rc<OwnedFd>,
|
||||
mut f: impl FnMut(&[u8], &[u8]),
|
||||
) -> Result<(), IoUringError> {
|
||||
let mut buf = VecDeque::<u8>::new();
|
||||
let mut buf2 = Buf::new(1024);
|
||||
let mut done = false;
|
||||
while !done {
|
||||
let n = ring.read(fd, buf2.clone()).await?;
|
||||
buf.extend(&buf2[..n]);
|
||||
if n == 0 {
|
||||
done = true;
|
||||
}
|
||||
while let Some(pos) = buf.iter().position(|b| b == &b'\n') {
|
||||
let (left, right) = buf.get_slices(..pos);
|
||||
f(left, right);
|
||||
buf.drain(..=pos);
|
||||
}
|
||||
}
|
||||
if buf.is_not_empty() {
|
||||
let (left, right) = buf.as_slices();
|
||||
f(left, right);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
|
@ -46,6 +46,23 @@ impl<T> LinkedList<T> {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn append_all(&self, other: &LinkedList<T>) {
|
||||
if other.is_empty() {
|
||||
return;
|
||||
}
|
||||
unsafe {
|
||||
let o_root = other.root.data.as_ref();
|
||||
let o_first = o_root.next.get();
|
||||
let o_last = o_root.prev.get();
|
||||
let s_first = self.root.data;
|
||||
let s_last = s_first.as_ref().prev.get();
|
||||
o_first.as_ref().prev.set(s_last);
|
||||
s_last.as_ref().next.set(o_first);
|
||||
o_last.as_ref().next.set(s_first);
|
||||
s_first.as_ref().prev.set(o_last);
|
||||
}
|
||||
}
|
||||
|
||||
fn endpoint(&self, ep: NonNull<NodeData<T>>) -> Option<NodeRef<T>> {
|
||||
unsafe {
|
||||
if ep != self.root.data {
|
||||
|
|
@ -57,11 +74,14 @@ impl<T> LinkedList<T> {
|
|||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.last().is_none()
|
||||
}
|
||||
|
||||
pub fn is_not_empty(&self) -> bool {
|
||||
!self.is_empty()
|
||||
}
|
||||
|
||||
pub fn last(&self) -> Option<NodeRef<T>> {
|
||||
unsafe { self.endpoint(self.root.data.as_ref().prev.get()) }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -58,6 +58,10 @@ impl<K: Eq, V, const N: usize> SmallMap<K, V, N> {
|
|||
unsafe { self.m.get().deref().is_empty() }
|
||||
}
|
||||
|
||||
pub fn is_not_empty(&self) -> bool {
|
||||
!self.is_empty()
|
||||
}
|
||||
|
||||
pub fn remove(&self, k: &K) -> Option<V> {
|
||||
unsafe { self.m.get().deref_mut().remove(k) }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -48,6 +48,13 @@ impl DmaBuf {
|
|||
}
|
||||
false
|
||||
}
|
||||
|
||||
pub fn import_sync_file(&self, flags: u32, sync_file: &OwnedFd) -> Result<(), OsError> {
|
||||
for plane in &self.planes {
|
||||
dma_buf_import_sync_file(&plane.fd, flags, sync_file)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
const DMA_BUF_BASE: u64 = b'b' as _;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
pub mod sync_obj;
|
||||
mod sys;
|
||||
pub mod wait_for_sync_obj;
|
||||
|
||||
use {
|
||||
crate::{
|
||||
|
|
@ -52,7 +54,7 @@ pub use sys::{
|
|||
#[derive(Debug, Error)]
|
||||
pub enum DrmError {
|
||||
#[error("Could not reopen a node")]
|
||||
ReopenNode(#[source] crate::utils::oserror::OsError),
|
||||
ReopenNode(#[source] OsError),
|
||||
#[error("Could not retrieve the render node name")]
|
||||
RenderNodeName(#[source] OsError),
|
||||
#[error("Could not retrieve the device node name")]
|
||||
|
|
@ -113,6 +115,28 @@ pub enum DrmError {
|
|||
Version(#[source] OsError),
|
||||
#[error("Format of IN_FORMATS property is invalid")]
|
||||
InFormats,
|
||||
#[error("Could not import a sync obj")]
|
||||
ImportSyncObj(#[source] OsError),
|
||||
#[error("Could not create a sync obj")]
|
||||
CreateSyncObj(#[source] OsError),
|
||||
#[error("Could not export a sync obj")]
|
||||
ExportSyncObj(#[source] OsError),
|
||||
#[error("Could not register an eventfd with a sync obj")]
|
||||
RegisterEventfd(#[source] OsError),
|
||||
#[error("Could not create an eventfd")]
|
||||
EventFd(#[source] OsError),
|
||||
#[error("Could not read from an eventfd")]
|
||||
ReadEventFd(#[source] IoUringError),
|
||||
#[error("No sync obj context available")]
|
||||
NoSyncObjContextAvailable,
|
||||
#[error("Could not signal the sync obj")]
|
||||
SignalSyncObj(#[source] OsError),
|
||||
#[error("Could not transfer a sync obj point")]
|
||||
TransferPoint(#[source] OsError),
|
||||
#[error("Could not merge two sync files")]
|
||||
Merge(#[source] OsError),
|
||||
#[error("Could not import a sync file into a sync obj")]
|
||||
ImportSyncFile(#[source] OsError),
|
||||
}
|
||||
|
||||
fn render_node_name(fd: c::c_int) -> Result<Ustring, DrmError> {
|
||||
|
|
|
|||
238
src/video/drm/sync_obj.rs
Normal file
238
src/video/drm/sync_obj.rs
Normal file
|
|
@ -0,0 +1,238 @@
|
|||
use {
|
||||
crate::{
|
||||
gfx_api::SyncFile,
|
||||
utils::{
|
||||
clonecell::{CloneCell, UnsafeCellCloneSafe},
|
||||
copyhashmap::CopyHashMap,
|
||||
errorfmt::ErrorFmt,
|
||||
linkedlist::{LinkedList, LinkedNode},
|
||||
oserror::OsError,
|
||||
},
|
||||
video::drm::{
|
||||
sys::{
|
||||
sync_ioc_merge, sync_obj_create, sync_obj_destroy, sync_obj_eventfd,
|
||||
sync_obj_fd_to_handle, sync_obj_handle_to_fd, sync_obj_signal, sync_obj_transfer,
|
||||
DRM_SYNCOBJ_FD_TO_HANDLE_FLAGS_IMPORT_SYNC_FILE,
|
||||
DRM_SYNCOBJ_WAIT_FLAGS_WAIT_AVAILABLE,
|
||||
},
|
||||
DrmError,
|
||||
},
|
||||
},
|
||||
std::{
|
||||
rc::Rc,
|
||||
sync::atomic::{AtomicU64, Ordering::Relaxed},
|
||||
},
|
||||
uapi::{c, OwnedFd},
|
||||
};
|
||||
|
||||
static SYNCOBJ_ID: AtomicU64 = AtomicU64::new(0);
|
||||
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
|
||||
pub struct SyncObjId(u64);
|
||||
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
|
||||
struct SyncObjHandle(u32);
|
||||
|
||||
unsafe impl UnsafeCellCloneSafe for SyncObjHandle {}
|
||||
|
||||
pub struct SyncObj {
|
||||
id: SyncObjId,
|
||||
fd: Rc<OwnedFd>,
|
||||
importers: LinkedList<Rc<Handles>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Ord, PartialOrd)]
|
||||
pub struct SyncObjPoint(pub u64);
|
||||
|
||||
impl SyncObj {
|
||||
pub fn new(fd: &Rc<OwnedFd>) -> Self {
|
||||
Self {
|
||||
id: SyncObjId(SYNCOBJ_ID.fetch_add(1, Relaxed)),
|
||||
fd: fd.clone(),
|
||||
importers: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for SyncObj {
|
||||
fn drop(&mut self) {
|
||||
let mut links = vec![];
|
||||
for importer in self.importers.iter() {
|
||||
if let Some(handle) = importer.handles.remove(&self.id) {
|
||||
destroy(&importer.drm, handle);
|
||||
}
|
||||
if let Some(link) = importer.links.remove(&self.id) {
|
||||
links.push(link);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct Handles {
|
||||
drm: Rc<OwnedFd>,
|
||||
handles: CopyHashMap<SyncObjId, SyncObjHandle>,
|
||||
links: CopyHashMap<SyncObjId, LinkedNode<Rc<Handles>>>,
|
||||
}
|
||||
|
||||
pub struct SyncObjCtx {
|
||||
inner: Rc<Handles>,
|
||||
dummy: CloneCell<Option<Rc<SyncObj>>>,
|
||||
}
|
||||
|
||||
impl SyncObjCtx {
|
||||
pub fn new(drm: &Rc<OwnedFd>) -> Self {
|
||||
Self {
|
||||
inner: Rc::new(Handles {
|
||||
drm: drm.clone(),
|
||||
handles: Default::default(),
|
||||
links: Default::default(),
|
||||
}),
|
||||
dummy: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
fn get_handle(&self, sync_obj: &SyncObj) -> Result<SyncObjHandle, DrmError> {
|
||||
if let Some(handle) = self.inner.handles.get(&sync_obj.id) {
|
||||
return Ok(handle);
|
||||
}
|
||||
let handle = sync_obj_fd_to_handle(self.inner.drm.raw(), sync_obj.fd.raw(), 0, 0)
|
||||
.map_err(DrmError::ImportSyncObj)?;
|
||||
let handle = SyncObjHandle(handle);
|
||||
let link = sync_obj.importers.add_last(self.inner.clone());
|
||||
self.inner.handles.set(sync_obj.id, handle);
|
||||
self.inner.links.set(sync_obj.id, link);
|
||||
Ok(handle)
|
||||
}
|
||||
|
||||
pub fn create_sync_obj(&self) -> Result<SyncObj, DrmError> {
|
||||
let handle = sync_obj_create(self.inner.drm.raw(), 0).map_err(DrmError::CreateSyncObj)?;
|
||||
let handle = SyncObjHandle(handle);
|
||||
let fd = sync_obj_handle_to_fd(self.inner.drm.raw(), handle.0, 0);
|
||||
if fd.is_err() {
|
||||
destroy(&self.inner.drm, handle);
|
||||
}
|
||||
let fd = fd.map_err(DrmError::ExportSyncObj).map(Rc::new)?;
|
||||
let sync_obj = SyncObj::new(&fd);
|
||||
let link = sync_obj.importers.add_last(self.inner.clone());
|
||||
self.inner.handles.set(sync_obj.id, handle);
|
||||
self.inner.links.set(sync_obj.id, link);
|
||||
Ok(sync_obj)
|
||||
}
|
||||
|
||||
pub fn wait_for_point(
|
||||
&self,
|
||||
eventfd: &OwnedFd,
|
||||
sync_obj: &SyncObj,
|
||||
point: SyncObjPoint,
|
||||
signaled: bool,
|
||||
) -> Result<(), DrmError> {
|
||||
let handle = self.get_handle(sync_obj)?;
|
||||
let flags = match signaled {
|
||||
true => 0,
|
||||
false => DRM_SYNCOBJ_WAIT_FLAGS_WAIT_AVAILABLE,
|
||||
};
|
||||
sync_obj_eventfd(
|
||||
self.inner.drm.raw(),
|
||||
eventfd.raw(),
|
||||
handle.0,
|
||||
point.0,
|
||||
flags,
|
||||
)
|
||||
.map_err(DrmError::RegisterEventfd)
|
||||
}
|
||||
|
||||
pub fn supports_async_wait(&self) -> bool {
|
||||
self.supports_async_wait_().is_ok()
|
||||
}
|
||||
|
||||
fn supports_async_wait_(&self) -> Result<(), DrmError> {
|
||||
let sync_obj = self.create_sync_obj()?;
|
||||
let eventfd = uapi::eventfd(0, c::EFD_CLOEXEC)
|
||||
.map_err(OsError::from)
|
||||
.map_err(DrmError::EventFd)?;
|
||||
self.wait_for_point(&eventfd, &sync_obj, SyncObjPoint(1), true)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn signal(&self, sync_obj: &SyncObj, point: SyncObjPoint) -> Result<(), DrmError> {
|
||||
let handle = self.get_handle(sync_obj)?;
|
||||
sync_obj_signal(self.inner.drm.raw(), handle.0, point.0).map_err(DrmError::SignalSyncObj)
|
||||
}
|
||||
|
||||
pub fn import_sync_files<'a, I>(
|
||||
&self,
|
||||
sync_obj: &SyncObj,
|
||||
point: SyncObjPoint,
|
||||
sync_files: I,
|
||||
) -> Result<(), DrmError>
|
||||
where
|
||||
I: IntoIterator<Item = &'a SyncFile>,
|
||||
{
|
||||
let mut sync_files = sync_files.into_iter();
|
||||
let Some(first) = sync_files.next() else {
|
||||
return self.signal(sync_obj, point);
|
||||
};
|
||||
let mut stash;
|
||||
let mut fd = &*first.0;
|
||||
for next in sync_files {
|
||||
stash = sync_ioc_merge(fd.raw(), next.raw()).map_err(DrmError::Merge)?;
|
||||
fd = &stash;
|
||||
}
|
||||
let dummy = self.get_dummy()?;
|
||||
sync_obj_fd_to_handle(
|
||||
self.inner.drm.raw(),
|
||||
fd.raw(),
|
||||
DRM_SYNCOBJ_FD_TO_HANDLE_FLAGS_IMPORT_SYNC_FILE,
|
||||
self.get_handle(&dummy)?.0,
|
||||
)
|
||||
.map_err(DrmError::ImportSyncFile)?;
|
||||
self.transfer(&dummy, SyncObjPoint(0), sync_obj, point)
|
||||
}
|
||||
|
||||
fn transfer(
|
||||
&self,
|
||||
src_sync_obj: &SyncObj,
|
||||
src_point: SyncObjPoint,
|
||||
dst_sync_obj: &SyncObj,
|
||||
dst_point: SyncObjPoint,
|
||||
) -> Result<(), DrmError> {
|
||||
let src_handle = self.get_handle(src_sync_obj)?;
|
||||
let dst_handle = self.get_handle(dst_sync_obj)?;
|
||||
sync_obj_transfer(
|
||||
self.inner.drm.raw(),
|
||||
src_handle.0,
|
||||
src_point.0,
|
||||
dst_handle.0,
|
||||
dst_point.0,
|
||||
0,
|
||||
)
|
||||
.map_err(DrmError::TransferPoint)
|
||||
}
|
||||
|
||||
fn get_dummy(&self) -> Result<Rc<SyncObj>, DrmError> {
|
||||
match self.dummy.get() {
|
||||
Some(d) => Ok(d),
|
||||
None => {
|
||||
let d = Rc::new(self.create_sync_obj()?);
|
||||
self.dummy.set(Some(d.clone()));
|
||||
Ok(d)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for SyncObjCtx {
|
||||
fn drop(&mut self) {
|
||||
self.inner.links.clear();
|
||||
let mut map = self.inner.handles.lock();
|
||||
for (_, handle) in map.drain() {
|
||||
destroy(&self.inner.drm, handle);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn destroy(drm: &OwnedFd, handle: SyncObjHandle) {
|
||||
if let Err(e) = sync_obj_destroy(drm.raw(), handle.0) {
|
||||
log::error!("Could not destroy sync obj: {}", ErrorFmt(e));
|
||||
}
|
||||
}
|
||||
|
|
@ -1156,3 +1156,201 @@ pub struct drm_format_modifier {
|
|||
}
|
||||
|
||||
unsafe impl Pod for drm_format_modifier {}
|
||||
|
||||
// pub const DRM_SYNCOBJ_CREATE_SIGNALED: u32 = 1 << 0;
|
||||
|
||||
#[repr(C)]
|
||||
struct drm_syncobj_create {
|
||||
handle: u32,
|
||||
flags: u32,
|
||||
}
|
||||
|
||||
const DRM_IOCTL_SYNCOBJ_CREATE: u64 = drm_iowr::<drm_syncobj_create>(0xBF);
|
||||
|
||||
pub fn sync_obj_create(drm: c::c_int, flags: u32) -> Result<u32, OsError> {
|
||||
let mut res = drm_syncobj_create { handle: 0, flags };
|
||||
unsafe {
|
||||
ioctl(drm, DRM_IOCTL_SYNCOBJ_CREATE, &mut res)?;
|
||||
}
|
||||
Ok(res.handle)
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
struct drm_syncobj_destroy {
|
||||
handle: u32,
|
||||
pad: u32,
|
||||
}
|
||||
|
||||
const DRM_IOCTL_SYNCOBJ_DESTROY: u64 = drm_iowr::<drm_syncobj_destroy>(0xC0);
|
||||
|
||||
pub fn sync_obj_destroy(drm: c::c_int, handle: u32) -> Result<(), OsError> {
|
||||
let mut res = drm_syncobj_destroy { handle, pad: 0 };
|
||||
unsafe {
|
||||
ioctl(drm, DRM_IOCTL_SYNCOBJ_DESTROY, &mut res)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub const DRM_SYNCOBJ_FD_TO_HANDLE_FLAGS_IMPORT_SYNC_FILE: u32 = 1 << 0;
|
||||
// pub const DRM_SYNCOBJ_HANDLE_TO_FD_FLAGS_EXPORT_SYNC_FILE: u32 = 1 << 0;
|
||||
|
||||
#[repr(C)]
|
||||
struct drm_syncobj_handle {
|
||||
handle: u32,
|
||||
flags: u32,
|
||||
fd: i32,
|
||||
pad: u32,
|
||||
}
|
||||
|
||||
const DRM_IOCTL_SYNCOBJ_HANDLE_TO_FD: u64 = drm_iowr::<drm_syncobj_handle>(0xC1);
|
||||
const DRM_IOCTL_SYNCOBJ_FD_TO_HANDLE: u64 = drm_iowr::<drm_syncobj_handle>(0xC2);
|
||||
|
||||
pub fn sync_obj_handle_to_fd(drm: c::c_int, handle: u32, flags: u32) -> Result<OwnedFd, OsError> {
|
||||
let mut res = drm_syncobj_handle {
|
||||
handle,
|
||||
flags,
|
||||
fd: 0,
|
||||
pad: 0,
|
||||
};
|
||||
unsafe {
|
||||
ioctl(drm, DRM_IOCTL_SYNCOBJ_HANDLE_TO_FD, &mut res)?;
|
||||
}
|
||||
Ok(OwnedFd::new(res.fd))
|
||||
}
|
||||
|
||||
pub fn sync_obj_fd_to_handle(
|
||||
drm: c::c_int,
|
||||
fd: c::c_int,
|
||||
flags: u32,
|
||||
handle: u32,
|
||||
) -> Result<u32, OsError> {
|
||||
let mut res = drm_syncobj_handle {
|
||||
handle,
|
||||
flags,
|
||||
fd,
|
||||
pad: 0,
|
||||
};
|
||||
unsafe {
|
||||
ioctl(drm, DRM_IOCTL_SYNCOBJ_FD_TO_HANDLE, &mut res)?;
|
||||
}
|
||||
Ok(res.handle)
|
||||
}
|
||||
|
||||
// pub const DRM_SYNCOBJ_WAIT_FLAGS_WAIT_ALL: u32 = 1 << 0;
|
||||
// pub const DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT: u32 = 1 << 1;
|
||||
pub const DRM_SYNCOBJ_WAIT_FLAGS_WAIT_AVAILABLE: u32 = 1 << 2;
|
||||
|
||||
#[repr(C)]
|
||||
struct drm_syncobj_eventfd {
|
||||
handle: u32,
|
||||
flags: u32,
|
||||
point: u64,
|
||||
fd: i32,
|
||||
pad: u32,
|
||||
}
|
||||
|
||||
const DRM_IOCTL_SYNCOBJ_EVENTFD: u64 = drm_iowr::<drm_syncobj_eventfd>(0xCF);
|
||||
|
||||
pub fn sync_obj_eventfd(
|
||||
drm: c::c_int,
|
||||
eventfd: c::c_int,
|
||||
handle: u32,
|
||||
point: u64,
|
||||
flags: u32,
|
||||
) -> Result<(), OsError> {
|
||||
let mut res = drm_syncobj_eventfd {
|
||||
handle,
|
||||
flags,
|
||||
point,
|
||||
fd: eventfd,
|
||||
pad: 0,
|
||||
};
|
||||
unsafe {
|
||||
ioctl(drm, DRM_IOCTL_SYNCOBJ_EVENTFD, &mut res)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
struct drm_syncobj_timeline_array {
|
||||
handles: u64,
|
||||
points: u64,
|
||||
count_handles: u32,
|
||||
flags: u32,
|
||||
}
|
||||
|
||||
const DRM_IOCTL_SYNCOBJ_TIMELINE_SIGNAL: u64 = drm_iowr::<drm_syncobj_timeline_array>(0xCD);
|
||||
|
||||
pub fn sync_obj_signal(drm: c::c_int, handle: u32, point: u64) -> Result<(), OsError> {
|
||||
let mut res = drm_syncobj_timeline_array {
|
||||
handles: &handle as *const u32 as u64,
|
||||
points: &point as *const u64 as u64,
|
||||
count_handles: 1,
|
||||
flags: 0,
|
||||
};
|
||||
unsafe {
|
||||
ioctl(drm, DRM_IOCTL_SYNCOBJ_TIMELINE_SIGNAL, &mut res)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
struct drm_syncobj_transfer {
|
||||
src_handle: u32,
|
||||
dst_handle: u32,
|
||||
src_point: u64,
|
||||
dst_point: u64,
|
||||
flags: u32,
|
||||
pad: u32,
|
||||
}
|
||||
|
||||
const DRM_IOCTL_SYNCOBJ_TRANSFER: u64 = drm_iowr::<drm_syncobj_transfer>(0xCC);
|
||||
|
||||
pub fn sync_obj_transfer(
|
||||
drm: c::c_int,
|
||||
src_handle: u32,
|
||||
src_point: u64,
|
||||
dst_handle: u32,
|
||||
dst_point: u64,
|
||||
flags: u32,
|
||||
) -> Result<(), OsError> {
|
||||
let mut res = drm_syncobj_transfer {
|
||||
src_handle,
|
||||
dst_handle,
|
||||
src_point,
|
||||
dst_point,
|
||||
flags,
|
||||
pad: 0,
|
||||
};
|
||||
unsafe {
|
||||
ioctl(drm, DRM_IOCTL_SYNCOBJ_TRANSFER, &mut res)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
struct sync_merge_data {
|
||||
name: [u8; 32],
|
||||
fd2: i32,
|
||||
fence: i32,
|
||||
flags: u32,
|
||||
pad: u32,
|
||||
}
|
||||
|
||||
const SYNC_IOC_MAGIC: u64 = b'>' as _;
|
||||
|
||||
const SYNC_IOC_MERGE: u64 = uapi::_IOWR::<sync_merge_data>(SYNC_IOC_MAGIC, 3);
|
||||
|
||||
pub fn sync_ioc_merge(left: c::c_int, right: c::c_int) -> Result<OwnedFd, OsError> {
|
||||
let mut res = sync_merge_data {
|
||||
name: [0; 32],
|
||||
fd2: right,
|
||||
fence: 0,
|
||||
flags: 0,
|
||||
pad: 0,
|
||||
};
|
||||
unsafe {
|
||||
ioctl(left, SYNC_IOC_MERGE, &mut res)?;
|
||||
}
|
||||
Ok(OwnedFd::new(res.fence))
|
||||
}
|
||||
|
|
|
|||
199
src/video/drm/wait_for_sync_obj.rs
Normal file
199
src/video/drm/wait_for_sync_obj.rs
Normal file
|
|
@ -0,0 +1,199 @@
|
|||
use {
|
||||
crate::{
|
||||
async_engine::{AsyncEngine, SpawnedFuture},
|
||||
io_uring::IoUring,
|
||||
utils::{
|
||||
asyncevent::AsyncEvent, buf::Buf, clonecell::CloneCell, copyhashmap::CopyHashMap,
|
||||
numcell::NumCell, oserror::OsError, stack::Stack,
|
||||
},
|
||||
video::drm::{
|
||||
sync_obj::{SyncObj, SyncObjCtx, SyncObjPoint},
|
||||
DrmError,
|
||||
},
|
||||
},
|
||||
std::{cell::Cell, rc::Rc},
|
||||
uapi::{c, OwnedFd},
|
||||
};
|
||||
|
||||
pub struct WaitForSyncObj {
|
||||
inner: Rc<Inner>,
|
||||
eng: Rc<AsyncEngine>,
|
||||
}
|
||||
|
||||
pub trait SyncObjWaiter {
|
||||
fn done(self: Rc<Self>, result: Result<(), DrmError>);
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
|
||||
struct JobId(u64);
|
||||
|
||||
pub struct WaitForSyncObjHandle {
|
||||
inner: Rc<Inner>,
|
||||
id: JobId,
|
||||
}
|
||||
|
||||
struct Inner {
|
||||
ctx: CloneCell<Option<Rc<SyncObjCtx>>>,
|
||||
next_id: NumCell<u64>,
|
||||
ring: Rc<IoUring>,
|
||||
busy: CopyHashMap<JobId, BusyWaiter>,
|
||||
idle: Stack<Waiter>,
|
||||
}
|
||||
|
||||
struct BusyWaiter {
|
||||
waiter: Waiter,
|
||||
job: Job,
|
||||
sow: Rc<dyn SyncObjWaiter>,
|
||||
}
|
||||
|
||||
struct Waiter {
|
||||
_task: SpawnedFuture<()>,
|
||||
inner: Rc<WaiterInner>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
struct Job {
|
||||
id: JobId,
|
||||
sync_obj: Rc<SyncObj>,
|
||||
point: SyncObjPoint,
|
||||
signaled: bool,
|
||||
}
|
||||
|
||||
struct WaiterInner {
|
||||
inner: Rc<Inner>,
|
||||
eventfd: Rc<OwnedFd>,
|
||||
next: Cell<Option<Job>>,
|
||||
trigger: AsyncEvent,
|
||||
}
|
||||
|
||||
impl Drop for WaitForSyncObjHandle {
|
||||
fn drop(&mut self) {
|
||||
let _ = self.inner.busy.remove(&self.id);
|
||||
}
|
||||
}
|
||||
|
||||
impl WaitForSyncObj {
|
||||
pub fn new(ring: &Rc<IoUring>, eng: &Rc<AsyncEngine>) -> Self {
|
||||
Self {
|
||||
inner: Rc::new(Inner {
|
||||
ctx: Default::default(),
|
||||
next_id: Default::default(),
|
||||
ring: ring.clone(),
|
||||
busy: Default::default(),
|
||||
idle: Default::default(),
|
||||
}),
|
||||
eng: eng.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_ctx(&self, ctx: Option<Rc<SyncObjCtx>>) {
|
||||
self.inner.ctx.set(ctx);
|
||||
let busy_waiters: Vec<_> = self.inner.busy.lock().drain().map(|(_, w)| w).collect();
|
||||
for waiter in busy_waiters {
|
||||
let res = self.submit_job(
|
||||
waiter.job.id,
|
||||
&waiter.job.sync_obj,
|
||||
waiter.job.point,
|
||||
waiter.job.signaled,
|
||||
waiter.sow.clone(),
|
||||
);
|
||||
if res.is_err() {
|
||||
waiter.sow.done(res);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn wait(
|
||||
&self,
|
||||
sync_obj: &Rc<SyncObj>,
|
||||
point: SyncObjPoint,
|
||||
signaled: bool,
|
||||
sow: Rc<dyn SyncObjWaiter>,
|
||||
) -> Result<WaitForSyncObjHandle, DrmError> {
|
||||
let job_id = JobId(self.inner.next_id.fetch_add(1));
|
||||
self.submit_job(job_id, sync_obj, point, signaled, sow)?;
|
||||
Ok(WaitForSyncObjHandle {
|
||||
inner: self.inner.clone(),
|
||||
id: job_id,
|
||||
})
|
||||
}
|
||||
|
||||
fn submit_job(
|
||||
&self,
|
||||
job_id: JobId,
|
||||
sync_obj: &Rc<SyncObj>,
|
||||
point: SyncObjPoint,
|
||||
signaled: bool,
|
||||
sow: Rc<dyn SyncObjWaiter>,
|
||||
) -> Result<(), DrmError> {
|
||||
let waiter = match self.inner.idle.pop() {
|
||||
Some(w) => w,
|
||||
None => {
|
||||
let eventfd = uapi::eventfd(0, c::EFD_CLOEXEC)
|
||||
.map_err(OsError::from)
|
||||
.map_err(DrmError::EventFd)?;
|
||||
let waiter = Rc::new(WaiterInner {
|
||||
inner: self.inner.clone(),
|
||||
eventfd: Rc::new(eventfd),
|
||||
next: Cell::new(None),
|
||||
trigger: Default::default(),
|
||||
});
|
||||
Waiter {
|
||||
_task: self.eng.spawn(waiter.clone().run()),
|
||||
inner: waiter,
|
||||
}
|
||||
}
|
||||
};
|
||||
let job = Job {
|
||||
id: job_id,
|
||||
sync_obj: sync_obj.clone(),
|
||||
point,
|
||||
signaled,
|
||||
};
|
||||
let waiter = BusyWaiter {
|
||||
waiter,
|
||||
job: job.clone(),
|
||||
sow: sow.clone(),
|
||||
};
|
||||
waiter.waiter.inner.next.set(Some(job));
|
||||
waiter.waiter.inner.trigger.trigger();
|
||||
self.inner.busy.set(job_id, waiter);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for WaitForSyncObj {
|
||||
fn drop(&mut self) {
|
||||
self.inner.busy.clear();
|
||||
self.inner.idle.take();
|
||||
}
|
||||
}
|
||||
|
||||
impl WaiterInner {
|
||||
async fn run(self: Rc<Self>) {
|
||||
let mut buf = Buf::new(8);
|
||||
loop {
|
||||
self.trigger.triggered().await;
|
||||
let job = self.next.take().unwrap();
|
||||
let res = self.wait(&mut buf, &job).await;
|
||||
if let Some(waiter) = self.inner.busy.remove(&job.id) {
|
||||
waiter.sow.done(res);
|
||||
self.inner.idle.push(waiter.waiter);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn wait(&self, buf: &mut Buf, job: &Job) -> Result<(), DrmError> {
|
||||
let ctx = match self.inner.ctx.get() {
|
||||
None => return Err(DrmError::NoSyncObjContextAvailable),
|
||||
Some(c) => c,
|
||||
};
|
||||
ctx.wait_for_point(&self.eventfd, &job.sync_obj, job.point, job.signaled)?;
|
||||
self.inner
|
||||
.ring
|
||||
.read(&self.eventfd, buf.clone())
|
||||
.await
|
||||
.map(drop)
|
||||
.map_err(DrmError::ReadEventFd)
|
||||
}
|
||||
}
|
||||
|
|
@ -18,7 +18,7 @@ use {
|
|||
io_uring::IoUringError,
|
||||
state::State,
|
||||
user_session::import_environment,
|
||||
utils::{buf::Buf, errorfmt::ErrorFmt, oserror::OsError},
|
||||
utils::{errorfmt::ErrorFmt, line_logger::log_lines, oserror::OsError},
|
||||
wire::WlSurfaceId,
|
||||
xcon::XconError,
|
||||
xwayland::{
|
||||
|
|
@ -217,29 +217,12 @@ pub fn build_args() -> (String, Vec<String>) {
|
|||
|
||||
async fn log_xwayland(state: Rc<State>, stderr: OwnedFd) {
|
||||
let stderr = Rc::new(stderr);
|
||||
let mut buf = vec![];
|
||||
let mut buf2 = Buf::new(128);
|
||||
let mut done = false;
|
||||
while !done {
|
||||
loop {
|
||||
match state.ring.read(&stderr, buf2.clone()).await {
|
||||
Ok(n) if n > 0 => {
|
||||
buf.extend_from_slice(&buf2[..n]);
|
||||
}
|
||||
Ok(_) => {
|
||||
done = true;
|
||||
break;
|
||||
}
|
||||
Err(e) => {
|
||||
log::error!("Could not read from stderr fd: {}", ErrorFmt(e));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
for line in buf.lines() {
|
||||
log::info!("Xwayland: {}", line.as_bstr());
|
||||
}
|
||||
buf.clear();
|
||||
let res = log_lines(&state.ring, &stderr, |left, right| {
|
||||
log::info!("Xwayland: {}{}", left.as_bstr(), right.as_bstr());
|
||||
})
|
||||
.await;
|
||||
if let Err(e) = res {
|
||||
log::error!("Could not read from stderr fd: {}", ErrorFmt(e));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -289,6 +289,7 @@ pub struct Config {
|
|||
pub render_device: Option<DrmDeviceMatch>,
|
||||
pub inputs: Vec<Input>,
|
||||
pub idle: Option<Duration>,
|
||||
pub explicit_sync_enabled: Option<bool>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
|
|
|
|||
|
|
@ -95,6 +95,7 @@ impl Parser for ConfigParser<'_> {
|
|||
_,
|
||||
idle_val,
|
||||
),
|
||||
(explicit_sync,),
|
||||
) = ext.extract((
|
||||
(
|
||||
opt(val("keymap")),
|
||||
|
|
@ -120,6 +121,7 @@ impl Parser for ConfigParser<'_> {
|
|||
opt(val("$schema")),
|
||||
opt(val("idle")),
|
||||
),
|
||||
(recover(opt(bol("explicit-sync"))),),
|
||||
))?;
|
||||
let mut keymap = None;
|
||||
if let Some(value) = keymap_val {
|
||||
|
|
@ -271,6 +273,7 @@ impl Parser for ConfigParser<'_> {
|
|||
gfx_api,
|
||||
drm_devices,
|
||||
direct_scanout_enabled: direct_scanout.despan(),
|
||||
explicit_sync_enabled: explicit_sync.despan(),
|
||||
render_device,
|
||||
inputs,
|
||||
idle,
|
||||
|
|
|
|||
|
|
@ -19,7 +19,8 @@ use {
|
|||
is_reload,
|
||||
keyboard::{Keymap, ModifiedKeySym},
|
||||
logging::set_log_level,
|
||||
on_devices_enumerated, on_idle, quit, reload, set_default_workspace_capture, set_idle,
|
||||
on_devices_enumerated, on_idle, quit, reload, set_default_workspace_capture,
|
||||
set_explicit_sync_enabled, set_idle,
|
||||
status::{set_i3bar_separator, set_status, set_status_command, unset_status_command},
|
||||
switch_to_vt,
|
||||
theme::{reset_colors, reset_font, reset_sizes, set_font},
|
||||
|
|
@ -789,6 +790,9 @@ fn load_config(initial_load: bool, persistent: &Rc<PersistentState>) {
|
|||
if let Some(dse) = config.direct_scanout_enabled {
|
||||
set_direct_scanout_enabled(dse);
|
||||
}
|
||||
if let Some(ese) = config.explicit_sync_enabled {
|
||||
set_explicit_sync_enabled(ese);
|
||||
}
|
||||
on_new_drm_device({
|
||||
let state = state.clone();
|
||||
move |d| {
|
||||
|
|
|
|||
|
|
@ -96,9 +96,7 @@ impl<'a> Lexer<'a> {
|
|||
|
||||
self.skip_ws();
|
||||
|
||||
let Some(c) = get!(0) else {
|
||||
return None;
|
||||
};
|
||||
let c = get!(0)?;
|
||||
let pos = self.pos;
|
||||
|
||||
macro_rules! span {
|
||||
|
|
|
|||
|
|
@ -498,6 +498,10 @@
|
|||
"type": "boolean",
|
||||
"description": "Configured whether the compositor attempts direct scanout.\n"
|
||||
},
|
||||
"explicit-sync": {
|
||||
"type": "boolean",
|
||||
"description": "Configures whether the compositor supports explicit sync.\n\nThis cannot be changed after the compositor has started.\n\nThe default is `true`.\n"
|
||||
},
|
||||
"render-device": {
|
||||
"description": "Selects the device to use for rendering in a system with multiple GPUs.\n\nThe first device that matches will be used.\n\n- Example:\n\n ```toml\n render-device.name = \"dedicated\"\n\n [[drm-devices]]\n name = \"dedicated\"\n match = { pci-vendor = 0x1002, pci-model = 0x73ff }\n ```\n",
|
||||
"$ref": "#/$defs/DrmDeviceMatch"
|
||||
|
|
|
|||
|
|
@ -887,6 +887,16 @@ The table has the following fields:
|
|||
|
||||
The value of this field should be a boolean.
|
||||
|
||||
- `explicit-sync` (optional):
|
||||
|
||||
Configures whether the compositor supports explicit sync.
|
||||
|
||||
This cannot be changed after the compositor has started.
|
||||
|
||||
The default is `true`.
|
||||
|
||||
The value of this field should be a boolean.
|
||||
|
||||
- `render-device` (optional):
|
||||
|
||||
Selects the device to use for rendering in a system with multiple GPUs.
|
||||
|
|
|
|||
|
|
@ -1875,6 +1875,15 @@ Config:
|
|||
required: false
|
||||
description: |
|
||||
Configured whether the compositor attempts direct scanout.
|
||||
explicit-sync:
|
||||
kind: boolean
|
||||
required: false
|
||||
description: |
|
||||
Configures whether the compositor supports explicit sync.
|
||||
|
||||
This cannot be changed after the compositor has started.
|
||||
|
||||
The default is `true`.
|
||||
render-device:
|
||||
ref: DrmDeviceMatch
|
||||
required: false
|
||||
|
|
|
|||
15
wire/wp_linux_drm_syncobj_manager_v1.txt
Normal file
15
wire/wp_linux_drm_syncobj_manager_v1.txt
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
# requests
|
||||
|
||||
msg destroy = 0 {
|
||||
|
||||
}
|
||||
|
||||
msg get_surface = 1 {
|
||||
id: id(wp_linux_drm_syncobj_surface_v1),
|
||||
surface: id(wl_surface),
|
||||
}
|
||||
|
||||
msg import_timeline = 2 {
|
||||
id: id(wp_linux_drm_syncobj_timeline_v1),
|
||||
fd: fd,
|
||||
}
|
||||
17
wire/wp_linux_drm_syncobj_surface_v1.txt
Normal file
17
wire/wp_linux_drm_syncobj_surface_v1.txt
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
# requests
|
||||
|
||||
msg destroy = 0 {
|
||||
|
||||
}
|
||||
|
||||
msg set_acquire_point = 1 {
|
||||
timeline: id(wp_linux_drm_syncobj_timeline_v1),
|
||||
point_hi: u32,
|
||||
point_lo: u32,
|
||||
}
|
||||
|
||||
msg set_release_point = 2 {
|
||||
timeline: id(wp_linux_drm_syncobj_timeline_v1),
|
||||
point_hi: u32,
|
||||
point_lo: u32,
|
||||
}
|
||||
5
wire/wp_linux_drm_syncobj_timeline_v1.txt
Normal file
5
wire/wp_linux_drm_syncobj_timeline_v1.txt
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
# requests
|
||||
|
||||
msg destroy = 0 {
|
||||
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue