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
|
- Selecting the primary device in multi-GPU systems
|
||||||
- An OpenGL backend
|
- An OpenGL backend
|
||||||
- A Vulkan backend
|
- A Vulkan backend
|
||||||
|
- Explicit sync
|
||||||
|
|
||||||
### Missing Features
|
### 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")][..],
|
&[("target", "GLenum"), ("image", "GLeglImageOES")][..],
|
||||||
),
|
),
|
||||||
("glGetGraphicsResetStatusKHR", "GLenum", &[][..]),
|
("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;")?;
|
writeln!(f, "use std::ptr;")?;
|
||||||
|
|
|
||||||
|
|
@ -813,7 +813,7 @@ struct Extension {
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
enum NamedType {
|
enum NamedType {
|
||||||
Struct(Rc<Struct>),
|
Struct(Rc<Struct>),
|
||||||
Bitmask(Rc<Bitmask>),
|
Bitmask(#[allow(dead_code)] Rc<Bitmask>),
|
||||||
Enum(Rc<Enum>),
|
Enum(Rc<Enum>),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -810,6 +810,10 @@ impl Client {
|
||||||
self.send(&ClientMessage::SetIdle { timeout })
|
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) {
|
pub fn set_seat(&self, device: InputDevice, seat: Seat) {
|
||||||
self.send(&ClientMessage::SetSeat { device, seat })
|
self.send(&ClientMessage::SetSeat { device, seat })
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -428,6 +428,9 @@ pub enum ClientMessage<'a> {
|
||||||
workspace: WorkspaceSource,
|
workspace: WorkspaceSource,
|
||||||
connector: Connector,
|
connector: Connector,
|
||||||
},
|
},
|
||||||
|
SetExplicitSyncEnabled {
|
||||||
|
enabled: bool,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
|
|
|
||||||
|
|
@ -222,3 +222,12 @@ pub fn workspaces() -> Vec<Workspace> {
|
||||||
pub fn set_idle(timeout: Option<Duration>) {
|
pub fn set_idle(timeout: Option<Duration>) {
|
||||||
get!().set_idle(timeout.unwrap_or_default())
|
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,
|
async_engine::SpawnedFuture,
|
||||||
drm_feedback::DrmFeedback,
|
drm_feedback::DrmFeedback,
|
||||||
fixed::Fixed,
|
fixed::Fixed,
|
||||||
gfx_api::GfxFramebuffer,
|
gfx_api::{GfxFramebuffer, SyncFile},
|
||||||
ifs::wl_seat::wl_pointer::{CONTINUOUS, FINGER, HORIZONTAL_SCROLL, VERTICAL_SCROLL, WHEEL},
|
ifs::wl_seat::wl_pointer::{CONTINUOUS, FINGER, HORIZONTAL_SCROLL, VERTICAL_SCROLL, WHEEL},
|
||||||
libinput::consts::DeviceCapability,
|
libinput::consts::DeviceCapability,
|
||||||
video::drm::{ConnectorType, DrmError, DrmVersion},
|
video::drm::{ConnectorType, DrmError, DrmVersion},
|
||||||
|
|
@ -106,6 +106,7 @@ pub trait HardwareCursor: Debug {
|
||||||
fn get_buffer(&self) -> Rc<dyn GfxFramebuffer>;
|
fn get_buffer(&self) -> Rc<dyn GfxFramebuffer>;
|
||||||
fn set_position(&self, x: i32, y: i32);
|
fn set_position(&self, x: i32, y: i32);
|
||||||
fn swap_buffer(&self);
|
fn swap_buffer(&self);
|
||||||
|
fn set_sync_file(&self, sync_file: Option<SyncFile>);
|
||||||
fn commit(&self);
|
fn commit(&self);
|
||||||
fn size(&self) -> (i32, i32);
|
fn size(&self) -> (i32, i32);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -114,6 +114,14 @@ pub enum MetalError {
|
||||||
MissingDevModifier(&'static str),
|
MissingDevModifier(&'static str),
|
||||||
#[error("Device GFX API cannot read any buffers writable by the render GFX API (format {0})")]
|
#[error("Device GFX API cannot read any buffers writable by the render GFX API (format {0})")]
|
||||||
MissingRenderModifier(&'static str),
|
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 {
|
pub struct MetalBackend {
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,10 @@ use {
|
||||||
drm_feedback::DrmFeedback,
|
drm_feedback::DrmFeedback,
|
||||||
edid::Descriptor,
|
edid::Descriptor,
|
||||||
format::{Format, ARGB8888, XRGB8888},
|
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},
|
ifs::wp_presentation_feedback::{KIND_HW_COMPLETION, KIND_VSYNC},
|
||||||
renderer::RenderResult,
|
renderer::RenderResult,
|
||||||
state::State,
|
state::State,
|
||||||
|
|
@ -227,6 +230,7 @@ pub struct MetalConnector {
|
||||||
pub cursor_buffers: CloneCell<Option<Rc<[RenderBuffer; 3]>>>,
|
pub cursor_buffers: CloneCell<Option<Rc<[RenderBuffer; 3]>>>,
|
||||||
pub cursor_front_buffer: NumCell<usize>,
|
pub cursor_front_buffer: NumCell<usize>,
|
||||||
pub cursor_swap_buffer: Cell<bool>,
|
pub cursor_swap_buffer: Cell<bool>,
|
||||||
|
pub cursor_sync_file: CloneCell<Option<SyncFile>>,
|
||||||
|
|
||||||
pub drm_feedback: CloneCell<Option<Rc<DrmFeedback>>>,
|
pub drm_feedback: CloneCell<Option<Rc<DrmFeedback>>>,
|
||||||
pub scanout_buffers: RefCell<AHashMap<DmaBufId, DirectScanoutCache>>,
|
pub scanout_buffers: RefCell<AHashMap<DmaBufId, DirectScanoutCache>>,
|
||||||
|
|
@ -244,6 +248,7 @@ pub struct MetalHardwareCursor {
|
||||||
pub cursor_x_pending: Cell<i32>,
|
pub cursor_x_pending: Cell<i32>,
|
||||||
pub cursor_y_pending: Cell<i32>,
|
pub cursor_y_pending: Cell<i32>,
|
||||||
pub cursor_buffers: Rc<[RenderBuffer; 3]>,
|
pub cursor_buffers: Rc<[RenderBuffer; 3]>,
|
||||||
|
pub sync_file: CloneCell<Option<SyncFile>>,
|
||||||
pub have_changes: Cell<bool>,
|
pub have_changes: Cell<bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -270,6 +275,11 @@ impl HardwareCursor for MetalHardwareCursor {
|
||||||
self.have_changes.set(true);
|
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) {
|
fn commit(&self) {
|
||||||
if self.generation != self.connector.cursor_generation.get() {
|
if self.generation != self.connector.cursor_generation.get() {
|
||||||
return;
|
return;
|
||||||
|
|
@ -285,6 +295,7 @@ impl HardwareCursor for MetalHardwareCursor {
|
||||||
if self.cursor_swap_buffer.take() {
|
if self.cursor_swap_buffer.take() {
|
||||||
self.connector.cursor_swap_buffer.set(true);
|
self.connector.cursor_swap_buffer.set(true);
|
||||||
}
|
}
|
||||||
|
self.connector.cursor_sync_file.set(self.sync_file.take());
|
||||||
self.connector.cursor_changed.set(true);
|
self.connector.cursor_changed.set(true);
|
||||||
if self.connector.can_present.get() {
|
if self.connector.can_present.get() {
|
||||||
self.connector.schedule_present();
|
self.connector.schedule_present();
|
||||||
|
|
@ -350,9 +361,10 @@ pub struct DirectScanoutCache {
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct DirectScanoutData {
|
pub struct DirectScanoutData {
|
||||||
tex: Rc<dyn GfxTexture>,
|
tex: Rc<dyn GfxTexture>,
|
||||||
|
acquire_sync: AcquireSync,
|
||||||
|
_resv: Option<Rc<dyn BufferResv>>,
|
||||||
fb: Rc<DrmFramebuffer>,
|
fb: Rc<DrmFramebuffer>,
|
||||||
dma_buf_id: DmaBufId,
|
dma_buf_id: DmaBufId,
|
||||||
acquired: Cell<bool>,
|
|
||||||
position: DirectScanoutPosition,
|
position: DirectScanoutPosition,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -366,25 +378,20 @@ pub struct DirectScanoutPosition {
|
||||||
pub crtc_height: i32,
|
pub crtc_height: i32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Drop for DirectScanoutData {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
if self.acquired.replace(false) {
|
|
||||||
self.tex.reservations().release();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct PresentFb {
|
pub struct PresentFb {
|
||||||
fb: Rc<DrmFramebuffer>,
|
fb: Rc<DrmFramebuffer>,
|
||||||
direct_scanout_data: Option<DirectScanoutData>,
|
direct_scanout_data: Option<DirectScanoutData>,
|
||||||
|
sync_file: Option<SyncFile>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MetalConnector {
|
impl MetalConnector {
|
||||||
async fn present_loop(self: Rc<Self>) {
|
async fn present_loop(self: Rc<Self>) {
|
||||||
loop {
|
loop {
|
||||||
self.present_trigger.triggered().await;
|
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_x_pending: Cell::new(self.cursor_x.get()),
|
||||||
cursor_y_pending: Cell::new(self.cursor_y.get()),
|
cursor_y_pending: Cell::new(self.cursor_y.get()),
|
||||||
cursor_buffers: cp.clone(),
|
cursor_buffers: cp.clone(),
|
||||||
|
sync_file: Default::default(),
|
||||||
have_changes: Cell::new(false),
|
have_changes: Cell::new(false),
|
||||||
}) as _),
|
}) as _),
|
||||||
_ => None,
|
_ => None,
|
||||||
|
|
@ -480,6 +488,10 @@ impl MetalConnector {
|
||||||
}
|
}
|
||||||
ct
|
ct
|
||||||
};
|
};
|
||||||
|
if let AcquireSync::None = ct.acquire_sync {
|
||||||
|
// Cannot perform scanout without sync.
|
||||||
|
return None;
|
||||||
|
}
|
||||||
if ct.source.buffer_transform != ct.target.output_transform {
|
if ct.source.buffer_transform != ct.target.output_transform {
|
||||||
// Rotations and mirroring are not supported.
|
// Rotations and mirroring are not supported.
|
||||||
return None;
|
return None;
|
||||||
|
|
@ -532,9 +544,10 @@ impl MetalConnector {
|
||||||
if let Some(buffer) = cache.get(&dmabuf.id) {
|
if let Some(buffer) = cache.get(&dmabuf.id) {
|
||||||
return buffer.fb.as_ref().map(|fb| DirectScanoutData {
|
return buffer.fb.as_ref().map(|fb| DirectScanoutData {
|
||||||
tex: buffer.tex.upgrade().unwrap(),
|
tex: buffer.tex.upgrade().unwrap(),
|
||||||
|
acquire_sync: ct.acquire_sync.clone(),
|
||||||
|
_resv: ct.buffer_resv.clone(),
|
||||||
fb: fb.clone(),
|
fb: fb.clone(),
|
||||||
dma_buf_id: dmabuf.id,
|
dma_buf_id: dmabuf.id,
|
||||||
acquired: Default::default(),
|
|
||||||
position,
|
position,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -556,9 +569,10 @@ impl MetalConnector {
|
||||||
let data = match self.dev.master.add_fb(dmabuf, Some(format.format)) {
|
let data = match self.dev.master.add_fb(dmabuf, Some(format.format)) {
|
||||||
Ok(fb) => Some(DirectScanoutData {
|
Ok(fb) => Some(DirectScanoutData {
|
||||||
tex: ct.tex.clone(),
|
tex: ct.tex.clone(),
|
||||||
|
acquire_sync: ct.acquire_sync.clone(),
|
||||||
|
_resv: ct.buffer_resv.clone(),
|
||||||
fb: Rc::new(fb),
|
fb: Rc::new(fb),
|
||||||
dma_buf_id: dmabuf.id,
|
dma_buf_id: dmabuf.id,
|
||||||
acquired: Default::default(),
|
|
||||||
position,
|
position,
|
||||||
}),
|
}),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
|
|
@ -593,7 +607,7 @@ impl MetalConnector {
|
||||||
plane: &Rc<MetalPlane>,
|
plane: &Rc<MetalPlane>,
|
||||||
output: &OutputNode,
|
output: &OutputNode,
|
||||||
try_direct_scanout: bool,
|
try_direct_scanout: bool,
|
||||||
) -> PresentFb {
|
) -> Result<PresentFb, MetalError> {
|
||||||
self.trim_scanout_cache();
|
self.trim_scanout_cache();
|
||||||
let buffer_fb = buffer.render_fb();
|
let buffer_fb = buffer.render_fb();
|
||||||
let render_hw_cursor = !self.cursor_enabled.get();
|
let render_hw_cursor = !self.cursor_enabled.get();
|
||||||
|
|
@ -636,25 +650,36 @@ impl MetalConnector {
|
||||||
};
|
};
|
||||||
log::debug!("{} direct scanout on {}", change, self.kernel_id());
|
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 => {
|
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);
|
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);
|
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,
|
fb,
|
||||||
direct_scanout_data,
|
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() {
|
let crtc = match self.crtc.get() {
|
||||||
Some(crtc) => crtc,
|
Some(crtc) => crtc,
|
||||||
_ => return Ok(()),
|
_ => return Ok(()),
|
||||||
|
|
@ -684,7 +709,7 @@ impl MetalConnector {
|
||||||
let buffer = &buffers[self.next_buffer.get() % buffers.len()];
|
let buffer = &buffers[self.next_buffer.get() % buffers.len()];
|
||||||
let mut rr = self.render_result.borrow_mut();
|
let mut rr = self.render_result.borrow_mut();
|
||||||
let fb =
|
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();
|
rr.dispatch_frame_requests();
|
||||||
let (crtc_x, crtc_y, crtc_w, crtc_h, src_width, src_height) =
|
let (crtc_x, crtc_y, crtc_w, crtc_h, src_width, src_height) =
|
||||||
match &fb.direct_scanout_data {
|
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| {
|
changes.change_object(plane.id, |c| {
|
||||||
c.change(plane.fb_id, fb.fb.id().0 as _);
|
c.change(plane.fb_id, fb.fb.id().0 as _);
|
||||||
c.change(plane.src_w.id, (src_width as u64) << 16);
|
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_y.id, crtc_y as u64);
|
||||||
c.change(plane.crtc_w.id, crtc_w 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.crtc_h.id, crtc_h as u64);
|
||||||
|
c.change(plane.in_fence_fd, in_fence as u64);
|
||||||
});
|
});
|
||||||
new_fb = Some(fb);
|
new_fb = Some(fb);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
let mut cursor_swap_buffer = false;
|
||||||
|
let mut cursor_sync_file = None;
|
||||||
if self.cursor_changed.get() && cursor.is_some() {
|
if self.cursor_changed.get() && cursor.is_some() {
|
||||||
let plane = cursor.unwrap();
|
let plane = cursor.unwrap();
|
||||||
if self.cursor_enabled.get() {
|
if self.cursor_enabled.get() {
|
||||||
let swap_buffer = self.cursor_swap_buffer.take();
|
cursor_swap_buffer = self.cursor_swap_buffer.get();
|
||||||
if swap_buffer {
|
let mut front_buffer = self.cursor_front_buffer.get();
|
||||||
self.cursor_front_buffer.fetch_add(1);
|
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 buffers = self.cursor_buffers.get().unwrap();
|
||||||
let buffer = &buffers[self.cursor_front_buffer.get() % buffers.len()];
|
let buffer = &buffers[front_buffer % buffers.len()];
|
||||||
if swap_buffer {
|
if cursor_swap_buffer {
|
||||||
if let Some(tex) = &buffer.dev_tex {
|
cursor_sync_file = buffer.copy_to_dev(cursor_sync_file)?;
|
||||||
buffer.dev_fb.copy_texture(tex, 0, 0);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
let in_fence = cursor_sync_file.as_ref().map(|s| s.raw()).unwrap_or(-1);
|
||||||
let (width, height) = buffer.dev_fb.physical_size();
|
let (width, height) = buffer.dev_fb.physical_size();
|
||||||
changes.change_object(plane.id, |c| {
|
changes.change_object(plane.id, |c| {
|
||||||
c.change(plane.fb_id, buffer.drm.id().0 as _);
|
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_y.id, 0);
|
||||||
c.change(plane.src_w.id, (width as u64) << 16);
|
c.change(plane.src_w.id, (width as u64) << 16);
|
||||||
c.change(plane.src_h.id, (height as u64) << 16);
|
c.change(plane.src_h.id, (height as u64) << 16);
|
||||||
|
c.change(plane.in_fence_fd, in_fence as u64);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
changes.change_object(plane.id, |c| {
|
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) {
|
if let Err(e) = changes.commit(DRM_MODE_ATOMIC_NONBLOCK | DRM_MODE_PAGE_FLIP_EVENT, 0) {
|
||||||
match e {
|
if let DrmError::Atomic(OsError(c::EACCES)) = e {
|
||||||
DrmError::Atomic(OsError(c::EACCES)) => {
|
log::debug!("Could not perform atomic commit, likely because we're no longer the DRM master");
|
||||||
log::debug!("Could not perform atomic commit, likely because we're no longer the DRM master");
|
return Ok(());
|
||||||
}
|
}
|
||||||
_ => 'handle_failure: {
|
if let Some(fb) = &new_fb {
|
||||||
if let Some(fb) = &new_fb {
|
if let Some(dsd) = &fb.direct_scanout_data {
|
||||||
if let Some(dsd) = &fb.direct_scanout_data {
|
if self.present(false).is_ok() {
|
||||||
if self.present(false).is_ok() {
|
let mut cache = self.scanout_buffers.borrow_mut();
|
||||||
let mut cache = self.scanout_buffers.borrow_mut();
|
if let Some(buffer) = cache.remove(&dsd.dma_buf_id) {
|
||||||
if let Some(buffer) = cache.remove(&dsd.dma_buf_id) {
|
cache.insert(
|
||||||
cache.insert(
|
dsd.dma_buf_id,
|
||||||
dsd.dma_buf_id,
|
DirectScanoutCache {
|
||||||
DirectScanoutCache {
|
tex: buffer.tex,
|
||||||
tex: buffer.tex,
|
fb: None,
|
||||||
fb: None,
|
},
|
||||||
},
|
);
|
||||||
);
|
|
||||||
}
|
|
||||||
break 'handle_failure;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
return Ok(());
|
||||||
}
|
}
|
||||||
log::error!("Could not set plane framebuffer: {}", ErrorFmt(e));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(())
|
Err(MetalError::Commit(e))
|
||||||
} else {
|
} else {
|
||||||
if let Some(fb) = new_fb {
|
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));
|
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.can_present.set(false);
|
||||||
self.has_damage.set(false);
|
self.has_damage.set(false);
|
||||||
self.cursor_changed.set(false);
|
self.cursor_changed.set(false);
|
||||||
|
|
@ -1036,6 +1064,7 @@ fn create_connector(
|
||||||
cursor_changed: Cell::new(false),
|
cursor_changed: Cell::new(false),
|
||||||
cursor_front_buffer: Default::default(),
|
cursor_front_buffer: Default::default(),
|
||||||
cursor_swap_buffer: Cell::new(false),
|
cursor_swap_buffer: Cell::new(false),
|
||||||
|
cursor_sync_file: Default::default(),
|
||||||
drm_feedback: Default::default(),
|
drm_feedback: Default::default(),
|
||||||
scanout_buffers: Default::default(),
|
scanout_buffers: Default::default(),
|
||||||
active_framebuffer: Default::default(),
|
active_framebuffer: Default::default(),
|
||||||
|
|
@ -2166,7 +2195,7 @@ impl MetalBackend {
|
||||||
Ok(fb) => fb,
|
Ok(fb) => fb,
|
||||||
Err(e) => return Err(MetalError::ImportFb(e)),
|
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 (dev_tex, render_tex, render_fb) = if dev.id == render_ctx.dev_id {
|
||||||
let render_tex = match dev_img.to_texture() {
|
let render_tex = match dev_img.to_texture() {
|
||||||
Ok(fb) => fb,
|
Ok(fb) => fb,
|
||||||
|
|
@ -2215,7 +2244,7 @@ impl MetalBackend {
|
||||||
Ok(fb) => fb,
|
Ok(fb) => fb,
|
||||||
Err(e) => return Err(MetalError::ImportFb(e)),
|
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() {
|
let render_tex = match render_img.to_texture() {
|
||||||
Ok(fb) => fb,
|
Ok(fb) => fb,
|
||||||
Err(e) => return Err(MetalError::ImportTexture(e)),
|
Err(e) => return Err(MetalError::ImportTexture(e)),
|
||||||
|
|
@ -2435,6 +2464,16 @@ impl RenderBuffer {
|
||||||
.clone()
|
.clone()
|
||||||
.unwrap_or_else(|| self.dev_fb.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 {
|
fn modes_equal(a: &DrmModeInfo, b: &DrmModeInfo) -> bool {
|
||||||
|
|
|
||||||
|
|
@ -738,13 +738,17 @@ impl XBackend {
|
||||||
image.last_serial.set(serial);
|
image.last_serial.set(serial);
|
||||||
|
|
||||||
if let Some(node) = self.state.root.outputs.get(&output.id) {
|
if let Some(node) = self.state.root.outputs.get(&output.id) {
|
||||||
self.state.present_output(
|
let res = self.state.present_output(
|
||||||
&node,
|
&node,
|
||||||
&image.fb.get(),
|
&image.fb.get(),
|
||||||
&image.tex.get(),
|
&image.tex.get(),
|
||||||
&mut self.render_result.borrow_mut(),
|
&mut self.render_result.borrow_mut(),
|
||||||
true,
|
true,
|
||||||
);
|
);
|
||||||
|
if let Err(e) = res {
|
||||||
|
log::error!("Could not render screen: {}", ErrorFmt(e));
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let pp = PresentPixmap {
|
let pp = PresentPixmap {
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,11 @@ use {
|
||||||
crate::{
|
crate::{
|
||||||
async_engine::SpawnedFuture,
|
async_engine::SpawnedFuture,
|
||||||
client::{error::LookupError, objects::Objects},
|
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,
|
leaks::Tracker,
|
||||||
object::{Interface, Object, ObjectId, WL_DISPLAY_ID},
|
object::{Interface, Object, ObjectId, WL_DISPLAY_ID},
|
||||||
state::State,
|
state::State,
|
||||||
|
|
@ -149,6 +153,7 @@ impl Clients {
|
||||||
last_xwayland_serial: Cell::new(0),
|
last_xwayland_serial: Cell::new(0),
|
||||||
surfaces_by_xwayland_serial: Default::default(),
|
surfaces_by_xwayland_serial: Default::default(),
|
||||||
activation_tokens: Default::default(),
|
activation_tokens: Default::default(),
|
||||||
|
commit_timelines: Rc::new(CommitTimelines::new(&global.wait_for_sync_obj)),
|
||||||
});
|
});
|
||||||
track!(data, data);
|
track!(data, data);
|
||||||
let display = Rc::new(WlDisplay::new(&data));
|
let display = Rc::new(WlDisplay::new(&data));
|
||||||
|
|
@ -220,6 +225,7 @@ impl Drop for ClientHolder {
|
||||||
self.data.shutdown.clear();
|
self.data.shutdown.clear();
|
||||||
self.data.surfaces_by_xwayland_serial.clear();
|
self.data.surfaces_by_xwayland_serial.clear();
|
||||||
self.data.remove_activation_tokens();
|
self.data.remove_activation_tokens();
|
||||||
|
self.data.commit_timelines.clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -260,6 +266,7 @@ pub struct Client {
|
||||||
pub last_xwayland_serial: Cell<u64>,
|
pub last_xwayland_serial: Cell<u64>,
|
||||||
pub surfaces_by_xwayland_serial: CopyHashMap<u64, Rc<WlSurface>>,
|
pub surfaces_by_xwayland_serial: CopyHashMap<u64, Rc<WlSurface>>,
|
||||||
pub activation_tokens: RefCell<VecDeque<ActivationToken>>,
|
pub activation_tokens: RefCell<VecDeque<ActivationToken>>,
|
||||||
|
pub commit_timelines: Rc<CommitTimelines>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const NUM_CACHED_SERIAL_RANGES: usize = 64;
|
pub const NUM_CACHED_SERIAL_RANGES: usize = 64;
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@ use {
|
||||||
xdg_surface::{xdg_toplevel::XdgToplevel, XdgSurface},
|
xdg_surface::{xdg_toplevel::XdgToplevel, XdgSurface},
|
||||||
WlSurface,
|
WlSurface,
|
||||||
},
|
},
|
||||||
|
wp_linux_drm_syncobj_timeline_v1::WpLinuxDrmSyncobjTimelineV1,
|
||||||
xdg_positioner::XdgPositioner,
|
xdg_positioner::XdgPositioner,
|
||||||
xdg_wm_base::XdgWmBase,
|
xdg_wm_base::XdgWmBase,
|
||||||
},
|
},
|
||||||
|
|
@ -29,8 +30,9 @@ use {
|
||||||
},
|
},
|
||||||
wire::{
|
wire::{
|
||||||
JayOutputId, JayScreencastId, JayWorkspaceId, WlBufferId, WlDataSourceId, WlOutputId,
|
JayOutputId, JayScreencastId, JayWorkspaceId, WlBufferId, WlDataSourceId, WlOutputId,
|
||||||
WlPointerId, WlRegionId, WlRegistryId, WlSeatId, WlSurfaceId, XdgPositionerId,
|
WlPointerId, WlRegionId, WlRegistryId, WlSeatId, WlSurfaceId,
|
||||||
XdgSurfaceId, XdgToplevelId, XdgWmBaseId, ZwpPrimarySelectionSourceV1Id,
|
WpLinuxDrmSyncobjTimelineV1Id, XdgPositionerId, XdgSurfaceId, XdgToplevelId,
|
||||||
|
XdgWmBaseId, ZwpPrimarySelectionSourceV1Id,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
std::{cell::RefCell, mem, rc::Rc},
|
std::{cell::RefCell, mem, rc::Rc},
|
||||||
|
|
@ -56,6 +58,7 @@ pub struct Objects {
|
||||||
pub xdg_wm_bases: CopyHashMap<XdgWmBaseId, Rc<XdgWmBase>>,
|
pub xdg_wm_bases: CopyHashMap<XdgWmBaseId, Rc<XdgWmBase>>,
|
||||||
pub seats: CopyHashMap<WlSeatId, Rc<WlSeat>>,
|
pub seats: CopyHashMap<WlSeatId, Rc<WlSeat>>,
|
||||||
pub screencasts: CopyHashMap<JayScreencastId, Rc<JayScreencast>>,
|
pub screencasts: CopyHashMap<JayScreencastId, Rc<JayScreencast>>,
|
||||||
|
pub timelines: CopyHashMap<WpLinuxDrmSyncobjTimelineV1Id, Rc<WpLinuxDrmSyncobjTimelineV1>>,
|
||||||
ids: RefCell<Vec<usize>>,
|
ids: RefCell<Vec<usize>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -83,6 +86,7 @@ impl Objects {
|
||||||
xdg_wm_bases: Default::default(),
|
xdg_wm_bases: Default::default(),
|
||||||
seats: Default::default(),
|
seats: Default::default(),
|
||||||
screencasts: Default::default(),
|
screencasts: Default::default(),
|
||||||
|
timelines: Default::default(),
|
||||||
ids: RefCell::new(vec![]),
|
ids: RefCell::new(vec![]),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -38,6 +38,7 @@ use {
|
||||||
oserror::OsError, queue::AsyncQueue, refcounted::RefCounted, run_toplevel::RunToplevel,
|
oserror::OsError, queue::AsyncQueue, refcounted::RefCounted, run_toplevel::RunToplevel,
|
||||||
tri::Try,
|
tri::Try,
|
||||||
},
|
},
|
||||||
|
video::drm::wait_for_sync_obj::WaitForSyncObj,
|
||||||
wheel::{Wheel, WheelError},
|
wheel::{Wheel, WheelError},
|
||||||
xkbcommon::XkbContext,
|
xkbcommon::XkbContext,
|
||||||
},
|
},
|
||||||
|
|
@ -221,6 +222,9 @@ fn start_compositor2(
|
||||||
double_click_interval_usec: Cell::new(400 * 1000),
|
double_click_interval_usec: Cell::new(400 * 1000),
|
||||||
double_click_distance: Cell::new(5),
|
double_click_distance: Cell::new(5),
|
||||||
create_default_seat: Cell::new(true),
|
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));
|
state.tracker.register(ClientId::from_raw(0));
|
||||||
create_dummy_output(&state);
|
create_dummy_output(&state);
|
||||||
|
|
|
||||||
|
|
@ -800,6 +800,10 @@ impl ConfigProxyHandler {
|
||||||
self.state.idle.set_timeout(timeout);
|
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> {
|
fn handle_connector_connected(&self, connector: Connector) -> Result<(), CphError> {
|
||||||
let connector = self.get_connector(connector)?;
|
let connector = self.get_connector(connector)?;
|
||||||
self.respond(Response::ConnectorConnected {
|
self.respond(Response::ConnectorConnected {
|
||||||
|
|
@ -1725,6 +1729,9 @@ impl ConfigProxyHandler {
|
||||||
} => self
|
} => self
|
||||||
.handle_move_to_output(workspace, connector)
|
.handle_move_to_output(workspace, connector)
|
||||||
.wrn("move_to_output")?,
|
.wrn("move_to_output")?,
|
||||||
|
ClientMessage::SetExplicitSyncEnabled { enabled } => {
|
||||||
|
self.handle_set_explicit_sync_enabled(enabled)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ use {
|
||||||
crate::{
|
crate::{
|
||||||
fixed::Fixed,
|
fixed::Fixed,
|
||||||
format::ARGB8888,
|
format::ARGB8888,
|
||||||
gfx_api::{GfxContext, GfxError, GfxTexture},
|
gfx_api::{AcquireSync, GfxContext, GfxError, GfxTexture, ReleaseSync},
|
||||||
rect::Rect,
|
rect::Rect,
|
||||||
renderer::Renderer,
|
renderer::Renderer,
|
||||||
scale::Scale,
|
scale::Scale,
|
||||||
|
|
@ -378,6 +378,9 @@ fn render_img(image: &InstantiatedCursorImage, renderer: &mut Renderer, x: Fixed
|
||||||
None,
|
None,
|
||||||
scale,
|
scale,
|
||||||
None,
|
None,
|
||||||
|
None,
|
||||||
|
AcquireSync::None,
|
||||||
|
ReleaseSync::None,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -389,9 +392,18 @@ impl Cursor for StaticCursor {
|
||||||
|
|
||||||
fn render_hardware_cursor(&self, renderer: &mut Renderer) {
|
fn render_hardware_cursor(&self, renderer: &mut Renderer) {
|
||||||
if let Some(img) = self.image.scales.get(&renderer.scale()) {
|
if let Some(img) = self.image.scales.get(&renderer.scale()) {
|
||||||
renderer
|
renderer.base.render_texture(
|
||||||
.base
|
&img.tex,
|
||||||
.render_texture(&img.tex, 0, 0, None, None, renderer.scale(), None);
|
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) {
|
fn render_hardware_cursor(&self, renderer: &mut Renderer) {
|
||||||
let img = &self.images[self.idx.get()];
|
let img = &self.images[self.idx.get()];
|
||||||
if let Some(img) = img.scales.get(&renderer.scale()) {
|
if let Some(img) = img.scales.get(&renderer.scale()) {
|
||||||
renderer
|
renderer.base.render_texture(
|
||||||
.base
|
&img.tex,
|
||||||
.render_texture(&img.tex, 0, 0, None, None, renderer.scale(), None);
|
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,
|
state::State,
|
||||||
theme::Color,
|
theme::Color,
|
||||||
tree::{Node, OutputNode},
|
tree::{Node, OutputNode},
|
||||||
utils::{numcell::NumCell, transform_ext::TransformExt},
|
utils::{clonecell::UnsafeCellCloneSafe, transform_ext::TransformExt},
|
||||||
video::{dmabuf::DmaBuf, gbm::GbmDevice, Modifier},
|
video::{dmabuf::DmaBuf, drm::sync_obj::SyncObjCtx, gbm::GbmDevice, Modifier},
|
||||||
},
|
},
|
||||||
ahash::AHashMap,
|
ahash::AHashMap,
|
||||||
indexmap::IndexSet,
|
indexmap::IndexSet,
|
||||||
|
|
@ -21,9 +21,12 @@ use {
|
||||||
error::Error,
|
error::Error,
|
||||||
ffi::CString,
|
ffi::CString,
|
||||||
fmt::{Debug, Formatter},
|
fmt::{Debug, Formatter},
|
||||||
|
ops::Deref,
|
||||||
rc::Rc,
|
rc::Rc,
|
||||||
|
sync::atomic::{AtomicU64, Ordering::Relaxed},
|
||||||
},
|
},
|
||||||
thiserror::Error,
|
thiserror::Error,
|
||||||
|
uapi::OwnedFd,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub enum GfxApiOpt {
|
pub enum GfxApiOpt {
|
||||||
|
|
@ -141,6 +144,72 @@ pub struct CopyTexture {
|
||||||
pub tex: Rc<dyn GfxTexture>,
|
pub tex: Rc<dyn GfxTexture>,
|
||||||
pub source: SampleRect,
|
pub source: SampleRect,
|
||||||
pub target: FramebufferRect,
|
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)]
|
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||||
|
|
@ -158,7 +227,11 @@ pub trait GfxFramebuffer: Debug {
|
||||||
|
|
||||||
fn physical_size(&self) -> (i32, i32);
|
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(
|
fn copy_to_shm(
|
||||||
self: Rc<Self>,
|
self: Rc<Self>,
|
||||||
|
|
@ -175,13 +248,13 @@ pub trait GfxFramebuffer: Debug {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl dyn GfxFramebuffer {
|
impl dyn GfxFramebuffer {
|
||||||
pub fn clear(&self) {
|
pub fn clear(&self) -> Result<Option<SyncFile>, GfxError> {
|
||||||
self.clear_with(0.0, 0.0, 0.0, 0.0);
|
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();
|
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) {
|
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 mut ops = self.take_render_ops();
|
||||||
let scale = Scale::from_int(1);
|
let scale = Scale::from_int(1);
|
||||||
let mut renderer = self.renderer_base(&mut ops, scale, Transform::None);
|
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);
|
let clear = self.format().has_alpha.then_some(&Color::TRANSPARENT);
|
||||||
self.render(ops, clear);
|
self.render(ops, clear)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn render_custom(
|
pub fn render_custom(
|
||||||
|
|
@ -220,11 +311,11 @@ impl dyn GfxFramebuffer {
|
||||||
scale: Scale,
|
scale: Scale,
|
||||||
clear: Option<&Color>,
|
clear: Option<&Color>,
|
||||||
f: &mut dyn FnMut(&mut RendererBase),
|
f: &mut dyn FnMut(&mut RendererBase),
|
||||||
) {
|
) -> Result<Option<SyncFile>, GfxError> {
|
||||||
let mut ops = self.take_render_ops();
|
let mut ops = self.take_render_ops();
|
||||||
let mut renderer = self.renderer_base(&mut ops, scale, Transform::None);
|
let mut renderer = self.renderer_base(&mut ops, scale, Transform::None);
|
||||||
f(&mut renderer);
|
f(&mut renderer);
|
||||||
self.render(ops, clear);
|
self.render(ops, clear)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create_render_pass(
|
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())
|
self.render(pass.ops, pass.clear.as_ref())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -307,7 +398,7 @@ impl dyn GfxFramebuffer {
|
||||||
result: Option<&mut RenderResult>,
|
result: Option<&mut RenderResult>,
|
||||||
scale: Scale,
|
scale: Scale,
|
||||||
render_hardware_cursor: bool,
|
render_hardware_cursor: bool,
|
||||||
) {
|
) -> Result<Option<SyncFile>, GfxError> {
|
||||||
self.render_node(
|
self.render_node(
|
||||||
node,
|
node,
|
||||||
state,
|
state,
|
||||||
|
|
@ -330,7 +421,7 @@ impl dyn GfxFramebuffer {
|
||||||
render_hardware_cursor: bool,
|
render_hardware_cursor: bool,
|
||||||
black_background: bool,
|
black_background: bool,
|
||||||
transform: Transform,
|
transform: Transform,
|
||||||
) {
|
) -> Result<Option<SyncFile>, GfxError> {
|
||||||
let pass = self.create_render_pass(
|
let pass = self.create_render_pass(
|
||||||
node,
|
node,
|
||||||
state,
|
state,
|
||||||
|
|
@ -341,7 +432,7 @@ impl dyn GfxFramebuffer {
|
||||||
black_background,
|
black_background,
|
||||||
transform,
|
transform,
|
||||||
);
|
);
|
||||||
self.perform_render_pass(pass);
|
self.perform_render_pass(pass)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn render_hardware_cursor(
|
pub fn render_hardware_cursor(
|
||||||
|
|
@ -350,7 +441,7 @@ impl dyn GfxFramebuffer {
|
||||||
state: &State,
|
state: &State,
|
||||||
scale: Scale,
|
scale: Scale,
|
||||||
transform: Transform,
|
transform: Transform,
|
||||||
) {
|
) -> Result<Option<SyncFile>, GfxError> {
|
||||||
let mut ops = self.take_render_ops();
|
let mut ops = self.take_render_ops();
|
||||||
let mut renderer = Renderer {
|
let mut renderer = Renderer {
|
||||||
base: self.renderer_base(&mut ops, scale, transform),
|
base: self.renderer_base(&mut ops, scale, transform),
|
||||||
|
|
@ -363,7 +454,7 @@ impl dyn GfxFramebuffer {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
cursor.render_hardware_cursor(&mut renderer);
|
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;
|
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 {
|
pub trait GfxTexture: Debug {
|
||||||
fn size(&self) -> (i32, i32);
|
fn size(&self) -> (i32, i32);
|
||||||
fn as_any(&self) -> &dyn Any;
|
fn as_any(&self) -> &dyn Any;
|
||||||
|
|
@ -423,7 +482,6 @@ pub trait GfxTexture: Debug {
|
||||||
shm: &[Cell<u8>],
|
shm: &[Cell<u8>],
|
||||||
) -> Result<(), GfxError>;
|
) -> Result<(), GfxError>;
|
||||||
fn dmabuf(&self) -> Option<&DmaBuf>;
|
fn dmabuf(&self) -> Option<&DmaBuf>;
|
||||||
fn reservations(&self) -> &TextureReservations;
|
|
||||||
fn format(&self) -> &'static Format;
|
fn format(&self) -> &'static Format;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -461,6 +519,8 @@ pub trait GfxContext: Debug {
|
||||||
stride: i32,
|
stride: i32,
|
||||||
format: &'static Format,
|
format: &'static Format,
|
||||||
) -> Result<Rc<dyn GfxFramebuffer>, GfxError>;
|
) -> Result<Rc<dyn GfxFramebuffer>, GfxError>;
|
||||||
|
|
||||||
|
fn sync_obj_ctx(&self) -> &Rc<SyncObjCtx>;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
|
|
||||||
|
|
@ -68,8 +68,8 @@ macro_rules! dynload {
|
||||||
use {
|
use {
|
||||||
crate::{
|
crate::{
|
||||||
gfx_api::{
|
gfx_api::{
|
||||||
CopyTexture, FillRect, FramebufferRect, GfxApiOpt, GfxContext, GfxError, GfxTexture,
|
AcquireSync, CopyTexture, FillRect, GfxApiOpt, GfxContext, GfxError, GfxTexture,
|
||||||
SampleRect,
|
ReleaseSync, SyncFile,
|
||||||
},
|
},
|
||||||
gfx_apis::gl::{
|
gfx_apis::gl::{
|
||||||
gl::texture::image_target,
|
gl::texture::image_target,
|
||||||
|
|
@ -80,8 +80,9 @@ use {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
theme::Color,
|
theme::Color,
|
||||||
utils::{rc_eq::rc_eq, vecstorage::VecStorage},
|
utils::{errorfmt::ErrorFmt, rc_eq::rc_eq, vecstorage::VecStorage},
|
||||||
video::{
|
video::{
|
||||||
|
dmabuf::DMA_BUF_SYNC_READ,
|
||||||
drm::{Drm, DrmError},
|
drm::{Drm, DrmError},
|
||||||
gbm::GbmError,
|
gbm::GbmError,
|
||||||
},
|
},
|
||||||
|
|
@ -181,6 +182,14 @@ enum RenderError {
|
||||||
NoSupportedFormats,
|
NoSupportedFormats,
|
||||||
#[error("Cannot convert a shm texture into a framebuffer")]
|
#[error("Cannot convert a shm texture into a framebuffer")]
|
||||||
ShmTextureToFb,
|
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)]
|
#[derive(Default)]
|
||||||
|
|
@ -190,7 +199,7 @@ struct GfxGlState {
|
||||||
copy_tex: VecStorage<&'static CopyTexture>,
|
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 mut state = fb.ctx.gl_state.borrow_mut();
|
||||||
let state = &mut *state;
|
let state = &mut *state;
|
||||||
let mut fill_rect = state.fill_rect.take();
|
let mut fill_rect = state.fill_rect.take();
|
||||||
|
|
@ -256,9 +265,30 @@ fn run_ops(fb: &Framebuffer, ops: &[GfxApiOpt]) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for tex in &*copy_tex {
|
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) {
|
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(
|
fn render_texture(ctx: &GlRenderContext, tex: &CopyTexture) {
|
||||||
ctx: &GlRenderContext,
|
let texture = tex.tex.as_gl();
|
||||||
texture: &Texture,
|
|
||||||
target_rect: &FramebufferRect,
|
|
||||||
src: &SampleRect,
|
|
||||||
) {
|
|
||||||
assert!(rc_eq(&ctx.ctx, &texture.ctx.ctx));
|
assert!(rc_eq(&ctx.ctx, &texture.ctx.ctx));
|
||||||
let gles = ctx.ctx.dpy.gles;
|
let gles = ctx.ctx.dpy.gles;
|
||||||
unsafe {
|
unsafe {
|
||||||
|
handle_explicit_sync(ctx, texture, &tex.acquire_sync);
|
||||||
|
|
||||||
(gles.glActiveTexture)(GL_TEXTURE0);
|
(gles.glActiveTexture)(GL_TEXTURE0);
|
||||||
|
|
||||||
let target = image_target(texture.gl.external_only);
|
let target = image_target(texture.gl.external_only);
|
||||||
|
|
@ -321,8 +349,8 @@ fn render_texture(
|
||||||
|
|
||||||
(gles.glUniform1i)(prog.tex, 0);
|
(gles.glUniform1i)(prog.tex, 0);
|
||||||
|
|
||||||
let texcoord = src.to_points();
|
let texcoord = tex.source.to_points();
|
||||||
let pos = target_rect.to_points();
|
let pos = tex.target.to_points();
|
||||||
|
|
||||||
(gles.glVertexAttribPointer)(
|
(gles.glVertexAttribPointer)(
|
||||||
prog.texcoord as _,
|
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 {
|
impl dyn GfxTexture {
|
||||||
fn as_gl(&self) -> &Texture {
|
fn as_gl(&self) -> &Texture {
|
||||||
self.as_any()
|
self.as_any()
|
||||||
|
|
|
||||||
|
|
@ -24,9 +24,10 @@ use {
|
||||||
PROCS,
|
PROCS,
|
||||||
},
|
},
|
||||||
ext::{
|
ext::{
|
||||||
get_display_ext, get_gl_ext, DisplayExt, GlExt, EXT_CREATE_CONTEXT_ROBUSTNESS,
|
get_display_ext, get_gl_ext, DisplayExt, GlExt, ANDROID_NATIVE_FENCE_SYNC,
|
||||||
EXT_IMAGE_DMA_BUF_IMPORT_MODIFIERS, GL_OES_EGL_IMAGE, GL_OES_EGL_IMAGE_EXTERNAL,
|
EXT_CREATE_CONTEXT_ROBUSTNESS, EXT_IMAGE_DMA_BUF_IMPORT_MODIFIERS,
|
||||||
KHR_IMAGE_BASE, KHR_NO_CONFIG_CONTEXT, KHR_SURFACELESS_CONTEXT,
|
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,
|
MESA_CONFIGLESS_CONTEXT,
|
||||||
},
|
},
|
||||||
proc::ExtProc,
|
proc::ExtProc,
|
||||||
|
|
@ -65,6 +66,7 @@ pub struct EglDisplay {
|
||||||
pub formats: AHashMap<u32, EglFormat>,
|
pub formats: AHashMap<u32, EglFormat>,
|
||||||
pub gbm: Rc<GbmDevice>,
|
pub gbm: Rc<GbmDevice>,
|
||||||
pub dpy: EGLDisplay,
|
pub dpy: EGLDisplay,
|
||||||
|
pub explicit_sync: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl EglDisplay {
|
impl EglDisplay {
|
||||||
|
|
@ -99,6 +101,7 @@ impl EglDisplay {
|
||||||
formats: AHashMap::new(),
|
formats: AHashMap::new(),
|
||||||
gbm: Rc::new(gbm),
|
gbm: Rc::new(gbm),
|
||||||
dpy,
|
dpy,
|
||||||
|
explicit_sync: false,
|
||||||
};
|
};
|
||||||
let mut major = 0;
|
let mut major = 0;
|
||||||
let mut minor = 0;
|
let mut minor = 0;
|
||||||
|
|
@ -122,6 +125,9 @@ impl EglDisplay {
|
||||||
return Err(RenderError::SurfacelessContext);
|
return Err(RenderError::SurfacelessContext);
|
||||||
}
|
}
|
||||||
dpy.formats = query_formats(procs, dpy.dpy)?;
|
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))
|
Ok(Rc::new(dpy))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ pub type EGLBoolean = c::c_uint;
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub type EGLuint64KHR = u64;
|
pub type EGLuint64KHR = u64;
|
||||||
pub type EGLAttrib = isize;
|
pub type EGLAttrib = isize;
|
||||||
|
pub type EGLSyncKHR = *mut u8;
|
||||||
|
|
||||||
egl_transparent!(EGLDisplay);
|
egl_transparent!(EGLDisplay);
|
||||||
egl_transparent!(EGLSurface);
|
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_DMA_BUF_PLANE3_MODIFIER_HI_EXT: EGLint = 0x344A;
|
||||||
pub const EGL_IMAGE_PRESERVED_KHR: EGLint = 0x30D2;
|
pub const EGL_IMAGE_PRESERVED_KHR: EGLint = 0x30D2;
|
||||||
pub const EGL_LINUX_DMA_BUF_EXT: EGLint = 0x3270;
|
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! {
|
dynload! {
|
||||||
EGL: Egl from "libEGL.so" {
|
EGL: Egl from "libEGL.so" {
|
||||||
|
|
|
||||||
|
|
@ -78,6 +78,9 @@ bitflags! {
|
||||||
KHR_SURFACELESS_CONTEXT = 1 << 5,
|
KHR_SURFACELESS_CONTEXT = 1 << 5,
|
||||||
IMG_CONTEXT_PRIORITY = 1 << 6,
|
IMG_CONTEXT_PRIORITY = 1 << 6,
|
||||||
EXT_CREATE_CONTEXT_ROBUSTNESS = 1 << 7,
|
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 {
|
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",
|
"EGL_EXT_create_context_robustness",
|
||||||
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) {
|
match get_dpy_extensions(dpy) {
|
||||||
Some(exts) => get_typed_ext(&exts, DisplayExt::none(), &map),
|
Some(exts) => get_typed_ext(&exts, DisplayExt::none(), &map),
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
pub(super) mod context;
|
pub(super) mod context;
|
||||||
pub(super) mod framebuffer;
|
pub(super) mod framebuffer;
|
||||||
pub(super) mod image;
|
pub(super) mod image;
|
||||||
|
pub(super) mod sync;
|
||||||
pub(super) mod texture;
|
pub(super) mod texture;
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,8 @@ use {
|
||||||
crate::{
|
crate::{
|
||||||
format::{Format, XRGB8888},
|
format::{Format, XRGB8888},
|
||||||
gfx_api::{
|
gfx_api::{
|
||||||
GfxApiOpt, GfxContext, GfxError, GfxFormat, GfxFramebuffer, GfxImage, GfxTexture,
|
BufferResvUser, GfxApiOpt, GfxContext, GfxError, GfxFormat, GfxFramebuffer, GfxImage,
|
||||||
ResetStatus,
|
GfxTexture, ResetStatus,
|
||||||
},
|
},
|
||||||
gfx_apis::gl::{
|
gfx_apis::gl::{
|
||||||
egl::{context::EglContext, display::EglDisplay, image::EglImage},
|
egl::{context::EglContext, display::EglDisplay, image::EglImage},
|
||||||
|
|
@ -14,7 +14,11 @@ use {
|
||||||
renderer::{framebuffer::Framebuffer, image::Image},
|
renderer::{framebuffer::Framebuffer, image::Image},
|
||||||
GfxGlState, RenderError, Texture,
|
GfxGlState, RenderError, Texture,
|
||||||
},
|
},
|
||||||
video::{dmabuf::DmaBuf, drm::Drm, gbm::GbmDevice},
|
video::{
|
||||||
|
dmabuf::DmaBuf,
|
||||||
|
drm::{sync_obj::SyncObjCtx, Drm},
|
||||||
|
gbm::GbmDevice,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
ahash::AHashMap,
|
ahash::AHashMap,
|
||||||
jay_config::video::GfxApi,
|
jay_config::video::GfxApi,
|
||||||
|
|
@ -53,6 +57,7 @@ pub(crate) struct TexProgs {
|
||||||
pub(in crate::gfx_apis::gl) struct GlRenderContext {
|
pub(in crate::gfx_apis::gl) struct GlRenderContext {
|
||||||
pub(crate) ctx: Rc<EglContext>,
|
pub(crate) ctx: Rc<EglContext>,
|
||||||
pub gbm: Rc<GbmDevice>,
|
pub gbm: Rc<GbmDevice>,
|
||||||
|
pub sync_ctx: Rc<SyncObjCtx>,
|
||||||
|
|
||||||
pub(crate) render_node: Rc<CString>,
|
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(crate) gfx_ops: RefCell<Vec<GfxApiOpt>>,
|
||||||
pub(in crate::gfx_apis::gl) gl_state: RefCell<GfxGlState>,
|
pub(in crate::gfx_apis::gl) gl_state: RefCell<GfxGlState>,
|
||||||
|
|
||||||
|
pub(in crate::gfx_apis::gl) buffer_resv_user: BufferResvUser,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Debug for GlRenderContext {
|
impl Debug for GlRenderContext {
|
||||||
|
|
@ -126,6 +133,7 @@ impl GlRenderContext {
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
ctx: ctx.clone(),
|
ctx: ctx.clone(),
|
||||||
gbm: ctx.dpy.gbm.clone(),
|
gbm: ctx.dpy.gbm.clone(),
|
||||||
|
sync_ctx: Rc::new(SyncObjCtx::new(ctx.dpy.gbm.drm.fd())),
|
||||||
|
|
||||||
render_node: node.clone(),
|
render_node: node.clone(),
|
||||||
|
|
||||||
|
|
@ -141,6 +149,8 @@ impl GlRenderContext {
|
||||||
|
|
||||||
gfx_ops: Default::default(),
|
gfx_ops: Default::default(),
|
||||||
gl_state: Default::default(),
|
gl_state: Default::default(),
|
||||||
|
|
||||||
|
buffer_resv_user: Default::default(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -186,7 +196,6 @@ impl GlRenderContext {
|
||||||
Ok(Rc::new(Texture {
|
Ok(Rc::new(Texture {
|
||||||
ctx: self.clone(),
|
ctx: self.clone(),
|
||||||
gl,
|
gl,
|
||||||
resv: Default::default(),
|
|
||||||
format,
|
format,
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
@ -268,4 +277,8 @@ impl GfxContext for GlRenderContext {
|
||||||
})?;
|
})?;
|
||||||
Ok(Rc::new(Framebuffer { ctx: self, gl: fb }))
|
Ok(Rc::new(Framebuffer { ctx: self, gl: fb }))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn sync_obj_ctx(&self) -> &Rc<SyncObjCtx> {
|
||||||
|
&self.sync_ctx
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
use {
|
use {
|
||||||
crate::{
|
crate::{
|
||||||
format::Format,
|
format::Format,
|
||||||
gfx_api::{GfxApiOpt, GfxError, GfxFramebuffer},
|
gfx_api::{GfxApiOpt, GfxError, GfxFramebuffer, SyncFile},
|
||||||
gfx_apis::gl::{
|
gfx_apis::gl::{
|
||||||
gl::{
|
gl::{
|
||||||
frame_buffer::GlFrameBuffer,
|
frame_buffer::GlFrameBuffer,
|
||||||
|
|
@ -10,6 +10,7 @@ use {
|
||||||
renderer::context::GlRenderContext,
|
renderer::context::GlRenderContext,
|
||||||
run_ops,
|
run_ops,
|
||||||
sys::{GL_ONE, GL_ONE_MINUS_SRC_ALPHA},
|
sys::{GL_ONE, GL_ONE_MINUS_SRC_ALPHA},
|
||||||
|
RenderError,
|
||||||
},
|
},
|
||||||
theme::Color,
|
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 gles = self.ctx.ctx.dpy.gles;
|
||||||
let _ = self.ctx.ctx.with_current(|| {
|
let res = self.ctx.ctx.with_current(|| {
|
||||||
unsafe {
|
unsafe {
|
||||||
(gles.glBindFramebuffer)(GL_FRAMEBUFFER, self.gl.fbo);
|
(gles.glBindFramebuffer)(GL_FRAMEBUFFER, self.gl.fbo);
|
||||||
(gles.glViewport)(0, 0, self.gl.width, self.gl.height);
|
(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);
|
(gles.glBlendFunc)(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
|
||||||
}
|
}
|
||||||
run_ops(self, &ops);
|
let fd = run_ops(self, &ops);
|
||||||
unsafe {
|
if fd.is_none() {
|
||||||
(gles.glFlush)();
|
unsafe {
|
||||||
|
(gles.glFlush)();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(fd)
|
||||||
});
|
});
|
||||||
|
ops.clear();
|
||||||
*self.ctx.gfx_ops.borrow_mut() = ops;
|
*self.ctx.gfx_ops.borrow_mut() = ops;
|
||||||
|
res
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -92,17 +101,19 @@ impl GfxFramebuffer for Framebuffer {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn take_render_ops(&self) -> Vec<GfxApiOpt> {
|
fn take_render_ops(&self) -> Vec<GfxApiOpt> {
|
||||||
let mut ops = mem::take(&mut *self.ctx.gfx_ops.borrow_mut());
|
mem::take(&mut *self.ctx.gfx_ops.borrow_mut())
|
||||||
ops.clear();
|
|
||||||
ops
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn physical_size(&self) -> (i32, i32) {
|
fn physical_size(&self) -> (i32, i32) {
|
||||||
(self.gl.width, self.gl.height)
|
(self.gl.width, self.gl.height)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render(&self, ops: Vec<GfxApiOpt>, clear: Option<&Color>) {
|
fn render(
|
||||||
self.render(ops, clear);
|
&self,
|
||||||
|
ops: Vec<GfxApiOpt>,
|
||||||
|
clear: Option<&Color>,
|
||||||
|
) -> Result<Option<SyncFile>, GfxError> {
|
||||||
|
self.render(ops, clear).map_err(|e| e.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn copy_to_shm(
|
fn copy_to_shm(
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,6 @@ impl Image {
|
||||||
Ok(Rc::new(Texture {
|
Ok(Rc::new(Texture {
|
||||||
ctx: self.ctx.clone(),
|
ctx: self.ctx.clone(),
|
||||||
gl: GlTexture::import_img(&self.ctx.ctx, &self.gl)?,
|
gl: GlTexture::import_img(&self.ctx.ctx, &self.gl)?,
|
||||||
resv: Default::default(),
|
|
||||||
format: self.gl.dmabuf.format,
|
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 {
|
use {
|
||||||
crate::{
|
crate::{
|
||||||
format::Format,
|
format::Format,
|
||||||
gfx_api::{GfxError, GfxTexture, TextureReservations},
|
gfx_api::{GfxError, GfxTexture},
|
||||||
gfx_apis::gl::{
|
gfx_apis::gl::{
|
||||||
gl::texture::GlTexture,
|
gl::texture::GlTexture,
|
||||||
renderer::{context::GlRenderContext, framebuffer::Framebuffer},
|
renderer::{context::GlRenderContext, framebuffer::Framebuffer},
|
||||||
|
|
@ -20,7 +20,6 @@ use {
|
||||||
pub struct Texture {
|
pub struct Texture {
|
||||||
pub(in crate::gfx_apis::gl) ctx: Rc<GlRenderContext>,
|
pub(in crate::gfx_apis::gl) ctx: Rc<GlRenderContext>,
|
||||||
pub(in crate::gfx_apis::gl) gl: GlTexture,
|
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,
|
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)
|
self.gl.img.as_ref().map(|i| &i.dmabuf)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn reservations(&self) -> &TextureReservations {
|
|
||||||
&self.resv
|
|
||||||
}
|
|
||||||
|
|
||||||
fn format(&self) -> &'static Format {
|
fn format(&self) -> &'static Format {
|
||||||
self.format
|
self.format
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,7 @@ use {
|
||||||
utils::oserror::OsError,
|
utils::oserror::OsError,
|
||||||
video::{
|
video::{
|
||||||
dmabuf::DmaBuf,
|
dmabuf::DmaBuf,
|
||||||
drm::{Drm, DrmError},
|
drm::{sync_obj::SyncObjCtx, Drm, DrmError},
|
||||||
gbm::{GbmDevice, GbmError},
|
gbm::{GbmDevice, GbmError},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
@ -93,7 +93,7 @@ pub enum VulkanError {
|
||||||
LoadImageProperties(#[source] vk::Result),
|
LoadImageProperties(#[source] vk::Result),
|
||||||
#[error("Device does not support rending and texturing from the XRGB8888 format")]
|
#[error("Device does not support rending and texturing from the XRGB8888 format")]
|
||||||
XRGB8888,
|
XRGB8888,
|
||||||
#[error("Device does not support syncobj import")]
|
#[error("Device does not support sync obj import")]
|
||||||
SyncobjImport,
|
SyncobjImport,
|
||||||
#[error("Could not start a command buffer")]
|
#[error("Could not start a command buffer")]
|
||||||
BeginCommandBuffer(vk::Result),
|
BeginCommandBuffer(vk::Result),
|
||||||
|
|
@ -153,8 +153,6 @@ pub enum VulkanError {
|
||||||
IoctlExportSyncFile(#[source] OsError),
|
IoctlExportSyncFile(#[source] OsError),
|
||||||
#[error("Could not import a sync file into a semaphore")]
|
#[error("Could not import a sync file into a semaphore")]
|
||||||
ImportSyncFile(#[source] vk::Result),
|
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")]
|
#[error("Could not export a sync file from a semaphore")]
|
||||||
ExportSyncFile(#[source] vk::Result),
|
ExportSyncFile(#[source] vk::Result),
|
||||||
#[error("Could not fetch the render node of the device")]
|
#[error("Could not fetch the render node of the device")]
|
||||||
|
|
@ -175,6 +173,8 @@ pub enum VulkanError {
|
||||||
height: i32,
|
height: i32,
|
||||||
stride: i32,
|
stride: i32,
|
||||||
},
|
},
|
||||||
|
#[error(transparent)]
|
||||||
|
GfxError(GfxError),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<VulkanError> for GfxError {
|
impl From<VulkanError> for GfxError {
|
||||||
|
|
@ -270,6 +270,10 @@ impl GfxContext for Context {
|
||||||
.create_shm_texture(format, width, height, stride, &[], true)?;
|
.create_shm_texture(format, width, height, stride, &[], true)?;
|
||||||
Ok(fb)
|
Ok(fb)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn sync_obj_ctx(&self) -> &Rc<SyncObjCtx> {
|
||||||
|
&self.0.device.sync_ctx
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Drop for Context {
|
impl Drop for Context {
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,10 @@ use {
|
||||||
util::OnDrop,
|
util::OnDrop,
|
||||||
VulkanError,
|
VulkanError,
|
||||||
},
|
},
|
||||||
video::{drm::Drm, gbm::GbmDevice},
|
video::{
|
||||||
|
drm::{sync_obj::SyncObjCtx, Drm},
|
||||||
|
gbm::GbmDevice,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
ahash::AHashMap,
|
ahash::AHashMap,
|
||||||
arrayvec::ArrayVec,
|
arrayvec::ArrayVec,
|
||||||
|
|
@ -43,6 +46,7 @@ pub struct VulkanDevice {
|
||||||
pub(super) physical_device: PhysicalDevice,
|
pub(super) physical_device: PhysicalDevice,
|
||||||
pub(super) render_node: Rc<CString>,
|
pub(super) render_node: Rc<CString>,
|
||||||
pub(super) gbm: GbmDevice,
|
pub(super) gbm: GbmDevice,
|
||||||
|
pub(super) sync_ctx: Rc<SyncObjCtx>,
|
||||||
pub(super) instance: Rc<VulkanInstance>,
|
pub(super) instance: Rc<VulkanInstance>,
|
||||||
pub(super) device: Device,
|
pub(super) device: Device,
|
||||||
pub(super) external_memory_fd: ExternalMemoryFd,
|
pub(super) external_memory_fd: ExternalMemoryFd,
|
||||||
|
|
@ -279,6 +283,7 @@ impl VulkanInstance {
|
||||||
Ok(Rc::new(VulkanDevice {
|
Ok(Rc::new(VulkanDevice {
|
||||||
physical_device: phy_dev,
|
physical_device: phy_dev,
|
||||||
render_node,
|
render_node,
|
||||||
|
sync_ctx: Rc::new(SyncObjCtx::new(gbm.drm.fd())),
|
||||||
gbm,
|
gbm,
|
||||||
instance: self.clone(),
|
instance: self.clone(),
|
||||||
device,
|
device,
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,8 @@
|
||||||
use {
|
use {
|
||||||
crate::gfx_apis::vulkan::{device::VulkanDevice, VulkanError},
|
crate::{
|
||||||
|
gfx_api::SyncFile,
|
||||||
|
gfx_apis::vulkan::{device::VulkanDevice, VulkanError},
|
||||||
|
},
|
||||||
ash::vk::{
|
ash::vk::{
|
||||||
ExportFenceCreateInfo, ExternalFenceHandleTypeFlags, Fence, FenceCreateInfo,
|
ExportFenceCreateInfo, ExternalFenceHandleTypeFlags, Fence, FenceCreateInfo,
|
||||||
FenceGetFdInfoKHR,
|
FenceGetFdInfoKHR,
|
||||||
|
|
@ -38,12 +41,12 @@ impl VulkanDevice {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl VulkanFence {
|
impl VulkanFence {
|
||||||
pub fn export_syncfile(&self) -> Result<Rc<OwnedFd>, VulkanError> {
|
pub fn export_sync_file(&self) -> Result<SyncFile, VulkanError> {
|
||||||
let info = FenceGetFdInfoKHR::builder()
|
let info = FenceGetFdInfoKHR::builder()
|
||||||
.fence(self.fence)
|
.fence(self.fence)
|
||||||
.handle_type(ExternalFenceHandleTypeFlags::SYNC_FD);
|
.handle_type(ExternalFenceHandleTypeFlags::SYNC_FD);
|
||||||
let res = unsafe { self.device.external_fence_fd.get_fence_fd(&info) };
|
let res = unsafe { self.device.external_fence_fd.get_fence_fd(&info) };
|
||||||
res.map_err(VulkanError::ExportSyncFile)
|
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 {
|
use {
|
||||||
crate::{
|
crate::{
|
||||||
format::Format,
|
format::Format,
|
||||||
gfx_api::{GfxApiOpt, GfxError, GfxFramebuffer, GfxImage, GfxTexture, TextureReservations},
|
gfx_api::{GfxApiOpt, GfxError, GfxFramebuffer, GfxImage, GfxTexture, SyncFile},
|
||||||
gfx_apis::vulkan::{
|
gfx_apis::vulkan::{
|
||||||
allocator::VulkanAllocation, device::VulkanDevice, format::VulkanMaxExtents,
|
allocator::VulkanAllocation, device::VulkanDevice, format::VulkanMaxExtents,
|
||||||
renderer::VulkanRenderer, util::OnDrop, VulkanError,
|
renderer::VulkanRenderer, util::OnDrop, VulkanError,
|
||||||
|
|
@ -53,7 +53,6 @@ pub struct VulkanImage {
|
||||||
pub(super) is_undefined: Cell<bool>,
|
pub(super) is_undefined: Cell<bool>,
|
||||||
pub(super) ty: VulkanImageMemory,
|
pub(super) ty: VulkanImageMemory,
|
||||||
pub(super) render_ops: CloneCell<Vec<GfxApiOpt>>,
|
pub(super) render_ops: CloneCell<Vec<GfxApiOpt>>,
|
||||||
pub(super) resv: TextureReservations,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum VulkanImageMemory {
|
pub enum VulkanImageMemory {
|
||||||
|
|
@ -212,7 +211,6 @@ impl VulkanRenderer {
|
||||||
is_undefined: Cell::new(true),
|
is_undefined: Cell::new(true),
|
||||||
ty: VulkanImageMemory::Internal(shm),
|
ty: VulkanImageMemory::Internal(shm),
|
||||||
render_ops: Default::default(),
|
render_ops: Default::default(),
|
||||||
resv: Default::default(),
|
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -482,7 +480,6 @@ impl VulkanDmaBufImageTemplate {
|
||||||
}),
|
}),
|
||||||
format: self.dmabuf.format,
|
format: self.dmabuf.format,
|
||||||
is_undefined: Cell::new(true),
|
is_undefined: Cell::new(true),
|
||||||
resv: Default::default(),
|
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -528,8 +525,14 @@ impl GfxFramebuffer for VulkanImage {
|
||||||
(self.width as _, self.height as _)
|
(self.width as _, self.height as _)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render(&self, ops: Vec<GfxApiOpt>, clear: Option<&Color>) {
|
fn render(
|
||||||
self.renderer.execute(self, &ops, clear).unwrap();
|
&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(
|
fn copy_to_shm(
|
||||||
|
|
@ -587,10 +590,6 @@ impl GfxTexture for VulkanImage {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn reservations(&self) -> &TextureReservations {
|
|
||||||
&self.resv
|
|
||||||
}
|
|
||||||
|
|
||||||
fn format(&self) -> &'static Format {
|
fn format(&self) -> &'static Format {
|
||||||
self.format
|
self.format
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -146,8 +146,7 @@ impl Drop for VulkanInstance {
|
||||||
|
|
||||||
const REQUIRED_INSTANCE_EXTENSIONS: &[&CStr] = &[ExtDebugUtilsFn::name()];
|
const REQUIRED_INSTANCE_EXTENSIONS: &[&CStr] = &[ExtDebugUtilsFn::name()];
|
||||||
|
|
||||||
const VALIDATION_LAYER: &CStr =
|
const VALIDATION_LAYER: &CStr = c"VK_LAYER_KHRONOS_validation";
|
||||||
unsafe { CStr::from_bytes_with_nul_unchecked(b"VK_LAYER_KHRONOS_validation\0") };
|
|
||||||
|
|
||||||
pub type Extensions = AHashMap<CString, u32>;
|
pub type Extensions = AHashMap<CString, u32>;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,10 @@ use {
|
||||||
crate::{
|
crate::{
|
||||||
async_engine::SpawnedFuture,
|
async_engine::SpawnedFuture,
|
||||||
format::Format,
|
format::Format,
|
||||||
gfx_api::{GfxApiOpt, GfxFormat, GfxFramebuffer, GfxTexture},
|
gfx_api::{
|
||||||
|
AcquireSync, BufferResv, BufferResvUser, GfxApiOpt, GfxFormat, GfxFramebuffer,
|
||||||
|
GfxTexture, ReleaseSync, SyncFile,
|
||||||
|
},
|
||||||
gfx_apis::vulkan::{
|
gfx_apis::vulkan::{
|
||||||
allocator::VulkanAllocator,
|
allocator::VulkanAllocator,
|
||||||
command::{VulkanCommandBuffer, VulkanCommandPool},
|
command::{VulkanCommandBuffer, VulkanCommandPool},
|
||||||
|
|
@ -21,10 +24,7 @@ use {
|
||||||
io_uring::IoUring,
|
io_uring::IoUring,
|
||||||
theme::Color,
|
theme::Color,
|
||||||
utils::{copyhashmap::CopyHashMap, errorfmt::ErrorFmt, numcell::NumCell, stack::Stack},
|
utils::{copyhashmap::CopyHashMap, errorfmt::ErrorFmt, numcell::NumCell, stack::Stack},
|
||||||
video::dmabuf::{
|
video::dmabuf::{dma_buf_export_sync_file, DMA_BUF_SYNC_READ, DMA_BUF_SYNC_WRITE},
|
||||||
dma_buf_export_sync_file, dma_buf_import_sync_file, DMA_BUF_SYNC_READ,
|
|
||||||
DMA_BUF_SYNC_WRITE,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
ahash::AHashMap,
|
ahash::AHashMap,
|
||||||
ash::{
|
ash::{
|
||||||
|
|
@ -66,6 +66,14 @@ pub struct VulkanRenderer {
|
||||||
pub(super) pending_frames: CopyHashMap<u64, Rc<PendingFrame>>,
|
pub(super) pending_frames: CopyHashMap<u64, Rc<PendingFrame>>,
|
||||||
pub(super) allocator: Rc<VulkanAllocator>,
|
pub(super) allocator: Rc<VulkanAllocator>,
|
||||||
pub(super) last_point: NumCell<u64>,
|
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)]
|
#[derive(Default)]
|
||||||
|
|
@ -73,20 +81,20 @@ pub(super) struct Memory {
|
||||||
sample: Vec<Rc<VulkanImage>>,
|
sample: Vec<Rc<VulkanImage>>,
|
||||||
flush: Vec<Rc<VulkanImage>>,
|
flush: Vec<Rc<VulkanImage>>,
|
||||||
flush_staging: Vec<(Rc<VulkanImage>, VulkanStagingBuffer)>,
|
flush_staging: Vec<(Rc<VulkanImage>, VulkanStagingBuffer)>,
|
||||||
textures: Vec<Rc<VulkanImage>>,
|
textures: Vec<UsedTexture>,
|
||||||
image_barriers: Vec<ImageMemoryBarrier2>,
|
image_barriers: Vec<ImageMemoryBarrier2>,
|
||||||
shm_barriers: Vec<BufferMemoryBarrier2>,
|
shm_barriers: Vec<BufferMemoryBarrier2>,
|
||||||
wait_semaphores: Vec<Rc<VulkanSemaphore>>,
|
wait_semaphores: Vec<Rc<VulkanSemaphore>>,
|
||||||
wait_semaphore_infos: Vec<SemaphoreSubmitInfo>,
|
wait_semaphore_infos: Vec<SemaphoreSubmitInfo>,
|
||||||
release_fence: Option<Rc<VulkanFence>>,
|
release_fence: Option<Rc<VulkanFence>>,
|
||||||
release_syncfile: Option<Rc<OwnedFd>>,
|
release_sync_file: Option<SyncFile>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) struct PendingFrame {
|
pub(super) struct PendingFrame {
|
||||||
point: u64,
|
point: u64,
|
||||||
renderer: Rc<VulkanRenderer>,
|
renderer: Rc<VulkanRenderer>,
|
||||||
cmd: Cell<Option<Rc<VulkanCommandBuffer>>>,
|
cmd: Cell<Option<Rc<VulkanCommandBuffer>>>,
|
||||||
_textures: Vec<Rc<VulkanImage>>,
|
_textures: Vec<UsedTexture>,
|
||||||
_staging: Vec<(Rc<VulkanImage>, VulkanStagingBuffer)>,
|
_staging: Vec<(Rc<VulkanImage>, VulkanStagingBuffer)>,
|
||||||
wait_semaphores: Cell<Vec<Rc<VulkanSemaphore>>>,
|
wait_semaphores: Cell<Vec<Rc<VulkanSemaphore>>>,
|
||||||
waiter: Cell<Option<SpawnedFuture<()>>>,
|
waiter: Cell<Option<SpawnedFuture<()>>>,
|
||||||
|
|
@ -157,6 +165,7 @@ impl VulkanDevice {
|
||||||
pending_frames: Default::default(),
|
pending_frames: Default::default(),
|
||||||
allocator,
|
allocator,
|
||||||
last_point: Default::default(),
|
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>,
|
let import = |infos: &mut Vec<SemaphoreSubmitInfoKHR>,
|
||||||
semaphores: &mut Vec<Rc<VulkanSemaphore>>,
|
semaphores: &mut Vec<Rc<VulkanSemaphore>>,
|
||||||
img: &VulkanImage,
|
img: &VulkanImage,
|
||||||
|
sync: &AcquireSync,
|
||||||
flag: u32|
|
flag: u32|
|
||||||
-> Result<(), VulkanError> {
|
-> Result<(), VulkanError> {
|
||||||
if let VulkanImageMemory::DmaBuf(buf) = &img.ty {
|
if let VulkanImageMemory::DmaBuf(buf) = &img.ty {
|
||||||
for plane in &buf.template.dmabuf.planes {
|
let mut import_sync_file = |fd: OwnedFd| -> Result<(), VulkanError> {
|
||||||
let fd = dma_buf_export_sync_file(&plane.fd, flag)
|
|
||||||
.map_err(VulkanError::IoctlExportSyncFile)?;
|
|
||||||
let semaphore = self.allocate_semaphore()?;
|
let semaphore = self.allocate_semaphore()?;
|
||||||
semaphore.import_syncfile(fd)?;
|
semaphore.import_sync_file(fd)?;
|
||||||
infos.push(
|
infos.push(
|
||||||
SemaphoreSubmitInfo::builder()
|
SemaphoreSubmitInfo::builder()
|
||||||
.semaphore(semaphore.semaphore)
|
.semaphore(semaphore.semaphore)
|
||||||
|
|
@ -545,6 +558,23 @@ impl VulkanRenderer {
|
||||||
.build(),
|
.build(),
|
||||||
);
|
);
|
||||||
semaphores.push(semaphore);
|
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(())
|
Ok(())
|
||||||
|
|
@ -553,7 +583,8 @@ impl VulkanRenderer {
|
||||||
import(
|
import(
|
||||||
&mut memory.wait_semaphore_infos,
|
&mut memory.wait_semaphore_infos,
|
||||||
&mut memory.wait_semaphores,
|
&mut memory.wait_semaphores,
|
||||||
texture,
|
&texture.tex,
|
||||||
|
&texture.acquire_sync,
|
||||||
DMA_BUF_SYNC_READ,
|
DMA_BUF_SYNC_READ,
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
|
|
@ -561,33 +592,43 @@ impl VulkanRenderer {
|
||||||
&mut memory.wait_semaphore_infos,
|
&mut memory.wait_semaphore_infos,
|
||||||
&mut memory.wait_semaphores,
|
&mut memory.wait_semaphores,
|
||||||
fb,
|
fb,
|
||||||
|
&AcquireSync::Implicit,
|
||||||
DMA_BUF_SYNC_WRITE,
|
DMA_BUF_SYNC_WRITE,
|
||||||
)?;
|
)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn import_release_semaphore(&self, fb: &VulkanImage) {
|
fn import_release_semaphore(&self, fb: &VulkanImage) {
|
||||||
let memory = self.memory.borrow();
|
let memory = &mut *self.memory.borrow_mut();
|
||||||
let syncfile = match memory.release_syncfile.as_ref() {
|
let sync_file = match memory.release_sync_file.as_ref() {
|
||||||
Some(syncfile) => syncfile,
|
Some(sync_file) => sync_file,
|
||||||
_ => return,
|
_ => return,
|
||||||
};
|
};
|
||||||
let import = |img: &VulkanImage, flag: u32| {
|
let import =
|
||||||
if let VulkanImageMemory::DmaBuf(buf) = &img.ty {
|
|img: &VulkanImage, sync: ReleaseSync, resv: Option<Rc<dyn BufferResv>>, flag: u32| {
|
||||||
for plane in &buf.template.dmabuf.planes {
|
if sync == ReleaseSync::None {
|
||||||
let res = dma_buf_import_sync_file(&plane.fd, flag, &syncfile)
|
return;
|
||||||
.map_err(VulkanError::IoctlImportSyncFile);
|
}
|
||||||
if let Err(e) = res {
|
if let Some(resv) = resv {
|
||||||
log::error!("Could not import syncfile into dmabuf: {}", ErrorFmt(e));
|
resv.set_sync_file(self.buffer_resv_user, sync_file);
|
||||||
log::warn!("Relying on implicit sync");
|
} else if sync == ReleaseSync::Implicit {
|
||||||
|
if let VulkanImageMemory::DmaBuf(buf) = &img.ty {
|
||||||
|
if let Err(e) = buf.template.dmabuf.import_sync_file(flag, sync_file) {
|
||||||
|
log::error!("Could not import sync file into dmabuf: {}", ErrorFmt(e));
|
||||||
|
log::warn!("Relying on implicit sync");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
};
|
for texture in &mut memory.textures {
|
||||||
for texture in &memory.textures {
|
import(
|
||||||
import(texture, DMA_BUF_SYNC_WRITE);
|
&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> {
|
fn submit(&self, buf: CommandBuffer) -> Result<(), VulkanError> {
|
||||||
|
|
@ -610,15 +651,15 @@ impl VulkanRenderer {
|
||||||
)
|
)
|
||||||
.map_err(VulkanError::Submit)?;
|
.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),
|
Ok(s) => Some(s),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
log::error!("Could not export syncfile from fence: {}", ErrorFmt(e));
|
log::error!("Could not export sync file from fence: {}", ErrorFmt(e));
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
memory.release_fence = Some(release_fence);
|
memory.release_fence = Some(release_fence);
|
||||||
memory.release_syncfile = release_syncfile;
|
memory.release_sync_file = release_sync_file;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -650,7 +691,7 @@ impl VulkanRenderer {
|
||||||
});
|
});
|
||||||
self.pending_frames.set(frame.point, frame.clone());
|
self.pending_frames.set(frame.point, frame.clone());
|
||||||
let future = self.device.instance.eng.spawn(await_release(
|
let future = self.device.instance.eng.spawn(await_release(
|
||||||
memory.release_syncfile.take(),
|
memory.release_sync_file.clone(),
|
||||||
self.device.instance.ring.clone(),
|
self.device.instance.ring.clone(),
|
||||||
frame.clone(),
|
frame.clone(),
|
||||||
self.clone(),
|
self.clone(),
|
||||||
|
|
@ -692,7 +733,15 @@ impl VulkanRenderer {
|
||||||
&[],
|
&[],
|
||||||
true,
|
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)
|
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)
|
let fd = dma_buf_export_sync_file(&plane.fd, DMA_BUF_SYNC_READ)
|
||||||
.map_err(VulkanError::IoctlExportSyncFile)?;
|
.map_err(VulkanError::IoctlExportSyncFile)?;
|
||||||
let semaphore = self.allocate_semaphore()?;
|
let semaphore = self.allocate_semaphore()?;
|
||||||
semaphore.import_syncfile(fd)?;
|
semaphore.import_sync_file(fd)?;
|
||||||
let semaphore_info = SemaphoreSubmitInfo::builder()
|
let semaphore_info = SemaphoreSubmitInfo::builder()
|
||||||
.semaphore(semaphore.semaphore)
|
.semaphore(semaphore.semaphore)
|
||||||
.stage_mask(PipelineStageFlags2::TOP_OF_PIPE)
|
.stage_mask(PipelineStageFlags2::TOP_OF_PIPE)
|
||||||
|
|
@ -831,9 +880,9 @@ impl VulkanRenderer {
|
||||||
fb: &VulkanImage,
|
fb: &VulkanImage,
|
||||||
opts: &[GfxApiOpt],
|
opts: &[GfxApiOpt],
|
||||||
clear: Option<&Color>,
|
clear: Option<&Color>,
|
||||||
) -> Result<(), VulkanError> {
|
) -> Result<Option<SyncFile>, VulkanError> {
|
||||||
let res = self.try_execute(fb, opts, clear);
|
let res = self.try_execute(fb, opts, clear);
|
||||||
{
|
let sync_file = {
|
||||||
let mut memory = self.memory.borrow_mut();
|
let mut memory = self.memory.borrow_mut();
|
||||||
memory.flush.clear();
|
memory.flush.clear();
|
||||||
memory.textures.clear();
|
memory.textures.clear();
|
||||||
|
|
@ -841,9 +890,9 @@ impl VulkanRenderer {
|
||||||
memory.sample.clear();
|
memory.sample.clear();
|
||||||
memory.wait_semaphores.clear();
|
memory.wait_semaphores.clear();
|
||||||
memory.release_fence.take();
|
memory.release_fence.take();
|
||||||
memory.release_syncfile.take();
|
memory.release_sync_file.take()
|
||||||
}
|
};
|
||||||
res
|
res.map(|_| sync_file)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn allocate_command_buffer(&self) -> Result<Rc<VulkanCommandBuffer>, VulkanError> {
|
fn allocate_command_buffer(&self) -> Result<Rc<VulkanCommandBuffer>, VulkanError> {
|
||||||
|
|
@ -964,14 +1013,14 @@ fn image_barrier() -> ImageMemoryBarrier2Builder<'static> {
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn await_release(
|
async fn await_release(
|
||||||
syncfile: Option<Rc<OwnedFd>>,
|
sync_file: Option<SyncFile>,
|
||||||
ring: Rc<IoUring>,
|
ring: Rc<IoUring>,
|
||||||
frame: Rc<PendingFrame>,
|
frame: Rc<PendingFrame>,
|
||||||
renderer: Rc<VulkanRenderer>,
|
renderer: Rc<VulkanRenderer>,
|
||||||
) {
|
) {
|
||||||
let mut is_released = false;
|
let mut is_released = false;
|
||||||
if let Some(syncfile) = syncfile {
|
if let Some(sync_file) = sync_file {
|
||||||
if let Err(e) = ring.readable(&syncfile).await {
|
if let Err(e) = ring.readable(&sync_file).await {
|
||||||
log::error!(
|
log::error!(
|
||||||
"Could not wait for release semaphore to be signaled: {}",
|
"Could not wait for release semaphore to be signaled: {}",
|
||||||
ErrorFmt(e)
|
ErrorFmt(e)
|
||||||
|
|
|
||||||
|
|
@ -36,9 +36,9 @@ impl VulkanDevice {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl VulkanSemaphore {
|
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()
|
let fd_info = ImportSemaphoreFdInfoKHR::builder()
|
||||||
.fd(syncfile.raw())
|
.fd(sync_file.raw())
|
||||||
.flags(SemaphoreImportFlags::TEMPORARY)
|
.flags(SemaphoreImportFlags::TEMPORARY)
|
||||||
.handle_type(ExternalSemaphoreHandleTypeFlags::SYNC_FD)
|
.handle_type(ExternalSemaphoreHandleTypeFlags::SYNC_FD)
|
||||||
.semaphore(self.semaphore);
|
.semaphore(self.semaphore);
|
||||||
|
|
@ -47,8 +47,8 @@ impl VulkanSemaphore {
|
||||||
.external_semaphore_fd
|
.external_semaphore_fd
|
||||||
.import_semaphore_fd(&fd_info)
|
.import_semaphore_fd(&fd_info)
|
||||||
};
|
};
|
||||||
mem::forget(syncfile);
|
|
||||||
res.map_err(VulkanError::ImportSyncFile)?;
|
res.map_err(VulkanError::ImportSyncFile)?;
|
||||||
|
mem::forget(sync_file);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -38,6 +38,8 @@ pub mod wp_content_type_v1;
|
||||||
pub mod wp_cursor_shape_device_v1;
|
pub mod wp_cursor_shape_device_v1;
|
||||||
pub mod wp_cursor_shape_manager_v1;
|
pub mod wp_cursor_shape_manager_v1;
|
||||||
pub mod wp_fractional_scale_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;
|
||||||
pub mod wp_presentation_feedback;
|
pub mod wp_presentation_feedback;
|
||||||
pub mod wp_single_pixel_buffer_manager_v1;
|
pub mod wp_single_pixel_buffer_manager_v1;
|
||||||
|
|
|
||||||
|
|
@ -173,7 +173,7 @@ impl JayScreencast {
|
||||||
let mut buffer = self.buffers.borrow_mut();
|
let mut buffer = self.buffers.borrow_mut();
|
||||||
for (idx, buffer) in buffer.deref_mut().iter_mut().enumerate() {
|
for (idx, buffer) in buffer.deref_mut().iter_mut().enumerate() {
|
||||||
if buffer.free {
|
if buffer.free {
|
||||||
self.client.state.perform_screencopy(
|
let res = self.client.state.perform_screencopy(
|
||||||
texture,
|
texture,
|
||||||
&buffer.fb,
|
&buffer.fb,
|
||||||
on.global.pos.get(),
|
on.global.pos.get(),
|
||||||
|
|
@ -183,12 +183,20 @@ impl JayScreencast {
|
||||||
size,
|
size,
|
||||||
on.global.persistent.transform.get(),
|
on.global.persistent.transform.get(),
|
||||||
);
|
);
|
||||||
self.client.event(Ready {
|
match res {
|
||||||
self_id: self.id,
|
Ok(_) => {
|
||||||
idx: idx as _,
|
self.client.event(Ready {
|
||||||
});
|
self_id: self.id,
|
||||||
buffer.free = false;
|
idx: idx as _,
|
||||||
return;
|
});
|
||||||
|
buffer.free = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
log::error!("Could not perform screencopy: {}", ErrorFmt(e));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.missed_frame.set(true);
|
self.missed_frame.set(true);
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,7 @@ pub struct WlBuffer {
|
||||||
pub client: Rc<Client>,
|
pub client: Rc<Client>,
|
||||||
pub rect: Rect,
|
pub rect: Rect,
|
||||||
pub format: &'static Format,
|
pub format: &'static Format,
|
||||||
dmabuf: Option<DmaBuf>,
|
pub dmabuf: Option<DmaBuf>,
|
||||||
render_ctx_version: Cell<u32>,
|
render_ctx_version: Cell<u32>,
|
||||||
pub storage: RefCell<Option<WlBufferStorage>>,
|
pub storage: RefCell<Option<WlBufferStorage>>,
|
||||||
pub color: Option<Color>,
|
pub color: Option<Color>,
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ use {
|
||||||
buffd::{MsgParser, MsgParserError},
|
buffd::{MsgParser, MsgParserError},
|
||||||
clonecell::CloneCell,
|
clonecell::CloneCell,
|
||||||
copyhashmap::CopyHashMap,
|
copyhashmap::CopyHashMap,
|
||||||
|
errorfmt::ErrorFmt,
|
||||||
linkedlist::LinkedList,
|
linkedlist::LinkedList,
|
||||||
transform_ext::TransformExt,
|
transform_ext::TransformExt,
|
||||||
},
|
},
|
||||||
|
|
@ -243,7 +244,7 @@ impl WlOutputGlobal {
|
||||||
if let Some(WlBufferStorage::Shm { mem, stride }) =
|
if let Some(WlBufferStorage::Shm { mem, stride }) =
|
||||||
wl_buffer.storage.borrow_mut().deref()
|
wl_buffer.storage.borrow_mut().deref()
|
||||||
{
|
{
|
||||||
self.state.perform_shm_screencopy(
|
let res = self.state.perform_shm_screencopy(
|
||||||
tex,
|
tex,
|
||||||
self.pos.get(),
|
self.pos.get(),
|
||||||
x_off,
|
x_off,
|
||||||
|
|
@ -255,6 +256,11 @@ impl WlOutputGlobal {
|
||||||
wl_buffer.format,
|
wl_buffer.format,
|
||||||
Transform::None,
|
Transform::None,
|
||||||
);
|
);
|
||||||
|
if let Err(e) = res {
|
||||||
|
log::warn!("Could not perform shm screencopy: {}", ErrorFmt(e));
|
||||||
|
capture.send_failed();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
let fb = match wl_buffer.famebuffer.get() {
|
let fb = match wl_buffer.famebuffer.get() {
|
||||||
Some(fb) => fb,
|
Some(fb) => fb,
|
||||||
|
|
@ -264,7 +270,7 @@ impl WlOutputGlobal {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
self.state.perform_screencopy(
|
let res = self.state.perform_screencopy(
|
||||||
tex,
|
tex,
|
||||||
&fb,
|
&fb,
|
||||||
self.pos.get(),
|
self.pos.get(),
|
||||||
|
|
@ -274,6 +280,11 @@ impl WlOutputGlobal {
|
||||||
size,
|
size,
|
||||||
Transform::None,
|
Transform::None,
|
||||||
);
|
);
|
||||||
|
if let Err(e) = res {
|
||||||
|
log::warn!("Could not perform screencopy: {}", ErrorFmt(e));
|
||||||
|
capture.send_failed();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if capture.with_damage.get() {
|
if capture.with_damage.get() {
|
||||||
capture.send_damage();
|
capture.send_damage();
|
||||||
|
|
|
||||||
|
|
@ -299,13 +299,21 @@ impl WlSeatGlobal {
|
||||||
if extents.intersects(&Rect::new_sized(-x_rel, -y_rel, width, height).unwrap()) {
|
if extents.intersects(&Rect::new_sized(-x_rel, -y_rel, width, height).unwrap()) {
|
||||||
if render {
|
if render {
|
||||||
let buffer = hc.get_buffer();
|
let buffer = hc.get_buffer();
|
||||||
buffer.render_hardware_cursor(
|
let res = buffer.render_hardware_cursor(
|
||||||
cursor.deref(),
|
cursor.deref(),
|
||||||
&self.state,
|
&self.state,
|
||||||
scale,
|
scale,
|
||||||
transform,
|
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);
|
hc.set_enabled(true);
|
||||||
let mode = output.global.mode.get();
|
let mode = output.global.mode.get();
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,9 @@
|
||||||
|
pub mod commit_timeline;
|
||||||
pub mod cursor;
|
pub mod cursor;
|
||||||
pub mod ext_session_lock_surface_v1;
|
pub mod ext_session_lock_surface_v1;
|
||||||
pub mod wl_subsurface;
|
pub mod wl_subsurface;
|
||||||
pub mod wp_fractional_scale_v1;
|
pub mod wp_fractional_scale_v1;
|
||||||
|
pub mod wp_linux_drm_syncobj_surface_v1;
|
||||||
pub mod wp_tearing_control_v1;
|
pub mod wp_tearing_control_v1;
|
||||||
pub mod wp_viewport;
|
pub mod wp_viewport;
|
||||||
pub mod x_surface;
|
pub mod x_surface;
|
||||||
|
|
@ -16,7 +18,7 @@ use {
|
||||||
client::{Client, ClientError, RequestParser},
|
client::{Client, ClientError, RequestParser},
|
||||||
drm_feedback::DrmFeedback,
|
drm_feedback::DrmFeedback,
|
||||||
fixed::Fixed,
|
fixed::Fixed,
|
||||||
gfx_api::SampleRect,
|
gfx_api::{AcquireSync, BufferResv, BufferResvUser, ReleaseSync, SampleRect, SyncFile},
|
||||||
ifs::{
|
ifs::{
|
||||||
wl_buffer::WlBuffer,
|
wl_buffer::WlBuffer,
|
||||||
wl_callback::WlCallback,
|
wl_callback::WlCallback,
|
||||||
|
|
@ -25,11 +27,16 @@ use {
|
||||||
NodeSeatState, SeatId, WlSeatGlobal,
|
NodeSeatState, SeatId, WlSeatGlobal,
|
||||||
},
|
},
|
||||||
wl_surface::{
|
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_fractional_scale_v1::WpFractionalScaleV1,
|
||||||
wp_tearing_control_v1::WpTearingControlV1, wp_viewport::WpViewport,
|
wp_linux_drm_syncobj_surface_v1::WpLinuxDrmSyncobjSurfaceV1,
|
||||||
x_surface::XSurface, xdg_surface::XdgSurfaceError,
|
wp_tearing_control_v1::WpTearingControlV1,
|
||||||
zwlr_layer_surface_v1::ZwlrLayerSurfaceV1Error,
|
wp_viewport::WpViewport,
|
||||||
|
x_surface::XSurface,
|
||||||
|
xdg_surface::{PendingXdgSurfaceData, XdgSurfaceError},
|
||||||
|
zwlr_layer_surface_v1::{PendingLayerSurfaceData, ZwlrLayerSurfaceV1Error},
|
||||||
},
|
},
|
||||||
wp_content_type_v1::ContentType,
|
wp_content_type_v1::ContentType,
|
||||||
wp_presentation_feedback::WpPresentationFeedback,
|
wp_presentation_feedback::WpPresentationFeedback,
|
||||||
|
|
@ -48,11 +55,16 @@ use {
|
||||||
cell_ext::CellExt,
|
cell_ext::CellExt,
|
||||||
clonecell::CloneCell,
|
clonecell::CloneCell,
|
||||||
copyhashmap::CopyHashMap,
|
copyhashmap::CopyHashMap,
|
||||||
|
errorfmt::ErrorFmt,
|
||||||
linkedlist::LinkedList,
|
linkedlist::LinkedList,
|
||||||
numcell::NumCell,
|
numcell::NumCell,
|
||||||
smallmap::SmallMap,
|
smallmap::SmallMap,
|
||||||
transform_ext::TransformExt,
|
transform_ext::TransformExt,
|
||||||
},
|
},
|
||||||
|
video::{
|
||||||
|
dmabuf::DMA_BUF_SYNC_READ,
|
||||||
|
drm::sync_obj::{SyncObj, SyncObjPoint},
|
||||||
|
},
|
||||||
wire::{
|
wire::{
|
||||||
wl_surface::*, WlOutputId, WlSurfaceId, ZwpIdleInhibitorV1Id,
|
wl_surface::*, WlOutputId, WlSurfaceId, ZwpIdleInhibitorV1Id,
|
||||||
ZwpLinuxDmabufFeedbackV1Id,
|
ZwpLinuxDmabufFeedbackV1Id,
|
||||||
|
|
@ -61,9 +73,11 @@ use {
|
||||||
xwayland::XWaylandEvent,
|
xwayland::XWaylandEvent,
|
||||||
},
|
},
|
||||||
ahash::AHashMap,
|
ahash::AHashMap,
|
||||||
|
isnt::std_1::primitive::IsntSliceExt,
|
||||||
jay_config::video::Transform,
|
jay_config::video::Transform,
|
||||||
std::{
|
std::{
|
||||||
cell::{Cell, RefCell},
|
cell::{Cell, RefCell},
|
||||||
|
collections::hash_map::{Entry, OccupiedEntry},
|
||||||
fmt::{Debug, Formatter},
|
fmt::{Debug, Formatter},
|
||||||
mem,
|
mem,
|
||||||
ops::{Deref, DerefMut},
|
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 struct WlSurface {
|
||||||
pub id: WlSurfaceId,
|
pub id: WlSurfaceId,
|
||||||
pub node_id: SurfaceNodeId,
|
pub node_id: SurfaceNodeId,
|
||||||
pub client: Rc<Client>,
|
pub client: Rc<Client>,
|
||||||
visible: Cell<bool>,
|
visible: Cell<bool>,
|
||||||
role: Cell<SurfaceRole>,
|
role: Cell<SurfaceRole>,
|
||||||
pending: PendingState,
|
pending: RefCell<Box<PendingState>>,
|
||||||
input_region: CloneCell<Option<Rc<Region>>>,
|
input_region: CloneCell<Option<Rc<Region>>>,
|
||||||
opaque_region: Cell<Option<Rc<Region>>>,
|
opaque_region: Cell<Option<Rc<Region>>>,
|
||||||
buffer_points: RefCell<BufferPoints>,
|
buffer_points: RefCell<BufferPoints>,
|
||||||
|
|
@ -145,7 +224,7 @@ pub struct WlSurface {
|
||||||
pub extents: Cell<Rect>,
|
pub extents: Cell<Rect>,
|
||||||
pub buffer_abs_pos: Cell<Rect>,
|
pub buffer_abs_pos: Cell<Rect>,
|
||||||
pub need_extents_update: Cell<bool>,
|
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_x: NumCell<i32>,
|
||||||
pub buf_y: NumCell<i32>,
|
pub buf_y: NumCell<i32>,
|
||||||
pub children: RefCell<Option<Box<ParentData>>>,
|
pub children: RefCell<Option<Box<ParentData>>>,
|
||||||
|
|
@ -169,6 +248,9 @@ pub struct WlSurface {
|
||||||
pub has_content_type_manager: Cell<bool>,
|
pub has_content_type_manager: Cell<bool>,
|
||||||
content_type: Cell<Option<ContentType>>,
|
content_type: Cell<Option<ContentType>>,
|
||||||
pub drm_feedback: CopyHashMap<ZwpLinuxDmabufFeedbackV1Id, Rc<ZwpLinuxDmabufFeedbackV1>>,
|
pub drm_feedback: CopyHashMap<ZwpLinuxDmabufFeedbackV1Id, Rc<ZwpLinuxDmabufFeedbackV1>>,
|
||||||
|
sync_obj_surface: CloneCell<Option<Rc<WpLinuxDrmSyncobjSurfaceV1>>>,
|
||||||
|
destroyed: Cell<bool>,
|
||||||
|
commit_timeline: CommitTimeline,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Debug for WlSurface {
|
impl Debug for WlSurface {
|
||||||
|
|
@ -185,12 +267,6 @@ struct BufferPoints {
|
||||||
y2: f32,
|
y2: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
|
||||||
enum CommitContext {
|
|
||||||
RootCommit,
|
|
||||||
ChildCommit,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||||
enum CommitAction {
|
enum CommitAction {
|
||||||
ContinueCommit,
|
ContinueCommit,
|
||||||
|
|
@ -198,13 +274,21 @@ enum CommitAction {
|
||||||
}
|
}
|
||||||
|
|
||||||
trait SurfaceExt {
|
trait SurfaceExt {
|
||||||
fn pre_commit(self: Rc<Self>, ctx: CommitContext) -> Result<CommitAction, WlSurfaceError> {
|
fn commit_requested(self: Rc<Self>, pending: &mut Box<PendingState>) -> CommitAction {
|
||||||
let _ = ctx;
|
let _ = pending;
|
||||||
Ok(CommitAction::ContinueCommit)
|
CommitAction::ContinueCommit
|
||||||
}
|
}
|
||||||
|
|
||||||
fn post_commit(self: Rc<Self>) {
|
fn before_apply_commit(
|
||||||
// nothing
|
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 {
|
fn is_some(&self) -> bool {
|
||||||
|
|
@ -246,6 +330,17 @@ trait SurfaceExt {
|
||||||
fn into_xsurface(self: Rc<Self>) -> Option<Rc<XSurface>> {
|
fn into_xsurface(self: Rc<Self>) -> Option<Rc<XSurface>> {
|
||||||
None
|
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;
|
pub struct NoneSurfaceExt;
|
||||||
|
|
@ -258,20 +353,122 @@ impl SurfaceExt for NoneSurfaceExt {
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
struct PendingState {
|
struct PendingState {
|
||||||
buffer: Cell<Option<Option<Rc<WlBuffer>>>>,
|
buffer: Option<Option<Rc<WlBuffer>>>,
|
||||||
offset: Cell<(i32, i32)>,
|
offset: (i32, i32),
|
||||||
opaque_region: Cell<Option<Option<Rc<Region>>>>,
|
opaque_region: Option<Option<Rc<Region>>>,
|
||||||
input_region: Cell<Option<Option<Rc<Region>>>>,
|
input_region: Option<Option<Rc<Region>>>,
|
||||||
frame_request: RefCell<Vec<Rc<WlCallback>>>,
|
frame_request: Vec<Rc<WlCallback>>,
|
||||||
damage: Cell<bool>,
|
damage: bool,
|
||||||
presentation_feedback: RefCell<Vec<Rc<WpPresentationFeedback>>>,
|
presentation_feedback: Vec<Rc<WpPresentationFeedback>>,
|
||||||
src_rect: Cell<Option<Option<[Fixed; 4]>>>,
|
src_rect: Option<Option<[Fixed; 4]>>,
|
||||||
dst_size: Cell<Option<Option<(i32, i32)>>>,
|
dst_size: Option<Option<(i32, i32)>>,
|
||||||
scale: Cell<Option<i32>>,
|
scale: Option<i32>,
|
||||||
transform: Cell<Option<Transform>>,
|
transform: Option<Transform>,
|
||||||
xwayland_serial: Cell<Option<u64>>,
|
xwayland_serial: Option<u64>,
|
||||||
tearing: Cell<Option<bool>>,
|
tearing: Option<bool>,
|
||||||
content_type: Cell<Option<Option<ContentType>>>,
|
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)]
|
#[derive(Default)]
|
||||||
|
|
@ -330,6 +527,9 @@ impl WlSurface {
|
||||||
has_content_type_manager: Default::default(),
|
has_content_type_manager: Default::default(),
|
||||||
content_type: Default::default(),
|
content_type: Default::default(),
|
||||||
drm_feedback: 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>) {
|
pub fn add_presentation_feedback(&self, fb: &Rc<WpPresentationFeedback>) {
|
||||||
self.pending
|
self.pending
|
||||||
.presentation_feedback
|
|
||||||
.borrow_mut()
|
.borrow_mut()
|
||||||
|
.presentation_feedback
|
||||||
.push(fb.clone());
|
.push(fb.clone());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -558,6 +758,7 @@ impl WlSurface {
|
||||||
|
|
||||||
fn destroy(&self, parser: MsgParser<'_, '_>) -> Result<(), WlSurfaceError> {
|
fn destroy(&self, parser: MsgParser<'_, '_>) -> Result<(), WlSurfaceError> {
|
||||||
let _req: Destroy = self.parse(parser)?;
|
let _req: Destroy = self.parse(parser)?;
|
||||||
|
self.commit_timeline.clear(ClearReason::Destroy);
|
||||||
self.unset_dnd_icons();
|
self.unset_dnd_icons();
|
||||||
self.unset_cursors();
|
self.unset_cursors();
|
||||||
self.ext.get().on_surface_destroy()?;
|
self.ext.get().on_surface_destroy()?;
|
||||||
|
|
@ -571,11 +772,7 @@ impl WlSurface {
|
||||||
}
|
}
|
||||||
*children = None;
|
*children = None;
|
||||||
}
|
}
|
||||||
if let Some(buffer) = self.buffer.set(None) {
|
self.buffer.set(None);
|
||||||
if !buffer.destroyed() {
|
|
||||||
buffer.send_release();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if let Some(xwayland_serial) = self.xwayland_serial.get() {
|
if let Some(xwayland_serial) = self.xwayland_serial.get() {
|
||||||
self.client
|
self.client
|
||||||
.surfaces_by_xwayland_serial
|
.surfaces_by_xwayland_serial
|
||||||
|
|
@ -586,30 +783,32 @@ impl WlSurface {
|
||||||
self.client.remove_obj(self)?;
|
self.client.remove_obj(self)?;
|
||||||
self.idle_inhibitors.clear();
|
self.idle_inhibitors.clear();
|
||||||
self.constraints.take();
|
self.constraints.take();
|
||||||
|
self.destroyed.set(true);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn attach(self: &Rc<Self>, parser: MsgParser<'_, '_>) -> Result<(), WlSurfaceError> {
|
fn attach(self: &Rc<Self>, parser: MsgParser<'_, '_>) -> Result<(), WlSurfaceError> {
|
||||||
let req: Attach = self.parse(parser)?;
|
let req: Attach = self.parse(parser)?;
|
||||||
|
let pending = &mut *self.pending.borrow_mut();
|
||||||
if self.version >= OFFSET_SINCE {
|
if self.version >= OFFSET_SINCE {
|
||||||
if req.x != 0 || req.y != 0 {
|
if req.x != 0 || req.y != 0 {
|
||||||
return Err(WlSurfaceError::OffsetInAttach);
|
return Err(WlSurfaceError::OffsetInAttach);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
self.pending.offset.set((req.x, req.y));
|
pending.offset = (req.x, req.y);
|
||||||
}
|
}
|
||||||
let buf = if req.buffer.is_some() {
|
let buf = if req.buffer.is_some() {
|
||||||
Some(self.client.lookup(req.buffer)?)
|
Some(self.client.lookup(req.buffer)?)
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
self.pending.buffer.set(Some(buf));
|
pending.buffer = Some(buf);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn damage(&self, parser: MsgParser<'_, '_>) -> Result<(), WlSurfaceError> {
|
fn damage(&self, parser: MsgParser<'_, '_>) -> Result<(), WlSurfaceError> {
|
||||||
let _req: Damage = self.parse(parser)?;
|
let _req: Damage = self.parse(parser)?;
|
||||||
self.pending.damage.set(true);
|
self.pending.borrow_mut().damage = true;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -618,7 +817,7 @@ impl WlSurface {
|
||||||
let cb = Rc::new(WlCallback::new(req.callback, &self.client));
|
let cb = Rc::new(WlCallback::new(req.callback, &self.client));
|
||||||
track!(self.client, cb);
|
track!(self.client, cb);
|
||||||
self.client.add_client_obj(&cb)?;
|
self.client.add_client_obj(&cb)?;
|
||||||
self.pending.frame_request.borrow_mut().push(cb);
|
self.pending.borrow_mut().frame_request.push(cb);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -629,7 +828,7 @@ impl WlSurface {
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
self.pending.opaque_region.set(Some(region));
|
self.pending.borrow_mut().opaque_region = Some(region);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -640,39 +839,37 @@ impl WlSurface {
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
self.pending.input_region.set(Some(region));
|
self.pending.borrow_mut().input_region = Some(region);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn do_commit(self: &Rc<Self>, ctx: CommitContext) -> Result<(), WlSurfaceError> {
|
fn apply_state(self: &Rc<Self>, pending: &mut PendingState) -> Result<(), WlSurfaceError> {
|
||||||
let ext = self.ext.get();
|
for (_, mut subsurface) in pending.subsurfaces.drain() {
|
||||||
if ext.clone().pre_commit(ctx)? == CommitAction::AbortCommit {
|
subsurface
|
||||||
|
.subsurface
|
||||||
|
.surface
|
||||||
|
.apply_state(&mut subsurface.state)?;
|
||||||
|
}
|
||||||
|
if self.destroyed.get() {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
{
|
self.ext.get().before_apply_commit(pending)?;
|
||||||
let children = self.children.borrow();
|
|
||||||
if let Some(children) = children.deref() {
|
|
||||||
for child in children.subsurfaces.values() {
|
|
||||||
child.surface.do_commit(CommitContext::ChildCommit)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let mut scale_changed = false;
|
let mut scale_changed = false;
|
||||||
if let Some(scale) = self.pending.scale.take() {
|
if let Some(scale) = pending.scale.take() {
|
||||||
scale_changed = true;
|
scale_changed = true;
|
||||||
self.buffer_scale.set(scale);
|
self.buffer_scale.set(scale);
|
||||||
}
|
}
|
||||||
let mut buffer_transform_changed = false;
|
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;
|
buffer_transform_changed = true;
|
||||||
self.buffer_transform.set(transform);
|
self.buffer_transform.set(transform);
|
||||||
}
|
}
|
||||||
let mut viewport_changed = false;
|
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;
|
viewport_changed = true;
|
||||||
self.dst_size.set(dst_size);
|
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;
|
viewport_changed = true;
|
||||||
self.src_rect.set(src_rect);
|
self.src_rect.set(src_rect);
|
||||||
}
|
}
|
||||||
|
|
@ -687,34 +884,30 @@ impl WlSurface {
|
||||||
}
|
}
|
||||||
let mut buffer_changed = false;
|
let mut buffer_changed = false;
|
||||||
let mut old_raw_size = None;
|
let mut old_raw_size = None;
|
||||||
let (dx, dy) = self.pending.offset.take();
|
let (dx, dy) = mem::take(&mut pending.offset);
|
||||||
if let Some(buffer_change) = self.pending.buffer.take() {
|
if let Some(buffer_change) = pending.buffer.take() {
|
||||||
buffer_changed = true;
|
buffer_changed = true;
|
||||||
if let Some(buffer) = self.buffer.take() {
|
if let Some(buffer) = self.buffer.take() {
|
||||||
old_raw_size = Some(buffer.rect);
|
old_raw_size = Some(buffer.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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if let Some(buffer) = buffer_change {
|
if let Some(buffer) = buffer_change {
|
||||||
buffer.update_texture_or_log();
|
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_x.fetch_add(dx);
|
||||||
self.buf_y.fetch_add(dy);
|
self.buf_y.fetch_add(dy);
|
||||||
if (dx, dy) != (0, 0) {
|
if (dx, dy) != (0, 0) {
|
||||||
|
|
@ -764,8 +957,10 @@ impl WlSurface {
|
||||||
}
|
}
|
||||||
if let Some(buffer) = self.buffer.get() {
|
if let Some(buffer) = self.buffer.get() {
|
||||||
if new_size.is_none() {
|
if new_size.is_none() {
|
||||||
let (mut width, mut height) =
|
let (mut width, mut height) = self
|
||||||
self.buffer_transform.get().maybe_swap(buffer.rect.size());
|
.buffer_transform
|
||||||
|
.get()
|
||||||
|
.maybe_swap(buffer.buffer.rect.size());
|
||||||
let scale = self.buffer_scale.get();
|
let scale = self.buffer_scale.get();
|
||||||
if scale != 1 {
|
if scale != 1 {
|
||||||
width = (width + scale - 1) / scale;
|
width = (width + scale - 1) / scale;
|
||||||
|
|
@ -773,12 +968,14 @@ impl WlSurface {
|
||||||
}
|
}
|
||||||
new_size = Some((width, height));
|
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() {
|
let (x1, y1, x2, y2) = if self.src_rect.is_none() {
|
||||||
(0.0, 0.0, 1.0, 1.0)
|
(0.0, 0.0, 1.0, 1.0)
|
||||||
} else {
|
} else {
|
||||||
let (width, height) =
|
let (width, height) = self
|
||||||
self.buffer_transform.get().maybe_swap(buffer.rect.size());
|
.buffer_transform
|
||||||
|
.get()
|
||||||
|
.maybe_swap(buffer.buffer.rect.size());
|
||||||
let width = width as f32;
|
let width = width as f32;
|
||||||
let height = height as f32;
|
let height = height as f32;
|
||||||
let x1 = buffer_points.x1 / width;
|
let x1 = buffer_points.x1 / width;
|
||||||
|
|
@ -806,34 +1003,32 @@ impl WlSurface {
|
||||||
self.buffer_abs_pos
|
self.buffer_abs_pos
|
||||||
.set(self.buffer_abs_pos.get().with_size(width, height).unwrap());
|
.set(self.buffer_abs_pos.get().with_size(width, height).unwrap());
|
||||||
}
|
}
|
||||||
{
|
self.frame_requests
|
||||||
let mut pfr = self.pending.frame_request.borrow_mut();
|
.borrow_mut()
|
||||||
self.frame_requests.borrow_mut().extend(pfr.drain(..));
|
.extend(pending.frame_request.drain(..));
|
||||||
}
|
|
||||||
{
|
{
|
||||||
let mut fbs = self.presentation_feedback.borrow_mut();
|
let mut fbs = self.presentation_feedback.borrow_mut();
|
||||||
for fb in fbs.drain(..) {
|
for fb in fbs.drain(..) {
|
||||||
fb.send_discarded();
|
fb.send_discarded();
|
||||||
let _ = self.client.remove_obj(&*fb);
|
let _ = self.client.remove_obj(&*fb);
|
||||||
}
|
}
|
||||||
let mut pfbs = self.pending.presentation_feedback.borrow_mut();
|
mem::swap(fbs.deref_mut(), &mut pending.presentation_feedback);
|
||||||
mem::swap(fbs.deref_mut(), pfbs.deref_mut());
|
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
if let Some(region) = self.pending.input_region.take() {
|
if let Some(region) = pending.input_region.take() {
|
||||||
self.input_region.set(region);
|
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);
|
self.opaque_region.set(region);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let Some(tearing) = self.pending.tearing.take() {
|
if let Some(tearing) = pending.tearing.take() {
|
||||||
self.tearing.set(tearing);
|
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);
|
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.xwayland_serial.set(Some(xwayland_serial));
|
||||||
self.client
|
self.client
|
||||||
.surfaces_by_xwayland_serial
|
.surfaces_by_xwayland_serial
|
||||||
|
|
@ -853,23 +1048,49 @@ impl WlSurface {
|
||||||
cursor.update_hardware_cursor();
|
cursor.update_hardware_cursor();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ext.post_commit();
|
self.ext.get().after_apply_commit(pending);
|
||||||
self.client.state.damage();
|
self.client.state.damage();
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn commit(self: &Rc<Self>, parser: MsgParser<'_, '_>) -> Result<(), WlSurfaceError> {
|
fn commit(self: &Rc<Self>, parser: MsgParser<'_, '_>) -> Result<(), WlSurfaceError> {
|
||||||
let _req: Commit = self.parse(parser)?;
|
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(())
|
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> {
|
fn set_buffer_transform(&self, parser: MsgParser<'_, '_>) -> Result<(), WlSurfaceError> {
|
||||||
let req: SetBufferTransform = self.parse(parser)?;
|
let req: SetBufferTransform = self.parse(parser)?;
|
||||||
let Some(tf) = Transform::from_wl(req.transform) else {
|
let Some(tf) = Transform::from_wl(req.transform) else {
|
||||||
return Err(WlSurfaceError::UnknownBufferTransform(req.transform));
|
return Err(WlSurfaceError::UnknownBufferTransform(req.transform));
|
||||||
};
|
};
|
||||||
self.pending.transform.set(Some(tf));
|
self.pending.borrow_mut().transform = Some(tf);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -878,19 +1099,19 @@ impl WlSurface {
|
||||||
if req.scale < 1 {
|
if req.scale < 1 {
|
||||||
return Err(WlSurfaceError::NonPositiveBufferScale);
|
return Err(WlSurfaceError::NonPositiveBufferScale);
|
||||||
}
|
}
|
||||||
self.pending.scale.set(Some(req.scale));
|
self.pending.borrow_mut().scale = Some(req.scale);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn damage_buffer(&self, parser: MsgParser<'_, '_>) -> Result<(), WlSurfaceError> {
|
fn damage_buffer(&self, parser: MsgParser<'_, '_>) -> Result<(), WlSurfaceError> {
|
||||||
let _req: DamageBuffer = self.parse(parser)?;
|
let _req: DamageBuffer = self.parse(parser)?;
|
||||||
self.pending.damage.set(true);
|
self.pending.borrow_mut().damage = true;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn offset(&self, parser: MsgParser<'_, '_>) -> Result<(), WlSurfaceError> {
|
fn offset(&self, parser: MsgParser<'_, '_>) -> Result<(), WlSurfaceError> {
|
||||||
let req: Offset = self.parse(parser)?;
|
let req: Offset = self.parse(parser)?;
|
||||||
self.pending.offset.set((req.x, req.y));
|
self.pending.borrow_mut().offset = (req.x, req.y);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1021,7 +1242,7 @@ impl WlSurface {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_content_type(&self, content_type: Option<ContentType>) {
|
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) {
|
pub fn request_activation(&self) {
|
||||||
|
|
@ -1035,6 +1256,18 @@ impl WlSurface {
|
||||||
consumer.send_feedback(fb);
|
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! {
|
object_base! {
|
||||||
|
|
@ -1064,13 +1297,14 @@ impl Object for WlSurface {
|
||||||
self.buffer.set(None);
|
self.buffer.set(None);
|
||||||
self.toplevel.set(None);
|
self.toplevel.set(None);
|
||||||
self.idle_inhibitors.clear();
|
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.presentation_feedback.borrow_mut().clear();
|
||||||
self.viewporter.take();
|
self.viewporter.take();
|
||||||
self.fractional_scale.take();
|
self.fractional_scale.take();
|
||||||
self.tearing_control.take();
|
self.tearing_control.take();
|
||||||
self.constraints.clear();
|
self.constraints.clear();
|
||||||
self.drm_feedback.clear();
|
self.drm_feedback.clear();
|
||||||
|
self.commit_timeline.clear(ClearReason::BreakLoops);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1237,8 +1471,15 @@ pub enum WlSurfaceError {
|
||||||
ViewportOutsideBuffer,
|
ViewportOutsideBuffer,
|
||||||
#[error("attach request must not contain offset")]
|
#[error("attach request must not contain offset")]
|
||||||
OffsetInAttach,
|
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, ClientError);
|
||||||
efrom!(WlSurfaceError, XdgSurfaceError);
|
efrom!(WlSurfaceError, XdgSurfaceError);
|
||||||
efrom!(WlSurfaceError, ZwlrLayerSurfaceV1Error);
|
efrom!(WlSurfaceError, ZwlrLayerSurfaceV1Error);
|
||||||
efrom!(WlSurfaceError, MsgParserError);
|
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::{
|
crate::{
|
||||||
client::ClientError,
|
client::ClientError,
|
||||||
ifs::wl_surface::{
|
ifs::wl_surface::{
|
||||||
CommitAction, CommitContext, StackElement, SurfaceExt, SurfaceRole, WlSurface,
|
CommitAction, CommittedSubsurface, PendingState, StackElement, SurfaceExt, SurfaceRole,
|
||||||
WlSurfaceError, WlSurfaceId,
|
WlSurface, WlSurfaceError, WlSurfaceId,
|
||||||
},
|
},
|
||||||
leaks::Tracker,
|
leaks::Tracker,
|
||||||
object::Object,
|
object::Object,
|
||||||
rect::Rect,
|
rect::Rect,
|
||||||
utils::{
|
utils::{
|
||||||
buffd::{MsgParser, MsgParserError},
|
buffd::{MsgParser, MsgParserError},
|
||||||
linkedlist::LinkedNode,
|
clonecell::CloneCell,
|
||||||
|
linkedlist::{LinkedNode, NodeRef},
|
||||||
numcell::NumCell,
|
numcell::NumCell,
|
||||||
|
option_ext::OptionExt,
|
||||||
},
|
},
|
||||||
wire::{wl_subsurface::*, WlSubsurfaceId},
|
wire::{wl_subsurface::*, WlSubsurfaceId},
|
||||||
},
|
},
|
||||||
std::{
|
std::{
|
||||||
cell::{Cell, RefCell},
|
cell::{Cell, RefCell, RefMut},
|
||||||
|
collections::hash_map::{Entry, OccupiedEntry},
|
||||||
|
mem,
|
||||||
ops::Deref,
|
ops::Deref,
|
||||||
rc::Rc,
|
rc::Rc,
|
||||||
},
|
},
|
||||||
|
|
@ -28,36 +32,39 @@ const BAD_SURFACE: u32 = 0;
|
||||||
|
|
||||||
const MAX_SUBSURFACE_DEPTH: u32 = 100;
|
const MAX_SUBSURFACE_DEPTH: u32 = 100;
|
||||||
|
|
||||||
|
linear_ids!(SubsurfaceIds, SubsurfaceId, u64);
|
||||||
|
|
||||||
pub struct WlSubsurface {
|
pub struct WlSubsurface {
|
||||||
id: WlSubsurfaceId,
|
id: WlSubsurfaceId,
|
||||||
|
unique_id: SubsurfaceId,
|
||||||
pub surface: Rc<WlSurface>,
|
pub surface: Rc<WlSurface>,
|
||||||
pub(super) parent: Rc<WlSurface>,
|
pub(super) parent: Rc<WlSurface>,
|
||||||
pub position: Cell<Rect>,
|
pub position: Cell<Rect>,
|
||||||
sync_requested: Cell<bool>,
|
sync_requested: Cell<bool>,
|
||||||
sync_ancestor: Cell<bool>,
|
sync_ancestor: Cell<bool>,
|
||||||
node: RefCell<Option<LinkedNode<StackElement>>>,
|
node: RefCell<Option<LinkedNode<StackElement>>>,
|
||||||
|
latest_node: CloneCell<Option<NodeRef<StackElement>>>,
|
||||||
depth: NumCell<u32>,
|
depth: NumCell<u32>,
|
||||||
pending: PendingSubsurfaceData,
|
|
||||||
pub tracker: Tracker<Self>,
|
pub tracker: Tracker<Self>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
struct PendingSubsurfaceData {
|
pub struct PendingSubsurfaceData {
|
||||||
node: RefCell<Option<LinkedNode<StackElement>>>,
|
node: Option<LinkedNode<StackElement>>,
|
||||||
position: Cell<Option<(i32, i32)>>,
|
position: Option<(i32, i32)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update_children_sync(surface: &WlSubsurface, sync: bool) {
|
impl PendingSubsurfaceData {
|
||||||
let children = surface.surface.children.borrow();
|
pub fn merge(&mut self, next: &mut Self) {
|
||||||
if let Some(children) = &*children {
|
macro_rules! opt {
|
||||||
for child in children.subsurfaces.values() {
|
($name:ident) => {
|
||||||
let was_sync = child.sync();
|
if let Some(n) = next.$name.take() {
|
||||||
child.sync_ancestor.set(sync);
|
self.$name = Some(n);
|
||||||
let is_sync = child.sync();
|
}
|
||||||
if was_sync != is_sync {
|
};
|
||||||
update_children_sync(child, sync);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
opt!(node);
|
||||||
|
opt!(position);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -85,18 +92,25 @@ impl WlSubsurface {
|
||||||
pub fn new(id: WlSubsurfaceId, surface: &Rc<WlSurface>, parent: &Rc<WlSurface>) -> Self {
|
pub fn new(id: WlSubsurfaceId, surface: &Rc<WlSurface>, parent: &Rc<WlSurface>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
id,
|
id,
|
||||||
|
unique_id: surface.client.state.subsurface_ids.next(),
|
||||||
surface: surface.clone(),
|
surface: surface.clone(),
|
||||||
parent: parent.clone(),
|
parent: parent.clone(),
|
||||||
position: Cell::new(Default::default()),
|
position: Cell::new(Default::default()),
|
||||||
sync_requested: Cell::new(false),
|
sync_requested: Cell::new(true),
|
||||||
sync_ancestor: Cell::new(false),
|
sync_ancestor: Cell::new(false),
|
||||||
node: RefCell::new(None),
|
node: RefCell::new(None),
|
||||||
|
latest_node: Default::default(),
|
||||||
depth: NumCell::new(0),
|
depth: NumCell::new(0),
|
||||||
pending: Default::default(),
|
|
||||||
tracker: 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> {
|
pub fn install(self: &Rc<Self>) -> Result<(), WlSubsurfaceError> {
|
||||||
if self.surface.id == self.parent.id {
|
if self.surface.id == self.parent.id {
|
||||||
return Err(WlSubsurfaceError::OwnParent(self.surface.id));
|
return Err(WlSubsurfaceError::OwnParent(self.surface.id));
|
||||||
|
|
@ -128,7 +142,8 @@ impl WlSubsurface {
|
||||||
sub_surface: self.clone(),
|
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.surface.set_toplevel(self.parent.toplevel.get());
|
||||||
self.sync_ancestor.set(sync_ancestor);
|
self.sync_ancestor.set(sync_ancestor);
|
||||||
self.depth.set(depth);
|
self.depth.set(depth);
|
||||||
|
|
@ -140,8 +155,12 @@ impl WlSubsurface {
|
||||||
fn destroy(&self, parser: MsgParser<'_, '_>) -> Result<(), WlSubsurfaceError> {
|
fn destroy(&self, parser: MsgParser<'_, '_>) -> Result<(), WlSubsurfaceError> {
|
||||||
let _req: Destroy = self.surface.client.parse(self, parser)?;
|
let _req: Destroy = self.surface.client.parse(self, parser)?;
|
||||||
self.surface.unset_ext();
|
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.node.borrow_mut() = None;
|
||||||
|
self.latest_node.take();
|
||||||
{
|
{
|
||||||
let mut children = self.parent.children.borrow_mut();
|
let mut children = self.parent.children.borrow_mut();
|
||||||
if let Some(children) = &mut *children {
|
if let Some(children) = &mut *children {
|
||||||
|
|
@ -164,7 +183,7 @@ impl WlSubsurface {
|
||||||
|
|
||||||
fn set_position(&self, parser: MsgParser<'_, '_>) -> Result<(), WlSubsurfaceError> {
|
fn set_position(&self, parser: MsgParser<'_, '_>) -> Result<(), WlSubsurfaceError> {
|
||||||
let req: SetPosition = self.surface.client.parse(self, parser)?;
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -188,19 +207,17 @@ impl WlSubsurface {
|
||||||
Some(s) => s,
|
Some(s) => s,
|
||||||
_ => return Err(WlSubsurfaceError::NotASibling(sibling, self.surface.id)),
|
_ => return Err(WlSubsurfaceError::NotASibling(sibling, self.surface.id)),
|
||||||
};
|
};
|
||||||
let node = match sibling.pending.node.borrow().deref() {
|
let sibling_node = match sibling.latest_node.get() {
|
||||||
Some(n) => n.to_ref(),
|
Some(n) => n,
|
||||||
_ => match sibling.node.borrow().deref() {
|
_ => return Ok(()),
|
||||||
Some(n) => n.to_ref(),
|
|
||||||
_ => return Ok(()),
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
match above {
|
match above {
|
||||||
true => node.append(element),
|
true => sibling_node.append(element),
|
||||||
_ => node.prepend(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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
@ -221,24 +238,56 @@ impl WlSubsurface {
|
||||||
self.sync_requested.get() || self.sync_ancestor.get()
|
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();
|
let was_sync = self.sync();
|
||||||
self.sync_requested.set(sync);
|
self.sync_requested.set(sync);
|
||||||
let is_sync = self.sync();
|
let is_sync = self.sync();
|
||||||
if was_sync != is_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> {
|
fn set_sync(&self, parser: MsgParser<'_, '_>) -> Result<(), WlSubsurfaceError> {
|
||||||
let _req: SetSync = self.surface.client.parse(self, parser)?;
|
let _req: SetSync = self.surface.client.parse(self, parser)?;
|
||||||
self.update_sync(true);
|
self.update_sync(true)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_desync(&self, parser: MsgParser<'_, '_>) -> Result<(), WlSubsurfaceError> {
|
fn set_desync(&self, parser: MsgParser<'_, '_>) -> Result<(), WlSubsurfaceError> {
|
||||||
let _req: SetDesync = self.surface.client.parse(self, parser)?;
|
let _req: SetDesync = self.surface.client.parse(self, parser)?;
|
||||||
self.update_sync(false);
|
self.update_sync(false)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -256,31 +305,44 @@ object_base! {
|
||||||
|
|
||||||
impl Object for WlSubsurface {
|
impl Object for WlSubsurface {
|
||||||
fn break_loops(&self) {
|
fn break_loops(&self) {
|
||||||
*self.pending.node.borrow_mut() = None;
|
|
||||||
*self.node.borrow_mut() = None;
|
*self.node.borrow_mut() = None;
|
||||||
|
self.latest_node.take();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
simple_add_obj!(WlSubsurface);
|
simple_add_obj!(WlSubsurface);
|
||||||
|
|
||||||
impl SurfaceExt for WlSubsurface {
|
impl SurfaceExt for WlSubsurface {
|
||||||
fn pre_commit(self: Rc<Self>, ctx: CommitContext) -> Result<CommitAction, WlSurfaceError> {
|
fn commit_requested(self: Rc<Self>, pending: &mut Box<PendingState>) -> CommitAction {
|
||||||
if ctx == CommitContext::RootCommit && self.sync() {
|
if self.sync() {
|
||||||
log::debug!("Aborting commit due to sync");
|
let mut parent_pending = self.parent.pending.borrow_mut();
|
||||||
return Ok(CommitAction::AbortCommit);
|
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>) {
|
fn after_apply_commit(self: Rc<Self>, pending: &mut PendingState) {
|
||||||
if let Some(v) = self.pending.node.take() {
|
if let Some(pending) = &mut pending.subsurface {
|
||||||
v.pending.set(false);
|
if let Some(v) = pending.node.take() {
|
||||||
self.node.borrow_mut().replace(v);
|
v.pending.set(false);
|
||||||
}
|
self.node.borrow_mut().replace(v);
|
||||||
if let Some((x, y)) = self.pending.position.take() {
|
}
|
||||||
self.position
|
if let Some((x, y)) = pending.position.take() {
|
||||||
.set(self.surface.buffer_abs_pos.get().at_point(x, y));
|
self.position
|
||||||
self.parent.need_extents_update.set(true);
|
.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>> {
|
fn into_subsurface(self: Rc<Self>) -> Option<Rc<WlSubsurface>> {
|
||||||
Some(self)
|
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)]
|
#[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,
|
ASYNC => true,
|
||||||
_ => return Err(WpTearingControlV1Error::UnknownPresentationHint(req.hint)),
|
_ => return Err(WpTearingControlV1Error::UnknownPresentationHint(req.hint)),
|
||||||
};
|
};
|
||||||
self.surface.pending.tearing.set(Some(tearing));
|
self.surface.pending.borrow_mut().tearing = Some(tearing);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn destroy(&self, parser: MsgParser<'_, '_>) -> Result<(), WpTearingControlV1Error> {
|
fn destroy(&self, parser: MsgParser<'_, '_>) -> Result<(), WpTearingControlV1Error> {
|
||||||
let _req: Destroy = self.surface.client.parse(self, parser)?;
|
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.tearing_control.take();
|
||||||
self.surface.client.remove_obj(self)?;
|
self.surface.client.remove_obj(self)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
||||||
|
|
@ -38,8 +38,9 @@ impl WpViewport {
|
||||||
|
|
||||||
fn destroy(&self, msg: MsgParser<'_, '_>) -> Result<(), WpViewportError> {
|
fn destroy(&self, msg: MsgParser<'_, '_>) -> Result<(), WpViewportError> {
|
||||||
let _req: Destroy = self.client.parse(self, msg)?;
|
let _req: Destroy = self.client.parse(self, msg)?;
|
||||||
self.surface.pending.src_rect.set(Some(None));
|
let pending = &mut *self.surface.pending.borrow_mut();
|
||||||
self.surface.pending.dst_size.set(Some(None));
|
pending.src_rect = Some(None);
|
||||||
|
pending.dst_size = Some(None);
|
||||||
self.surface.viewporter.take();
|
self.surface.viewporter.take();
|
||||||
self.client.remove_obj(self)?;
|
self.client.remove_obj(self)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
@ -56,7 +57,7 @@ impl WpViewport {
|
||||||
}
|
}
|
||||||
Some([req.x, req.y, req.width, req.height])
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -69,7 +70,7 @@ impl WpViewport {
|
||||||
} else {
|
} else {
|
||||||
Some((req.width, req.height))
|
Some((req.width, req.height))
|
||||||
};
|
};
|
||||||
self.surface.pending.dst_size.set(Some(size));
|
self.surface.pending.borrow_mut().dst_size = Some(size);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ use {
|
||||||
crate::{
|
crate::{
|
||||||
ifs::wl_surface::{
|
ifs::wl_surface::{
|
||||||
x_surface::{xwayland_surface_v1::XwaylandSurfaceV1, xwindow::Xwindow},
|
x_surface::{xwayland_surface_v1::XwaylandSurfaceV1, xwindow::Xwindow},
|
||||||
SurfaceExt, WlSurface, WlSurfaceError,
|
PendingState, SurfaceExt, WlSurface, WlSurfaceError,
|
||||||
},
|
},
|
||||||
leaks::Tracker,
|
leaks::Tracker,
|
||||||
tree::ToplevelNode,
|
tree::ToplevelNode,
|
||||||
|
|
@ -23,7 +23,7 @@ pub struct XSurface {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SurfaceExt for 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() {
|
if let Some(xwindow) = self.xwindow.get() {
|
||||||
xwindow.map_status_changed();
|
xwindow.map_status_changed();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,7 @@ impl XwaylandSurfaceV1 {
|
||||||
return Err(XwaylandSurfaceV1Error::NonMonotonicSerial);
|
return Err(XwaylandSurfaceV1Error::NonMonotonicSerial);
|
||||||
}
|
}
|
||||||
self.client.last_xwayland_serial.set(serial);
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ use {
|
||||||
xdg_popup::{XdgPopup, XdgPopupError},
|
xdg_popup::{XdgPopup, XdgPopupError},
|
||||||
xdg_toplevel::{XdgToplevel, WM_CAPABILITIES_SINCE},
|
xdg_toplevel::{XdgToplevel, WM_CAPABILITIES_SINCE},
|
||||||
},
|
},
|
||||||
CommitAction, CommitContext, SurfaceExt, SurfaceRole, WlSurface, WlSurfaceError,
|
PendingState, SurfaceExt, SurfaceRole, WlSurface, WlSurfaceError,
|
||||||
},
|
},
|
||||||
xdg_wm_base::XdgWmBase,
|
xdg_wm_base::XdgWmBase,
|
||||||
},
|
},
|
||||||
|
|
@ -23,10 +23,15 @@ use {
|
||||||
clonecell::CloneCell,
|
clonecell::CloneCell,
|
||||||
copyhashmap::CopyHashMap,
|
copyhashmap::CopyHashMap,
|
||||||
numcell::NumCell,
|
numcell::NumCell,
|
||||||
|
option_ext::OptionExt,
|
||||||
},
|
},
|
||||||
wire::{xdg_surface::*, WlSurfaceId, XdgPopupId, XdgSurfaceId},
|
wire::{xdg_surface::*, WlSurfaceId, XdgPopupId, XdgSurfaceId},
|
||||||
},
|
},
|
||||||
std::{cell::Cell, fmt::Debug, rc::Rc},
|
std::{
|
||||||
|
cell::{Cell, RefMut},
|
||||||
|
fmt::Debug,
|
||||||
|
rc::Rc,
|
||||||
|
},
|
||||||
thiserror::Error,
|
thiserror::Error,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -65,14 +70,26 @@ pub struct XdgSurface {
|
||||||
pub absolute_desired_extents: Cell<Rect>,
|
pub absolute_desired_extents: Cell<Rect>,
|
||||||
ext: CloneCell<Option<Rc<dyn XdgSurfaceExt>>>,
|
ext: CloneCell<Option<Rc<dyn XdgSurfaceExt>>>,
|
||||||
popups: CopyHashMap<XdgPopupId, Rc<XdgPopup>>,
|
popups: CopyHashMap<XdgPopupId, Rc<XdgPopup>>,
|
||||||
pending: PendingXdgSurfaceData,
|
|
||||||
pub workspace: CloneCell<Option<Rc<WorkspaceNode>>>,
|
pub workspace: CloneCell<Option<Rc<WorkspaceNode>>>,
|
||||||
pub tracker: Tracker<Self>,
|
pub tracker: Tracker<Self>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default, Debug)]
|
#[derive(Default, Debug)]
|
||||||
struct PendingXdgSurfaceData {
|
pub struct PendingXdgSurfaceData {
|
||||||
geometry: Cell<Option<Rect>>,
|
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 {
|
pub trait XdgSurfaceExt: Debug {
|
||||||
|
|
@ -103,7 +120,6 @@ impl XdgSurface {
|
||||||
absolute_desired_extents: Cell::new(Default::default()),
|
absolute_desired_extents: Cell::new(Default::default()),
|
||||||
ext: Default::default(),
|
ext: Default::default(),
|
||||||
popups: Default::default(),
|
popups: Default::default(),
|
||||||
pending: Default::default(),
|
|
||||||
workspace: Default::default(),
|
workspace: Default::default(),
|
||||||
tracker: Default::default(),
|
tracker: Default::default(),
|
||||||
}
|
}
|
||||||
|
|
@ -270,6 +286,12 @@ impl XdgSurface {
|
||||||
Ok(())
|
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> {
|
fn set_window_geometry(&self, parser: MsgParser<'_, '_>) -> Result<(), XdgSurfaceError> {
|
||||||
let req: SetWindowGeometry = self.surface.client.parse(self, parser)?;
|
let req: SetWindowGeometry = self.surface.client.parse(self, parser)?;
|
||||||
if req.height == 0 && req.width == 0 {
|
if req.height == 0 && req.width == 0 {
|
||||||
|
|
@ -280,7 +302,7 @@ impl XdgSurface {
|
||||||
return Err(XdgSurfaceError::NonPositiveWidthHeight);
|
return Err(XdgSurfaceError::NonPositiveWidthHeight);
|
||||||
}
|
}
|
||||||
let extents = Rect::new_sized(req.x, req.y, req.width, req.height).unwrap();
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -357,7 +379,10 @@ impl Object for XdgSurface {
|
||||||
dedicated_add_obj!(XdgSurface, XdgSurfaceId, xdg_surfaces);
|
dedicated_add_obj!(XdgSurface, XdgSurfaceId, xdg_surfaces);
|
||||||
|
|
||||||
impl SurfaceExt for XdgSurface {
|
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 ase = self.acked_serial.get();
|
||||||
let rse = self.requested_serial.get();
|
let rse = self.requested_serial.get();
|
||||||
|
|
@ -368,17 +393,18 @@ impl SurfaceExt for XdgSurface {
|
||||||
}
|
}
|
||||||
self.send_configure(rse);
|
self.send_configure(rse);
|
||||||
}
|
}
|
||||||
// return CommitAction::AbortCommit;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let Some(geometry) = self.pending.geometry.take() {
|
if let Some(pending) = &mut pending.xdg_surface {
|
||||||
self.geometry.set(Some(geometry));
|
if let Some(geometry) = pending.geometry.take() {
|
||||||
self.update_extents();
|
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() {
|
if let Some(ext) = self.ext.get() {
|
||||||
ext.post_commit();
|
ext.post_commit();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -646,7 +646,8 @@ impl ToplevelNodeBase for XdgToplevel {
|
||||||
|
|
||||||
impl XdgSurfaceExt for XdgToplevel {
|
impl XdgSurfaceExt for XdgToplevel {
|
||||||
fn initial_configure(self: Rc<Self>) -> Result<(), XdgSurfaceError> {
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,9 +3,7 @@ use {
|
||||||
client::{Client, ClientError},
|
client::{Client, ClientError},
|
||||||
ifs::{
|
ifs::{
|
||||||
wl_seat::NodeSeatState,
|
wl_seat::NodeSeatState,
|
||||||
wl_surface::{
|
wl_surface::{PendingState, SurfaceExt, SurfaceRole, WlSurface, WlSurfaceError},
|
||||||
CommitAction, CommitContext, SurfaceExt, SurfaceRole, WlSurface, WlSurfaceError,
|
|
||||||
},
|
|
||||||
zwlr_layer_shell_v1::{ZwlrLayerShellV1, OVERLAY},
|
zwlr_layer_shell_v1::{ZwlrLayerShellV1, OVERLAY},
|
||||||
},
|
},
|
||||||
leaks::Tracker,
|
leaks::Tracker,
|
||||||
|
|
@ -19,10 +17,16 @@ use {
|
||||||
cell_ext::CellExt,
|
cell_ext::CellExt,
|
||||||
linkedlist::LinkedNode,
|
linkedlist::LinkedNode,
|
||||||
numcell::NumCell,
|
numcell::NumCell,
|
||||||
|
option_ext::OptionExt,
|
||||||
},
|
},
|
||||||
wire::{zwlr_layer_surface_v1::*, WlSurfaceId, ZwlrLayerSurfaceV1Id},
|
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,
|
thiserror::Error,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -50,7 +54,6 @@ pub struct ZwlrLayerSurfaceV1 {
|
||||||
pos: Cell<Rect>,
|
pos: Cell<Rect>,
|
||||||
mapped: Cell<bool>,
|
mapped: Cell<bool>,
|
||||||
layer: Cell<u32>,
|
layer: Cell<u32>,
|
||||||
pending: Pending,
|
|
||||||
requested_serial: NumCell<u32>,
|
requested_serial: NumCell<u32>,
|
||||||
acked_serial: Cell<Option<u32>>,
|
acked_serial: Cell<Option<u32>>,
|
||||||
size: Cell<(i32, i32)>,
|
size: Cell<(i32, i32)>,
|
||||||
|
|
@ -63,14 +66,33 @@ pub struct ZwlrLayerSurfaceV1 {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
struct Pending {
|
pub struct PendingLayerSurfaceData {
|
||||||
size: Cell<Option<(i32, i32)>>,
|
size: Option<(i32, i32)>,
|
||||||
anchor: Cell<Option<u32>>,
|
anchor: Option<u32>,
|
||||||
exclusive_zone: Cell<Option<i32>>,
|
exclusive_zone: Option<i32>,
|
||||||
margin: Cell<Option<(i32, i32, i32, i32)>>,
|
margin: Option<(i32, i32, i32, i32)>,
|
||||||
keyboard_interactivity: Cell<Option<u32>>,
|
keyboard_interactivity: Option<u32>,
|
||||||
layer: Cell<Option<u32>>,
|
layer: Option<u32>,
|
||||||
any: Cell<bool>,
|
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 {
|
impl ZwlrLayerSurfaceV1 {
|
||||||
|
|
@ -95,7 +117,6 @@ impl ZwlrLayerSurfaceV1 {
|
||||||
pos: Default::default(),
|
pos: Default::default(),
|
||||||
mapped: Cell::new(false),
|
mapped: Cell::new(false),
|
||||||
layer: Cell::new(layer),
|
layer: Cell::new(layer),
|
||||||
pending: Default::default(),
|
|
||||||
requested_serial: Default::default(),
|
requested_serial: Default::default(),
|
||||||
acked_serial: Cell::new(None),
|
acked_serial: Cell::new(None),
|
||||||
size: Cell::new((0, 0)),
|
size: Cell::new((0, 0)),
|
||||||
|
|
@ -131,15 +152,20 @@ impl ZwlrLayerSurfaceV1 {
|
||||||
self.client.event(Closed { self_id: self.id });
|
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> {
|
fn set_size(&self, parser: MsgParser<'_, '_>) -> Result<(), ZwlrLayerSurfaceV1Error> {
|
||||||
let req: SetSize = self.client.parse(self, parser)?;
|
let req: SetSize = self.client.parse(self, parser)?;
|
||||||
if req.width > u16::MAX as u32 || req.height > u16::MAX as u32 {
|
if req.width > u16::MAX as u32 || req.height > u16::MAX as u32 {
|
||||||
return Err(ZwlrLayerSurfaceV1Error::ExcessiveSize);
|
return Err(ZwlrLayerSurfaceV1Error::ExcessiveSize);
|
||||||
}
|
}
|
||||||
self.pending
|
let mut pending = self.pending();
|
||||||
.size
|
pending.size = Some((req.width as _, req.height as _));
|
||||||
.set(Some((req.width as _, req.height as _)));
|
pending.any = true;
|
||||||
self.pending.any.set(true);
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -148,24 +174,25 @@ impl ZwlrLayerSurfaceV1 {
|
||||||
if req.anchor & !(LEFT | RIGHT | TOP | BOTTOM) != 0 {
|
if req.anchor & !(LEFT | RIGHT | TOP | BOTTOM) != 0 {
|
||||||
return Err(ZwlrLayerSurfaceV1Error::UnknownAnchor(req.anchor));
|
return Err(ZwlrLayerSurfaceV1Error::UnknownAnchor(req.anchor));
|
||||||
}
|
}
|
||||||
self.pending.anchor.set(Some(req.anchor));
|
let mut pending = self.pending();
|
||||||
self.pending.any.set(true);
|
pending.anchor = Some(req.anchor);
|
||||||
|
pending.any = true;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_exclusive_zone(&self, parser: MsgParser<'_, '_>) -> Result<(), ZwlrLayerSurfaceV1Error> {
|
fn set_exclusive_zone(&self, parser: MsgParser<'_, '_>) -> Result<(), ZwlrLayerSurfaceV1Error> {
|
||||||
let req: SetExclusiveZone = self.client.parse(self, parser)?;
|
let req: SetExclusiveZone = self.client.parse(self, parser)?;
|
||||||
self.pending.exclusive_zone.set(Some(req.zone));
|
let mut pending = self.pending();
|
||||||
self.pending.any.set(true);
|
pending.exclusive_zone = Some(req.zone);
|
||||||
|
pending.any = true;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_margin(&self, parser: MsgParser<'_, '_>) -> Result<(), ZwlrLayerSurfaceV1Error> {
|
fn set_margin(&self, parser: MsgParser<'_, '_>) -> Result<(), ZwlrLayerSurfaceV1Error> {
|
||||||
let req: SetMargin = self.client.parse(self, parser)?;
|
let req: SetMargin = self.client.parse(self, parser)?;
|
||||||
self.pending
|
let mut pending = self.pending();
|
||||||
.margin
|
pending.margin = Some((req.top, req.right, req.bottom, req.left));
|
||||||
.set(Some((req.top, req.right, req.bottom, req.left)));
|
pending.any = true;
|
||||||
self.pending.any.set(true);
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -179,10 +206,9 @@ impl ZwlrLayerSurfaceV1 {
|
||||||
req.keyboard_interactivity,
|
req.keyboard_interactivity,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
self.pending
|
let mut pending = self.pending();
|
||||||
.keyboard_interactivity
|
pending.keyboard_interactivity = Some(req.keyboard_interactivity);
|
||||||
.set(Some(req.keyboard_interactivity));
|
pending.any = true;
|
||||||
self.pending.any.set(true);
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -210,29 +236,31 @@ impl ZwlrLayerSurfaceV1 {
|
||||||
if req.layer > OVERLAY {
|
if req.layer > OVERLAY {
|
||||||
return Err(ZwlrLayerSurfaceV1Error::UnknownLayer(req.layer));
|
return Err(ZwlrLayerSurfaceV1Error::UnknownLayer(req.layer));
|
||||||
}
|
}
|
||||||
self.pending.layer.set(Some(req.layer));
|
let mut pending = self.pending();
|
||||||
self.pending.any.set(true);
|
pending.layer = Some(req.layer);
|
||||||
|
pending.any = true;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn pre_commit(&self) -> Result<(), ZwlrLayerSurfaceV1Error> {
|
fn pre_commit(&self, pending: &mut PendingState) -> Result<(), ZwlrLayerSurfaceV1Error> {
|
||||||
let mut send_configure = self.pending.any.replace(false);
|
let pending = pending.layer_surface.get_or_insert_default_ext();
|
||||||
if let Some(size) = self.pending.size.take() {
|
let mut send_configure = mem::replace(&mut pending.any, false);
|
||||||
|
if let Some(size) = pending.size.take() {
|
||||||
self.size.set(size);
|
self.size.set(size);
|
||||||
}
|
}
|
||||||
if let Some(anchor) = self.pending.anchor.take() {
|
if let Some(anchor) = pending.anchor.take() {
|
||||||
self.anchor.set(anchor);
|
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);
|
self.exclusive_zone.set(ez);
|
||||||
}
|
}
|
||||||
if let Some(margin) = self.pending.margin.take() {
|
if let Some(margin) = pending.margin.take() {
|
||||||
self.margin.set(margin);
|
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);
|
self.keyboard_interactivity.set(ki);
|
||||||
}
|
}
|
||||||
if let Some(layer) = self.pending.layer.take() {
|
if let Some(layer) = pending.layer.take() {
|
||||||
self.layer.set(layer);
|
self.layer.set(layer);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
|
|
@ -313,15 +341,18 @@ impl ZwlrLayerSurfaceV1 {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SurfaceExt for ZwlrLayerSurfaceV1 {
|
impl SurfaceExt for ZwlrLayerSurfaceV1 {
|
||||||
fn pre_commit(self: Rc<Self>, _ctx: CommitContext) -> Result<CommitAction, WlSurfaceError> {
|
fn before_apply_commit(
|
||||||
self.deref().pre_commit()?;
|
self: Rc<Self>,
|
||||||
Ok(CommitAction::ContinueCommit)
|
pending: &mut PendingState,
|
||||||
|
) -> Result<(), WlSurfaceError> {
|
||||||
|
self.deref().pre_commit(pending)?;
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn post_commit(self: Rc<Self>) {
|
fn after_apply_commit(self: Rc<Self>, _pending: &mut PendingState) {
|
||||||
let buffer = self.surface.buffer.get();
|
let buffer_is_some = self.surface.buffer.is_some();
|
||||||
if self.mapped.get() {
|
if self.mapped.get() {
|
||||||
if buffer.is_none() {
|
if !buffer_is_some {
|
||||||
self.destroy_node();
|
self.destroy_node();
|
||||||
} else {
|
} else {
|
||||||
let pos = self.pos.get();
|
let pos = self.pos.get();
|
||||||
|
|
@ -330,7 +361,7 @@ impl SurfaceExt for ZwlrLayerSurfaceV1 {
|
||||||
self.compute_position();
|
self.compute_position();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if buffer.is_some() {
|
} else if buffer_is_some {
|
||||||
let layer = &self.output.layers[self.layer.get() as usize];
|
let layer = &self.output.layers[self.layer.get() as usize];
|
||||||
self.link.set(Some(layer.add_last(self.clone())));
|
self.link.set(Some(layer.add_last(self.clone())));
|
||||||
self.mapped.set(true);
|
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);
|
return Err(XdgToplevelDragV1Error::AlreadyDragged);
|
||||||
}
|
}
|
||||||
if let Some(prev) = self.toplevel.set(Some(toplevel)) {
|
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);
|
return Err(XdgToplevelDragV1Error::ToplevelAttached);
|
||||||
}
|
}
|
||||||
if prev.id != req.toplevel {
|
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));
|
buffer.fill(Color::from_rgba_straight(255, 255, 255, 255));
|
||||||
|
|
||||||
child.attach(buffer.id)?;
|
child.attach(buffer.id)?;
|
||||||
|
child.commit()?;
|
||||||
|
|
||||||
parent.map().await?;
|
parent.map().await?;
|
||||||
|
|
||||||
|
|
@ -45,10 +46,12 @@ async fn test(run: Rc<TestRun>) -> Result<(), TestError> {
|
||||||
client.compare_screenshot("1").await?;
|
client.compare_screenshot("1").await?;
|
||||||
|
|
||||||
sub.place_below(parent.surface.id)?;
|
sub.place_below(parent.surface.id)?;
|
||||||
|
child.commit()?;
|
||||||
parent.map().await?;
|
parent.map().await?;
|
||||||
client.compare_screenshot("2").await?;
|
client.compare_screenshot("2").await?;
|
||||||
|
|
||||||
sub.place_above(parent.surface.id)?;
|
sub.place_above(parent.surface.id)?;
|
||||||
|
child.commit()?;
|
||||||
parent.map().await?;
|
parent.map().await?;
|
||||||
client.compare_screenshot("1").await?;
|
client.compare_screenshot("1").await?;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,7 @@ async fn test(run: Rc<TestRun>) -> TestResult {
|
||||||
nss.set_position(100, 100)?;
|
nss.set_position(100, 100)?;
|
||||||
let buffer = client.shm.create_buffer(100, 100)?;
|
let buffer = client.shm.create_buffer(100, 100)?;
|
||||||
ns.attach(buffer.id)?;
|
ns.attach(buffer.id)?;
|
||||||
|
ns.commit()?;
|
||||||
|
|
||||||
run.cfg.set_fullscreen(ds.seat.id(), true)?;
|
run.cfg.set_fullscreen(ds.seat.id(), true)?;
|
||||||
client.sync().await;
|
client.sync().await;
|
||||||
|
|
|
||||||
|
|
@ -21,15 +21,14 @@ use {
|
||||||
ptl_screencast::{add_screencast_dbus_members, ScreencastSession},
|
ptl_screencast::{add_screencast_dbus_members, ScreencastSession},
|
||||||
},
|
},
|
||||||
utils::{
|
utils::{
|
||||||
buf::Buf,
|
|
||||||
clone3::{fork_with_pidfd, Forked},
|
clone3::{fork_with_pidfd, Forked},
|
||||||
copyhashmap::CopyHashMap,
|
copyhashmap::CopyHashMap,
|
||||||
errorfmt::ErrorFmt,
|
errorfmt::ErrorFmt,
|
||||||
|
line_logger::log_lines,
|
||||||
numcell::NumCell,
|
numcell::NumCell,
|
||||||
oserror::OsError,
|
oserror::OsError,
|
||||||
process_name::set_process_name,
|
process_name::set_process_name,
|
||||||
run_toplevel::RunToplevel,
|
run_toplevel::RunToplevel,
|
||||||
vecdeque_ext::VecDequeExt,
|
|
||||||
xrd::xrd,
|
xrd::xrd,
|
||||||
},
|
},
|
||||||
video::dmabuf::DmaBufIds,
|
video::dmabuf::DmaBufIds,
|
||||||
|
|
@ -38,7 +37,6 @@ use {
|
||||||
},
|
},
|
||||||
log::Level,
|
log::Level,
|
||||||
std::{
|
std::{
|
||||||
collections::VecDeque,
|
|
||||||
rc::{Rc, Weak},
|
rc::{Rc, Weak},
|
||||||
sync::Arc,
|
sync::Arc,
|
||||||
},
|
},
|
||||||
|
|
@ -104,25 +102,14 @@ impl PortalStartup {
|
||||||
let ring = ring.clone();
|
let ring = ring.clone();
|
||||||
let logger = logger.clone();
|
let logger = logger.clone();
|
||||||
async move {
|
async move {
|
||||||
let mut buf = VecDeque::<u8>::new();
|
let res = log_lines(&ring, &self.logs, |left, right| {
|
||||||
let mut buf2 = Buf::new(1024);
|
logger.write_raw(left);
|
||||||
let mut done = false;
|
logger.write_raw(right);
|
||||||
while !done {
|
logger.write_raw(b" (portal)\n");
|
||||||
match ring.read(&self.logs, buf2.clone()).await {
|
})
|
||||||
Ok(n) if n > 0 => buf.extend(&buf2[..n]),
|
.await;
|
||||||
Ok(_) => done = true,
|
if let Err(e) = res {
|
||||||
Err(e) => {
|
log::error!("Could not read portal logs: {}", ErrorFmt(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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ use {
|
||||||
cursor::KnownCursor,
|
cursor::KnownCursor,
|
||||||
fixed::Fixed,
|
fixed::Fixed,
|
||||||
format::ARGB8888,
|
format::ARGB8888,
|
||||||
gfx_api::{GfxContext, GfxFramebuffer},
|
gfx_api::{AcquireSync, GfxContext, GfxFramebuffer, ReleaseSync},
|
||||||
ifs::zwlr_layer_shell_v1::OVERLAY,
|
ifs::zwlr_layer_shell_v1::OVERLAY,
|
||||||
portal::ptl_display::{PortalDisplay, PortalOutput, PortalSeat},
|
portal::ptl_display::{PortalDisplay, PortalOutput, PortalSeat},
|
||||||
renderer::renderer_base::RendererBase,
|
renderer::renderer_base::RendererBase,
|
||||||
|
|
@ -222,6 +222,9 @@ impl GuiElement for Button {
|
||||||
None,
|
None,
|
||||||
r.scale(),
|
r.scale(),
|
||||||
None,
|
None,
|
||||||
|
None,
|
||||||
|
AcquireSync::None,
|
||||||
|
ReleaseSync::None,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -323,6 +326,9 @@ impl GuiElement for Label {
|
||||||
None,
|
None,
|
||||||
r.scale(),
|
r.scale(),
|
||||||
None,
|
None,
|
||||||
|
None,
|
||||||
|
AcquireSync::None,
|
||||||
|
ReleaseSync::None,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -626,6 +632,19 @@ impl WindowData {
|
||||||
}
|
}
|
||||||
return;
|
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.frame_missed.set(false);
|
||||||
|
|
||||||
self.surface.frame({
|
self.surface.frame({
|
||||||
|
|
@ -641,13 +660,6 @@ impl WindowData {
|
||||||
self.have_frame.set(false);
|
self.have_frame.set(false);
|
||||||
buf.free.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.attach(&buf.wl);
|
||||||
self.surface.commit();
|
self.surface.commit();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
use {
|
use {
|
||||||
crate::{
|
crate::{
|
||||||
gfx_api::{GfxApiOpt, SampleRect},
|
gfx_api::{AcquireSync, GfxApiOpt, ReleaseSync, SampleRect},
|
||||||
ifs::{
|
ifs::{
|
||||||
wl_buffer::WlBuffer,
|
|
||||||
wl_callback::WlCallback,
|
wl_callback::WlCallback,
|
||||||
wl_surface::{
|
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,
|
wp_presentation_feedback::WpPresentationFeedback,
|
||||||
},
|
},
|
||||||
|
|
@ -151,13 +151,33 @@ impl Renderer<'_> {
|
||||||
let scale = output.global.persistent.scale.get();
|
let scale = output.global.persistent.scale.get();
|
||||||
for title in &rd.titles {
|
for title in &rd.titles {
|
||||||
let (x, y) = self.base.scale_point(x + title.tex_x, y + title.tex_y);
|
let (x, y) = self.base.scale_point(x + title.tex_x, y + title.tex_y);
|
||||||
self.base
|
self.base.render_texture(
|
||||||
.render_texture(&title.tex, x, y, None, None, scale, None);
|
&title.tex,
|
||||||
|
x,
|
||||||
|
y,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
scale,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
AcquireSync::None,
|
||||||
|
ReleaseSync::None,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
if let Some(status) = &rd.status {
|
if let Some(status) = &rd.status {
|
||||||
let (x, y) = self.base.scale_point(x + status.tex_x, y + status.tex_y);
|
let (x, y) = self.base.scale_point(x + status.tex_x, y + status.tex_y);
|
||||||
self.base
|
self.base.render_texture(
|
||||||
.render_texture(&status.tex.texture, x, y, None, None, scale, None);
|
&status.tex.texture,
|
||||||
|
x,
|
||||||
|
y,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
scale,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
AcquireSync::None,
|
||||||
|
ReleaseSync::None,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let Some(ws) = output.workspace.get() {
|
if let Some(ws) = output.workspace.get() {
|
||||||
|
|
@ -193,8 +213,18 @@ impl Renderer<'_> {
|
||||||
let (tex_width, tex_height) = tex.texture.size();
|
let (tex_width, tex_height) = tex.texture.size();
|
||||||
let x = x + (pos.width() - tex_width) / 2;
|
let x = x + (pos.width() - tex_width) / 2;
|
||||||
let y = y + (pos.height() - tex_height) / 2;
|
let y = y + (pos.height() - tex_height) / 2;
|
||||||
self.base
|
self.base.render_texture(
|
||||||
.render_texture(&tex.texture, x, y, None, None, self.base.scale, None);
|
&tex.texture,
|
||||||
|
x,
|
||||||
|
y,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
self.base.scale,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
AcquireSync::None,
|
||||||
|
ReleaseSync::None,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -231,6 +261,9 @@ impl Renderer<'_> {
|
||||||
None,
|
None,
|
||||||
self.base.scale,
|
self.base.scale,
|
||||||
None,
|
None,
|
||||||
|
None,
|
||||||
|
AcquireSync::None,
|
||||||
|
ReleaseSync::None,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -345,14 +378,14 @@ impl Renderer<'_> {
|
||||||
|
|
||||||
pub fn render_buffer(
|
pub fn render_buffer(
|
||||||
&mut self,
|
&mut self,
|
||||||
buffer: &WlBuffer,
|
buffer: &Rc<SurfaceBuffer>,
|
||||||
x: i32,
|
x: i32,
|
||||||
y: i32,
|
y: i32,
|
||||||
tpoints: SampleRect,
|
tpoints: SampleRect,
|
||||||
tsize: (i32, i32),
|
tsize: (i32, i32),
|
||||||
bounds: Option<&Rect>,
|
bounds: Option<&Rect>,
|
||||||
) {
|
) {
|
||||||
if let Some(tex) = buffer.texture.get() {
|
if let Some(tex) = buffer.buffer.texture.get() {
|
||||||
self.base.render_texture(
|
self.base.render_texture(
|
||||||
&tex,
|
&tex,
|
||||||
x,
|
x,
|
||||||
|
|
@ -361,8 +394,11 @@ impl Renderer<'_> {
|
||||||
Some(tsize),
|
Some(tsize),
|
||||||
self.base.scale,
|
self.base.scale,
|
||||||
bounds,
|
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) {
|
if let Some(rect) = Rect::new_sized(x, y, tsize.0, tsize.1) {
|
||||||
let rect = match bounds {
|
let rect = match bounds {
|
||||||
None => rect,
|
None => rect,
|
||||||
|
|
@ -409,8 +445,18 @@ impl Renderer<'_> {
|
||||||
self.base.fill_boxes(&title_underline, &uc);
|
self.base.fill_boxes(&title_underline, &uc);
|
||||||
if let Some(title) = floating.title_textures.get(&self.base.scale) {
|
if let Some(title) = floating.title_textures.get(&self.base.scale) {
|
||||||
let (x, y) = self.base.scale_point(x + bw, y + bw);
|
let (x, y) = self.base.scale_point(x + bw, y + bw);
|
||||||
self.base
|
self.base.render_texture(
|
||||||
.render_texture(&title.texture, x, y, None, None, self.base.scale, None);
|
&title.texture,
|
||||||
|
x,
|
||||||
|
y,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
self.base.scale,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
AcquireSync::None,
|
||||||
|
ReleaseSync::None,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
let body = Rect::new_sized(
|
let body = Rect::new_sized(
|
||||||
x + bw,
|
x + bw,
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,9 @@
|
||||||
use {
|
use {
|
||||||
crate::{
|
crate::{
|
||||||
gfx_api::{CopyTexture, FillRect, FramebufferRect, GfxApiOpt, GfxTexture, SampleRect},
|
gfx_api::{
|
||||||
|
AcquireSync, BufferResv, CopyTexture, FillRect, FramebufferRect, GfxApiOpt, GfxTexture,
|
||||||
|
ReleaseSync, SampleRect,
|
||||||
|
},
|
||||||
rect::Rect,
|
rect::Rect,
|
||||||
scale::Scale,
|
scale::Scale,
|
||||||
theme::Color,
|
theme::Color,
|
||||||
|
|
@ -130,6 +133,9 @@ impl RendererBase<'_> {
|
||||||
tsize: Option<(i32, i32)>,
|
tsize: Option<(i32, i32)>,
|
||||||
tscale: Scale,
|
tscale: Scale,
|
||||||
bounds: Option<&Rect>,
|
bounds: Option<&Rect>,
|
||||||
|
buffer_resv: Option<Rc<dyn BufferResv>>,
|
||||||
|
acquire_sync: AcquireSync,
|
||||||
|
release_sync: ReleaseSync,
|
||||||
) {
|
) {
|
||||||
let mut texcoord = tpoints.unwrap_or_else(SampleRect::identity);
|
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 {
|
self.ops.push(GfxApiOpt::CopyTexture(CopyTexture {
|
||||||
tex: texture.clone(),
|
tex: texture.clone(),
|
||||||
source: texcoord,
|
source: texcoord,
|
||||||
target: FramebufferRect::new(
|
target,
|
||||||
target_x[0] as f32,
|
buffer_resv,
|
||||||
target_y[0] as f32,
|
acquire_sync,
|
||||||
target_x[1] as f32,
|
release_sync,
|
||||||
target_y[1] as f32,
|
|
||||||
self.transform,
|
|
||||||
self.fb_width,
|
|
||||||
self.fb_height,
|
|
||||||
),
|
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -78,7 +78,7 @@ pub fn take_screenshot(state: &State) -> Result<Screenshot, ScreenshooterError>
|
||||||
true,
|
true,
|
||||||
false,
|
false,
|
||||||
Transform::None,
|
Transform::None,
|
||||||
);
|
)?;
|
||||||
let drm = gbm.drm.dup_render()?.fd().clone();
|
let drm = gbm.drm.dup_render()?.fd().clone();
|
||||||
Ok(Screenshot { drm, bo })
|
Ok(Screenshot { drm, bo })
|
||||||
}
|
}
|
||||||
|
|
|
||||||
109
src/state.rs
109
src/state.rs
|
|
@ -17,7 +17,10 @@ use {
|
||||||
fixed::Fixed,
|
fixed::Fixed,
|
||||||
forker::ForkerProxy,
|
forker::ForkerProxy,
|
||||||
format::Format,
|
format::Format,
|
||||||
gfx_api::{GfxContext, GfxError, GfxFramebuffer, GfxTexture, SampleRect},
|
gfx_api::{
|
||||||
|
AcquireSync, GfxContext, GfxError, GfxFramebuffer, GfxTexture, ReleaseSync, SampleRect,
|
||||||
|
SyncFile,
|
||||||
|
},
|
||||||
gfx_apis::create_gfx_context,
|
gfx_apis::create_gfx_context,
|
||||||
globals::{Globals, GlobalsError, WaylandGlobal},
|
globals::{Globals, GlobalsError, WaylandGlobal},
|
||||||
ifs::{
|
ifs::{
|
||||||
|
|
@ -30,9 +33,11 @@ use {
|
||||||
wl_output::{OutputId, PersistentOutputState},
|
wl_output::{OutputId, PersistentOutputState},
|
||||||
wl_seat::{SeatIds, WlSeatGlobal},
|
wl_seat::{SeatIds, WlSeatGlobal},
|
||||||
wl_surface::{
|
wl_surface::{
|
||||||
|
wl_subsurface::SubsurfaceIds,
|
||||||
zwp_idle_inhibitor_v1::{IdleInhibitorId, IdleInhibitorIds, ZwpIdleInhibitorV1},
|
zwp_idle_inhibitor_v1::{IdleInhibitorId, IdleInhibitorIds, ZwpIdleInhibitorV1},
|
||||||
NoneSurfaceExt, WlSurface,
|
NoneSurfaceExt, WlSurface,
|
||||||
},
|
},
|
||||||
|
wp_linux_drm_syncobj_manager_v1::WpLinuxDrmSyncobjManagerV1Global,
|
||||||
zwlr_screencopy_frame_v1::ZwlrScreencopyFrameV1,
|
zwlr_screencopy_frame_v1::ZwlrScreencopyFrameV1,
|
||||||
zwp_linux_dmabuf_feedback_v1::ZwpLinuxDmabufFeedbackV1,
|
zwp_linux_dmabuf_feedback_v1::ZwpLinuxDmabufFeedbackV1,
|
||||||
zwp_linux_dmabuf_v1::ZwpLinuxDmabufV1Global,
|
zwp_linux_dmabuf_v1::ZwpLinuxDmabufV1Global,
|
||||||
|
|
@ -55,7 +60,14 @@ use {
|
||||||
linkedlist::LinkedList, numcell::NumCell, queue::AsyncQueue, refcounted::RefCounted,
|
linkedlist::LinkedList, numcell::NumCell, queue::AsyncQueue, refcounted::RefCounted,
|
||||||
run_toplevel::RunToplevel,
|
run_toplevel::RunToplevel,
|
||||||
},
|
},
|
||||||
video::{dmabuf::DmaBufIds, drm::Drm},
|
video::{
|
||||||
|
dmabuf::DmaBufIds,
|
||||||
|
drm::{
|
||||||
|
sync_obj::{SyncObj, SyncObjPoint},
|
||||||
|
wait_for_sync_obj::WaitForSyncObj,
|
||||||
|
Drm,
|
||||||
|
},
|
||||||
|
},
|
||||||
wheel::Wheel,
|
wheel::Wheel,
|
||||||
wire::{
|
wire::{
|
||||||
ExtForeignToplevelListV1Id, JayRenderCtxId, JaySeatEventsId, JayWorkspaceWatcherId,
|
ExtForeignToplevelListV1Id, JayRenderCtxId, JaySeatEventsId, JayWorkspaceWatcherId,
|
||||||
|
|
@ -80,6 +92,7 @@ use {
|
||||||
sync::Arc,
|
sync::Arc,
|
||||||
time::Duration,
|
time::Duration,
|
||||||
},
|
},
|
||||||
|
thiserror::Error,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct State {
|
pub struct State {
|
||||||
|
|
@ -157,6 +170,9 @@ pub struct State {
|
||||||
pub double_click_interval_usec: Cell<u64>,
|
pub double_click_interval_usec: Cell<u64>,
|
||||||
pub double_click_distance: Cell<i32>,
|
pub double_click_distance: Cell<i32>,
|
||||||
pub create_default_seat: Cell<bool>,
|
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 {
|
// impl Drop for State {
|
||||||
|
|
@ -361,6 +377,8 @@ impl State {
|
||||||
self.render_ctx_version.fetch_add(1);
|
self.render_ctx_version.fetch_add(1);
|
||||||
self.cursors.set(None);
|
self.cursors.set(None);
|
||||||
self.drm_feedback.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: {
|
'handle_new_feedback: {
|
||||||
if let Some(ctx) = &ctx {
|
if let Some(ctx) = &ctx {
|
||||||
|
|
@ -406,7 +424,7 @@ impl State {
|
||||||
}
|
}
|
||||||
fn visit_surface(&mut self, node: &Rc<WlSurface>) {
|
fn visit_surface(&mut self, node: &Rc<WlSurface>) {
|
||||||
if let Some(buffer) = node.buffer.get() {
|
if let Some(buffer) = node.buffer.get() {
|
||||||
buffer.handle_gfx_context_change();
|
buffer.buffer.handle_gfx_context_change();
|
||||||
}
|
}
|
||||||
node.node_visit_children(self);
|
node.node_visit_children(self);
|
||||||
}
|
}
|
||||||
|
|
@ -429,11 +447,18 @@ impl State {
|
||||||
seat.render_ctx_changed();
|
seat.render_ctx_changed();
|
||||||
}
|
}
|
||||||
|
|
||||||
if ctx.is_some() && !self.render_ctx_ever_initialized.replace(true) {
|
if let Some(ctx) = &ctx {
|
||||||
self.add_global(&Rc::new(WlDrmGlobal::new(self.globals.name())));
|
if !self.render_ctx_ever_initialized.replace(true) {
|
||||||
self.add_global(&Rc::new(ZwpLinuxDmabufV1Global::new(self.globals.name())));
|
self.add_global(&Rc::new(WlDrmGlobal::new(self.globals.name())));
|
||||||
if let Some(config) = self.config.get() {
|
self.add_global(&Rc::new(ZwpLinuxDmabufV1Global::new(self.globals.name())));
|
||||||
config.graphics_initialized();
|
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>,
|
tex: &Rc<dyn GfxTexture>,
|
||||||
rr: &mut RenderResult,
|
rr: &mut RenderResult,
|
||||||
render_hw_cursor: bool,
|
render_hw_cursor: bool,
|
||||||
) {
|
) -> Result<Option<SyncFile>, GfxError> {
|
||||||
fb.render_output(
|
let sync_file = fb.render_output(
|
||||||
output,
|
output,
|
||||||
self,
|
self,
|
||||||
Some(output.global.pos.get()),
|
Some(output.global.pos.get()),
|
||||||
Some(rr),
|
Some(rr),
|
||||||
output.global.persistent.scale.get(),
|
output.global.persistent.scale.get(),
|
||||||
render_hw_cursor,
|
render_hw_cursor,
|
||||||
);
|
)?;
|
||||||
output.perform_screencopies(tex, !render_hw_cursor, 0, 0, None);
|
output.perform_screencopies(tex, !render_hw_cursor, 0, 0, None);
|
||||||
rr.dispatch_frame_requests();
|
rr.dispatch_frame_requests();
|
||||||
|
Ok(sync_file)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn perform_screencopy(
|
pub fn perform_screencopy(
|
||||||
|
|
@ -790,7 +816,7 @@ impl State {
|
||||||
y_off: i32,
|
y_off: i32,
|
||||||
size: Option<(i32, i32)>,
|
size: Option<(i32, i32)>,
|
||||||
transform: Transform,
|
transform: Transform,
|
||||||
) {
|
) -> Result<Option<SyncFile>, GfxError> {
|
||||||
let mut ops = target.take_render_ops();
|
let mut ops = target.take_render_ops();
|
||||||
let mut renderer = Renderer {
|
let mut renderer = Renderer {
|
||||||
base: target.renderer_base(&mut ops, Scale::from_int(1), Transform::None),
|
base: target.renderer_base(&mut ops, Scale::from_int(1), Transform::None),
|
||||||
|
|
@ -812,6 +838,9 @@ impl State {
|
||||||
size,
|
size,
|
||||||
Scale::from_int(1),
|
Scale::from_int(1),
|
||||||
None,
|
None,
|
||||||
|
None,
|
||||||
|
AcquireSync::None,
|
||||||
|
ReleaseSync::Implicit,
|
||||||
);
|
);
|
||||||
if render_hardware_cursors {
|
if render_hardware_cursors {
|
||||||
for seat in self.globals.lock_seats().values() {
|
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 {
|
fn have_hardware_cursor(&self) -> bool {
|
||||||
|
|
@ -851,7 +880,7 @@ impl State {
|
||||||
stride: i32,
|
stride: i32,
|
||||||
format: &'static Format,
|
format: &'static Format,
|
||||||
transform: Transform,
|
transform: Transform,
|
||||||
) {
|
) -> Result<(), ShmScreencopyError> {
|
||||||
let (src_width, src_height) = src.size();
|
let (src_width, src_height) = src.size();
|
||||||
let mut needs_copy = capture.rect.x1() < x_off
|
let mut needs_copy = capture.rect.x1() < x_off
|
||||||
|| capture.rect.x2() > x_off + src_width
|
|| capture.rect.x2() > x_off + src_width
|
||||||
|
|
@ -866,20 +895,11 @@ impl State {
|
||||||
}
|
}
|
||||||
let acc = if needs_copy {
|
let acc = if needs_copy {
|
||||||
let Some(ctx) = self.render_ctx.get() else {
|
let Some(ctx) = self.render_ctx.get() else {
|
||||||
log::warn!("Cannot perform shm screencopy because there is no render context");
|
return Err(ShmScreencopyError::NoRenderContext);
|
||||||
return;
|
|
||||||
};
|
};
|
||||||
let fb =
|
let fb = ctx
|
||||||
match ctx.create_fb(capture.rect.width(), capture.rect.height(), stride, format) {
|
.create_fb(capture.rect.width(), capture.rect.height(), stride, format)
|
||||||
Ok(f) => f,
|
.map_err(ShmScreencopyError::CreateTemporaryFb)?;
|
||||||
Err(e) => {
|
|
||||||
log::warn!(
|
|
||||||
"Could not create temporary fb for screencopy: {}",
|
|
||||||
ErrorFmt(e)
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
self.perform_screencopy(
|
self.perform_screencopy(
|
||||||
src,
|
src,
|
||||||
&fb,
|
&fb,
|
||||||
|
|
@ -889,7 +909,8 @@ impl State {
|
||||||
y_off - capture.rect.y1(),
|
y_off - capture.rect.y1(),
|
||||||
size,
|
size,
|
||||||
transform,
|
transform,
|
||||||
);
|
)
|
||||||
|
.map_err(ShmScreencopyError::CopyToTemporary)?;
|
||||||
mem.access(|mem| {
|
mem.access(|mem| {
|
||||||
fb.copy_to_shm(
|
fb.copy_to_shm(
|
||||||
0,
|
0,
|
||||||
|
|
@ -914,16 +935,12 @@ impl State {
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
let res = match acc {
|
match acc {
|
||||||
Ok(res) => res,
|
Ok(res) => res.map_err(ShmScreencopyError::ReadPixels),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
capture.client.error(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);
|
self.globals.add_global(self, &seat);
|
||||||
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 fdcloser;
|
||||||
pub mod gfx_api_ext;
|
pub mod gfx_api_ext;
|
||||||
pub mod hex;
|
pub mod hex;
|
||||||
|
pub mod line_logger;
|
||||||
pub mod linkedlist;
|
pub mod linkedlist;
|
||||||
pub mod log_on_drop;
|
pub mod log_on_drop;
|
||||||
pub mod mmap;
|
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>> {
|
fn endpoint(&self, ep: NonNull<NodeData<T>>) -> Option<NodeRef<T>> {
|
||||||
unsafe {
|
unsafe {
|
||||||
if ep != self.root.data {
|
if ep != self.root.data {
|
||||||
|
|
@ -57,11 +74,14 @@ impl<T> LinkedList<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
pub fn is_empty(&self) -> bool {
|
pub fn is_empty(&self) -> bool {
|
||||||
self.last().is_none()
|
self.last().is_none()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn is_not_empty(&self) -> bool {
|
||||||
|
!self.is_empty()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn last(&self) -> Option<NodeRef<T>> {
|
pub fn last(&self) -> Option<NodeRef<T>> {
|
||||||
unsafe { self.endpoint(self.root.data.as_ref().prev.get()) }
|
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() }
|
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> {
|
pub fn remove(&self, k: &K) -> Option<V> {
|
||||||
unsafe { self.m.get().deref_mut().remove(k) }
|
unsafe { self.m.get().deref_mut().remove(k) }
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -48,6 +48,13 @@ impl DmaBuf {
|
||||||
}
|
}
|
||||||
false
|
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 _;
|
const DMA_BUF_BASE: u64 = b'b' as _;
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,6 @@
|
||||||
|
pub mod sync_obj;
|
||||||
mod sys;
|
mod sys;
|
||||||
|
pub mod wait_for_sync_obj;
|
||||||
|
|
||||||
use {
|
use {
|
||||||
crate::{
|
crate::{
|
||||||
|
|
@ -52,7 +54,7 @@ pub use sys::{
|
||||||
#[derive(Debug, Error)]
|
#[derive(Debug, Error)]
|
||||||
pub enum DrmError {
|
pub enum DrmError {
|
||||||
#[error("Could not reopen a node")]
|
#[error("Could not reopen a node")]
|
||||||
ReopenNode(#[source] crate::utils::oserror::OsError),
|
ReopenNode(#[source] OsError),
|
||||||
#[error("Could not retrieve the render node name")]
|
#[error("Could not retrieve the render node name")]
|
||||||
RenderNodeName(#[source] OsError),
|
RenderNodeName(#[source] OsError),
|
||||||
#[error("Could not retrieve the device node name")]
|
#[error("Could not retrieve the device node name")]
|
||||||
|
|
@ -113,6 +115,28 @@ pub enum DrmError {
|
||||||
Version(#[source] OsError),
|
Version(#[source] OsError),
|
||||||
#[error("Format of IN_FORMATS property is invalid")]
|
#[error("Format of IN_FORMATS property is invalid")]
|
||||||
InFormats,
|
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> {
|
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 {}
|
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,
|
io_uring::IoUringError,
|
||||||
state::State,
|
state::State,
|
||||||
user_session::import_environment,
|
user_session::import_environment,
|
||||||
utils::{buf::Buf, errorfmt::ErrorFmt, oserror::OsError},
|
utils::{errorfmt::ErrorFmt, line_logger::log_lines, oserror::OsError},
|
||||||
wire::WlSurfaceId,
|
wire::WlSurfaceId,
|
||||||
xcon::XconError,
|
xcon::XconError,
|
||||||
xwayland::{
|
xwayland::{
|
||||||
|
|
@ -217,29 +217,12 @@ pub fn build_args() -> (String, Vec<String>) {
|
||||||
|
|
||||||
async fn log_xwayland(state: Rc<State>, stderr: OwnedFd) {
|
async fn log_xwayland(state: Rc<State>, stderr: OwnedFd) {
|
||||||
let stderr = Rc::new(stderr);
|
let stderr = Rc::new(stderr);
|
||||||
let mut buf = vec![];
|
let res = log_lines(&state.ring, &stderr, |left, right| {
|
||||||
let mut buf2 = Buf::new(128);
|
log::info!("Xwayland: {}{}", left.as_bstr(), right.as_bstr());
|
||||||
let mut done = false;
|
})
|
||||||
while !done {
|
.await;
|
||||||
loop {
|
if let Err(e) = res {
|
||||||
match state.ring.read(&stderr, buf2.clone()).await {
|
log::error!("Could not read from stderr fd: {}", ErrorFmt(e));
|
||||||
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();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -289,6 +289,7 @@ pub struct Config {
|
||||||
pub render_device: Option<DrmDeviceMatch>,
|
pub render_device: Option<DrmDeviceMatch>,
|
||||||
pub inputs: Vec<Input>,
|
pub inputs: Vec<Input>,
|
||||||
pub idle: Option<Duration>,
|
pub idle: Option<Duration>,
|
||||||
|
pub explicit_sync_enabled: Option<bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Error)]
|
#[derive(Debug, Error)]
|
||||||
|
|
|
||||||
|
|
@ -95,6 +95,7 @@ impl Parser for ConfigParser<'_> {
|
||||||
_,
|
_,
|
||||||
idle_val,
|
idle_val,
|
||||||
),
|
),
|
||||||
|
(explicit_sync,),
|
||||||
) = ext.extract((
|
) = ext.extract((
|
||||||
(
|
(
|
||||||
opt(val("keymap")),
|
opt(val("keymap")),
|
||||||
|
|
@ -120,6 +121,7 @@ impl Parser for ConfigParser<'_> {
|
||||||
opt(val("$schema")),
|
opt(val("$schema")),
|
||||||
opt(val("idle")),
|
opt(val("idle")),
|
||||||
),
|
),
|
||||||
|
(recover(opt(bol("explicit-sync"))),),
|
||||||
))?;
|
))?;
|
||||||
let mut keymap = None;
|
let mut keymap = None;
|
||||||
if let Some(value) = keymap_val {
|
if let Some(value) = keymap_val {
|
||||||
|
|
@ -271,6 +273,7 @@ impl Parser for ConfigParser<'_> {
|
||||||
gfx_api,
|
gfx_api,
|
||||||
drm_devices,
|
drm_devices,
|
||||||
direct_scanout_enabled: direct_scanout.despan(),
|
direct_scanout_enabled: direct_scanout.despan(),
|
||||||
|
explicit_sync_enabled: explicit_sync.despan(),
|
||||||
render_device,
|
render_device,
|
||||||
inputs,
|
inputs,
|
||||||
idle,
|
idle,
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,8 @@ use {
|
||||||
is_reload,
|
is_reload,
|
||||||
keyboard::{Keymap, ModifiedKeySym},
|
keyboard::{Keymap, ModifiedKeySym},
|
||||||
logging::set_log_level,
|
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},
|
status::{set_i3bar_separator, set_status, set_status_command, unset_status_command},
|
||||||
switch_to_vt,
|
switch_to_vt,
|
||||||
theme::{reset_colors, reset_font, reset_sizes, set_font},
|
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 {
|
if let Some(dse) = config.direct_scanout_enabled {
|
||||||
set_direct_scanout_enabled(dse);
|
set_direct_scanout_enabled(dse);
|
||||||
}
|
}
|
||||||
|
if let Some(ese) = config.explicit_sync_enabled {
|
||||||
|
set_explicit_sync_enabled(ese);
|
||||||
|
}
|
||||||
on_new_drm_device({
|
on_new_drm_device({
|
||||||
let state = state.clone();
|
let state = state.clone();
|
||||||
move |d| {
|
move |d| {
|
||||||
|
|
|
||||||
|
|
@ -96,9 +96,7 @@ impl<'a> Lexer<'a> {
|
||||||
|
|
||||||
self.skip_ws();
|
self.skip_ws();
|
||||||
|
|
||||||
let Some(c) = get!(0) else {
|
let c = get!(0)?;
|
||||||
return None;
|
|
||||||
};
|
|
||||||
let pos = self.pos;
|
let pos = self.pos;
|
||||||
|
|
||||||
macro_rules! span {
|
macro_rules! span {
|
||||||
|
|
|
||||||
|
|
@ -498,6 +498,10 @@
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
"description": "Configured whether the compositor attempts direct scanout.\n"
|
"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": {
|
"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",
|
"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"
|
"$ref": "#/$defs/DrmDeviceMatch"
|
||||||
|
|
|
||||||
|
|
@ -887,6 +887,16 @@ The table has the following fields:
|
||||||
|
|
||||||
The value of this field should be a boolean.
|
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):
|
- `render-device` (optional):
|
||||||
|
|
||||||
Selects the device to use for rendering in a system with multiple GPUs.
|
Selects the device to use for rendering in a system with multiple GPUs.
|
||||||
|
|
|
||||||
|
|
@ -1875,6 +1875,15 @@ Config:
|
||||||
required: false
|
required: false
|
||||||
description: |
|
description: |
|
||||||
Configured whether the compositor attempts direct scanout.
|
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:
|
render-device:
|
||||||
ref: DrmDeviceMatch
|
ref: DrmDeviceMatch
|
||||||
required: false
|
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