it: test direct-scanout feedback
This commit is contained in:
parent
6baa7ab07f
commit
b966a73682
9 changed files with 302 additions and 1 deletions
|
|
@ -8,6 +8,7 @@ use {
|
||||||
ScrollAxis, TransformMatrix,
|
ScrollAxis, TransformMatrix,
|
||||||
},
|
},
|
||||||
compositor::TestFuture,
|
compositor::TestFuture,
|
||||||
|
drm_feedback::DrmFeedback,
|
||||||
fixed::Fixed,
|
fixed::Fixed,
|
||||||
gfx_api::GfxError,
|
gfx_api::GfxError,
|
||||||
it::test_error::TestResult,
|
it::test_error::TestResult,
|
||||||
|
|
@ -57,6 +58,7 @@ impl TestBackend {
|
||||||
idx: 1,
|
idx: 1,
|
||||||
},
|
},
|
||||||
events: Default::default(),
|
events: Default::default(),
|
||||||
|
feedback: Default::default(),
|
||||||
});
|
});
|
||||||
let default_mouse = Rc::new(TestBackendMouse {
|
let default_mouse = Rc::new(TestBackendMouse {
|
||||||
common: TestInputDeviceCommon {
|
common: TestInputDeviceCommon {
|
||||||
|
|
@ -219,6 +221,7 @@ pub struct TestConnector {
|
||||||
pub id: ConnectorId,
|
pub id: ConnectorId,
|
||||||
pub kernel_id: ConnectorKernelId,
|
pub kernel_id: ConnectorKernelId,
|
||||||
pub events: OnChange<ConnectorEvent>,
|
pub events: OnChange<ConnectorEvent>,
|
||||||
|
pub feedback: CloneCell<Option<Rc<DrmFeedback>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Connector for TestConnector {
|
impl Connector for TestConnector {
|
||||||
|
|
@ -249,6 +252,10 @@ impl Connector for TestConnector {
|
||||||
fn set_mode(&self, _mode: Mode) {
|
fn set_mode(&self, _mode: Mode) {
|
||||||
// todo
|
// todo
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn drm_feedback(&self) -> Option<Rc<DrmFeedback>> {
|
||||||
|
self.feedback.get()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct TestMouseClick {
|
pub struct TestMouseClick {
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,8 @@ pub mod test_data_device_manager;
|
||||||
pub mod test_data_offer;
|
pub mod test_data_offer;
|
||||||
pub mod test_data_source;
|
pub mod test_data_source;
|
||||||
pub mod test_display;
|
pub mod test_display;
|
||||||
|
pub mod test_dmabuf;
|
||||||
|
pub mod test_dmabuf_feedback;
|
||||||
pub mod test_ext_foreign_toplevel_handle;
|
pub mod test_ext_foreign_toplevel_handle;
|
||||||
pub mod test_ext_foreign_toplevel_list;
|
pub mod test_ext_foreign_toplevel_list;
|
||||||
pub mod test_jay_compositor;
|
pub mod test_jay_compositor;
|
||||||
|
|
|
||||||
72
src/it/test_ifs/test_dmabuf.rs
Normal file
72
src/it/test_ifs/test_dmabuf.rs
Normal file
|
|
@ -0,0 +1,72 @@
|
||||||
|
use {
|
||||||
|
crate::{
|
||||||
|
it::{
|
||||||
|
test_error::TestResult,
|
||||||
|
test_ifs::{test_dmabuf_feedback::TestDmabufFeedback, test_surface::TestSurface},
|
||||||
|
test_object::TestObject,
|
||||||
|
test_transport::TestTransport,
|
||||||
|
},
|
||||||
|
wire::{zwp_linux_dmabuf_v1::*, ZwpLinuxDmabufV1Id},
|
||||||
|
},
|
||||||
|
std::{cell::Cell, rc::Rc},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub struct TestDmabuf {
|
||||||
|
pub id: ZwpLinuxDmabufV1Id,
|
||||||
|
pub tran: Rc<TestTransport>,
|
||||||
|
pub destroyed: Cell<bool>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TestDmabuf {
|
||||||
|
pub fn new(tran: &Rc<TestTransport>) -> Self {
|
||||||
|
Self {
|
||||||
|
id: tran.id(),
|
||||||
|
tran: tran.clone(),
|
||||||
|
destroyed: Cell::new(false),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn destroy(&self) -> TestResult {
|
||||||
|
if !self.destroyed.replace(true) {
|
||||||
|
self.tran.send(Destroy { self_id: self.id })?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn get_default_feedback(&self) -> TestResult<Rc<TestDmabufFeedback>> {
|
||||||
|
let obj = Rc::new(TestDmabufFeedback::new(&self.tran));
|
||||||
|
self.tran.add_obj(obj.clone())?;
|
||||||
|
self.tran.send(GetDefaultFeedback {
|
||||||
|
self_id: self.id,
|
||||||
|
id: obj.id,
|
||||||
|
})?;
|
||||||
|
Ok(obj)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_surface_feedback(
|
||||||
|
&self,
|
||||||
|
surface: &TestSurface,
|
||||||
|
) -> TestResult<Rc<TestDmabufFeedback>> {
|
||||||
|
let obj = Rc::new(TestDmabufFeedback::new(&self.tran));
|
||||||
|
self.tran.add_obj(obj.clone())?;
|
||||||
|
self.tran.send(GetSurfaceFeedback {
|
||||||
|
self_id: self.id,
|
||||||
|
id: obj.id,
|
||||||
|
surface: surface.id,
|
||||||
|
})?;
|
||||||
|
Ok(obj)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for TestDmabuf {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
let _ = self.destroy();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
test_object! {
|
||||||
|
TestDmabuf, ZwpLinuxDmabufV1;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TestObject for TestDmabuf {}
|
||||||
147
src/it/test_ifs/test_dmabuf_feedback.rs
Normal file
147
src/it/test_ifs/test_dmabuf_feedback.rs
Normal file
|
|
@ -0,0 +1,147 @@
|
||||||
|
use {
|
||||||
|
crate::{
|
||||||
|
it::{
|
||||||
|
test_error::TestResult, test_object::TestObject, test_transport::TestTransport,
|
||||||
|
test_utils::test_expected_event::TEEH, testrun::ParseFull,
|
||||||
|
},
|
||||||
|
utils::buffd::MsgParser,
|
||||||
|
wire::{zwp_linux_dmabuf_feedback_v1::*, ZwpLinuxDmabufFeedbackV1Id},
|
||||||
|
},
|
||||||
|
std::{
|
||||||
|
cell::{Cell, RefCell},
|
||||||
|
mem,
|
||||||
|
ops::DerefMut,
|
||||||
|
rc::Rc,
|
||||||
|
},
|
||||||
|
uapi::{c, OwnedFd},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub struct TestDmabufFeedback {
|
||||||
|
pub id: ZwpLinuxDmabufFeedbackV1Id,
|
||||||
|
pub tran: Rc<TestTransport>,
|
||||||
|
pub destroyed: Cell<bool>,
|
||||||
|
pub feedback: TEEH<Feedback>,
|
||||||
|
pub pending_feedback: RefCell<PendingFeedback>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct PendingFeedback {
|
||||||
|
pub format_table: Option<Rc<OwnedFd>>,
|
||||||
|
pub format_table_size: usize,
|
||||||
|
pub main_device: c::dev_t,
|
||||||
|
pub tranches: Vec<Tranche>,
|
||||||
|
pub pending_tranche: Tranche,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Feedback {
|
||||||
|
pub format_table: Rc<OwnedFd>,
|
||||||
|
pub format_table_size: usize,
|
||||||
|
pub main_device: c::dev_t,
|
||||||
|
pub tranches: Vec<Tranche>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct Tranche {
|
||||||
|
pub target_device: c::dev_t,
|
||||||
|
pub formats: Vec<usize>,
|
||||||
|
pub flags: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TestDmabufFeedback {
|
||||||
|
pub fn new(tran: &Rc<TestTransport>) -> Self {
|
||||||
|
Self {
|
||||||
|
id: tran.id(),
|
||||||
|
tran: tran.clone(),
|
||||||
|
destroyed: Cell::new(false),
|
||||||
|
feedback: Rc::new(Default::default()),
|
||||||
|
pending_feedback: RefCell::new(Default::default()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn destroy(&self) -> TestResult {
|
||||||
|
if !self.destroyed.replace(true) {
|
||||||
|
self.tran.send(Destroy { self_id: self.id })?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_done(&self, parser: MsgParser<'_, '_>) -> TestResult {
|
||||||
|
let _ev = Done::parse_full(parser)?;
|
||||||
|
let mut pending = mem::take(self.pending_feedback.borrow_mut().deref_mut());
|
||||||
|
self.feedback.push(Feedback {
|
||||||
|
format_table: match pending.format_table.take() {
|
||||||
|
None => bail!("compositor did not send format table"),
|
||||||
|
Some(ft) => ft,
|
||||||
|
},
|
||||||
|
format_table_size: pending.format_table_size,
|
||||||
|
main_device: pending.main_device,
|
||||||
|
tranches: pending.tranches,
|
||||||
|
});
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_format_table(&self, parser: MsgParser<'_, '_>) -> TestResult {
|
||||||
|
let ev = FormatTable::parse_full(parser)?;
|
||||||
|
let pending = &mut *self.pending_feedback.borrow_mut();
|
||||||
|
pending.format_table = Some(ev.fd);
|
||||||
|
pending.format_table_size = ev.size as _;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_main_device(&self, parser: MsgParser<'_, '_>) -> TestResult {
|
||||||
|
let ev = MainDevice::parse_full(parser)?;
|
||||||
|
let pending = &mut *self.pending_feedback.borrow_mut();
|
||||||
|
pending.main_device = ev.device;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_tranche_done(&self, parser: MsgParser<'_, '_>) -> TestResult {
|
||||||
|
let _ev = TrancheDone::parse_full(parser)?;
|
||||||
|
let pending = &mut *self.pending_feedback.borrow_mut();
|
||||||
|
pending
|
||||||
|
.tranches
|
||||||
|
.push(mem::take(&mut pending.pending_tranche));
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_tranche_target_device(&self, parser: MsgParser<'_, '_>) -> TestResult {
|
||||||
|
let ev = TrancheTargetDevice::parse_full(parser)?;
|
||||||
|
let pending = &mut *self.pending_feedback.borrow_mut();
|
||||||
|
pending.pending_tranche.target_device = ev.device;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_tranche_formats(&self, parser: MsgParser<'_, '_>) -> TestResult {
|
||||||
|
let ev = TrancheFormats::parse_full(parser)?;
|
||||||
|
let pending = &mut *self.pending_feedback.borrow_mut();
|
||||||
|
pending.pending_tranche.formats = ev.indices.iter().copied().map(|v| v as usize).collect();
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_tranche_flags(&self, parser: MsgParser<'_, '_>) -> TestResult {
|
||||||
|
let ev = TrancheFlags::parse_full(parser)?;
|
||||||
|
let pending = &mut *self.pending_feedback.borrow_mut();
|
||||||
|
pending.pending_tranche.flags = ev.flags;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for TestDmabufFeedback {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
let _ = self.destroy();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
test_object! {
|
||||||
|
TestDmabufFeedback, ZwpLinuxDmabufFeedbackV1;
|
||||||
|
|
||||||
|
DONE => handle_done,
|
||||||
|
FORMAT_TABLE => handle_format_table,
|
||||||
|
MAIN_DEVICE => handle_main_device,
|
||||||
|
TRANCHE_DONE => handle_tranche_done,
|
||||||
|
TRANCHE_TARGET_DEVICE => handle_tranche_target_device,
|
||||||
|
TRANCHE_FORMATS => handle_tranche_formats,
|
||||||
|
TRANCHE_FLAGS => handle_tranche_flags,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TestObject for TestDmabufFeedback {}
|
||||||
|
|
@ -8,7 +8,7 @@ use {
|
||||||
test_compositor::TestCompositor, test_content_type_manager::TestContentTypeManager,
|
test_compositor::TestCompositor, test_content_type_manager::TestContentTypeManager,
|
||||||
test_cursor_shape_manager::TestCursorShapeManager,
|
test_cursor_shape_manager::TestCursorShapeManager,
|
||||||
test_data_control_manager::TestDataControlManager,
|
test_data_control_manager::TestDataControlManager,
|
||||||
test_data_device_manager::TestDataDeviceManager,
|
test_data_device_manager::TestDataDeviceManager, test_dmabuf::TestDmabuf,
|
||||||
test_ext_foreign_toplevel_list::TestExtForeignToplevelList,
|
test_ext_foreign_toplevel_list::TestExtForeignToplevelList,
|
||||||
test_jay_compositor::TestJayCompositor, test_shm::TestShm,
|
test_jay_compositor::TestJayCompositor, test_shm::TestShm,
|
||||||
test_single_pixel_buffer_manager::TestSinglePixelBufferManager,
|
test_single_pixel_buffer_manager::TestSinglePixelBufferManager,
|
||||||
|
|
@ -47,6 +47,7 @@ pub struct TestRegistrySingletons {
|
||||||
pub wp_linux_drm_syncobj_manager_v1: u32,
|
pub wp_linux_drm_syncobj_manager_v1: u32,
|
||||||
pub wp_content_type_manager_v1: u32,
|
pub wp_content_type_manager_v1: u32,
|
||||||
pub zwlr_data_control_manager_v1: u32,
|
pub zwlr_data_control_manager_v1: u32,
|
||||||
|
pub zwp_linux_dmabuf_v1: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct TestRegistry {
|
pub struct TestRegistry {
|
||||||
|
|
@ -68,6 +69,7 @@ pub struct TestRegistry {
|
||||||
pub syncobj_manager: CloneCell<Option<Rc<TestSyncobjManager>>>,
|
pub syncobj_manager: CloneCell<Option<Rc<TestSyncobjManager>>>,
|
||||||
pub content_type_manager: CloneCell<Option<Rc<TestContentTypeManager>>>,
|
pub content_type_manager: CloneCell<Option<Rc<TestContentTypeManager>>>,
|
||||||
pub data_control_manager: CloneCell<Option<Rc<TestDataControlManager>>>,
|
pub data_control_manager: CloneCell<Option<Rc<TestDataControlManager>>>,
|
||||||
|
pub dmabuf: CloneCell<Option<Rc<TestDmabuf>>>,
|
||||||
pub seats: CopyHashMap<GlobalName, Rc<WlSeatGlobal>>,
|
pub seats: CopyHashMap<GlobalName, Rc<WlSeatGlobal>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -133,6 +135,7 @@ impl TestRegistry {
|
||||||
wp_linux_drm_syncobj_manager_v1,
|
wp_linux_drm_syncobj_manager_v1,
|
||||||
wp_content_type_manager_v1,
|
wp_content_type_manager_v1,
|
||||||
zwlr_data_control_manager_v1,
|
zwlr_data_control_manager_v1,
|
||||||
|
zwp_linux_dmabuf_v1,
|
||||||
};
|
};
|
||||||
self.singletons.set(Some(singletons.clone()));
|
self.singletons.set(Some(singletons.clone()));
|
||||||
Ok(singletons)
|
Ok(singletons)
|
||||||
|
|
@ -212,6 +215,7 @@ impl TestRegistry {
|
||||||
2,
|
2,
|
||||||
TestDataControlManager
|
TestDataControlManager
|
||||||
);
|
);
|
||||||
|
create_singleton!(get_dmabuf, dmabuf, zwp_linux_dmabuf_v1, 5, TestDmabuf);
|
||||||
|
|
||||||
pub fn bind<O: TestObject>(
|
pub fn bind<O: TestObject>(
|
||||||
&self,
|
&self,
|
||||||
|
|
|
||||||
|
|
@ -66,6 +66,7 @@ impl TestTransport {
|
||||||
syncobj_manager: Default::default(),
|
syncobj_manager: Default::default(),
|
||||||
content_type_manager: Default::default(),
|
content_type_manager: Default::default(),
|
||||||
data_control_manager: Default::default(),
|
data_control_manager: Default::default(),
|
||||||
|
dmabuf: Default::default(),
|
||||||
seats: Default::default(),
|
seats: Default::default(),
|
||||||
});
|
});
|
||||||
self.send(wl_display::GetRegistry {
|
self.send(wl_display::GetRegistry {
|
||||||
|
|
|
||||||
|
|
@ -66,6 +66,7 @@ mod t0032_content_type;
|
||||||
mod t0032_data_control;
|
mod t0032_data_control;
|
||||||
mod t0033_float_size_memoization;
|
mod t0033_float_size_memoization;
|
||||||
mod t0034_workspace_restoration;
|
mod t0034_workspace_restoration;
|
||||||
|
mod t0035_scanout_feedback;
|
||||||
|
|
||||||
pub trait TestCase: Sync {
|
pub trait TestCase: Sync {
|
||||||
fn name(&self) -> &'static str;
|
fn name(&self) -> &'static str;
|
||||||
|
|
@ -119,5 +120,6 @@ pub fn tests() -> Vec<&'static dyn TestCase> {
|
||||||
t0032_data_control,
|
t0032_data_control,
|
||||||
t0033_float_size_memoization,
|
t0033_float_size_memoization,
|
||||||
t0034_workspace_restoration,
|
t0034_workspace_restoration,
|
||||||
|
t0035_scanout_feedback,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,7 @@ async fn test(run: Rc<TestRun>) -> TestResult {
|
||||||
idx: 2,
|
idx: 2,
|
||||||
},
|
},
|
||||||
events: Default::default(),
|
events: Default::default(),
|
||||||
|
feedback: Default::default(),
|
||||||
});
|
});
|
||||||
let new_monitor_info = MonitorInfo {
|
let new_monitor_info = MonitorInfo {
|
||||||
modes: vec![],
|
modes: vec![],
|
||||||
|
|
|
||||||
65
src/it/tests/t0035_scanout_feedback.rs
Normal file
65
src/it/tests/t0035_scanout_feedback.rs
Normal file
|
|
@ -0,0 +1,65 @@
|
||||||
|
use {
|
||||||
|
crate::{
|
||||||
|
ifs::zwp_linux_dmabuf_feedback_v1::SCANOUT,
|
||||||
|
it::{
|
||||||
|
test_error::{TestErrorExt, TestResult},
|
||||||
|
testrun::TestRun,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
std::rc::Rc,
|
||||||
|
};
|
||||||
|
|
||||||
|
testcase!();
|
||||||
|
|
||||||
|
async fn test(run: Rc<TestRun>) -> TestResult {
|
||||||
|
let ds = run.create_default_setup().await?;
|
||||||
|
|
||||||
|
let scanout_feedback = {
|
||||||
|
let Some(base_fb) = run.state.drm_feedback.get() else {
|
||||||
|
bail!("no base fb");
|
||||||
|
};
|
||||||
|
let Some(index) = base_fb.shared.indices.keys().copied().next() else {
|
||||||
|
bail!("no formats");
|
||||||
|
};
|
||||||
|
let fb = base_fb
|
||||||
|
.for_scanout(&run.state.drm_feedback_ids, 1234, &[index])
|
||||||
|
.unwrap()
|
||||||
|
.unwrap();
|
||||||
|
Rc::new(fb)
|
||||||
|
};
|
||||||
|
|
||||||
|
ds.connector.feedback.set(Some(scanout_feedback.clone()));
|
||||||
|
|
||||||
|
let client1 = run.create_client().await?;
|
||||||
|
let win1 = client1.create_window().await?;
|
||||||
|
let dmabuf = client1.registry.get_dmabuf().await?;
|
||||||
|
let feedback = dmabuf.get_surface_feedback(&win1.surface)?;
|
||||||
|
let feedback = feedback.feedback.expect()?;
|
||||||
|
win1.map2().await?;
|
||||||
|
|
||||||
|
client1.sync().await;
|
||||||
|
let fb = feedback.last().with_context(|| "feedback 1")?;
|
||||||
|
tassert_eq!(fb.tranches.len(), 1);
|
||||||
|
tassert_eq!(fb.tranches[0].flags, 0);
|
||||||
|
|
||||||
|
run.cfg.set_fullscreen(ds.seat.id(), true)?;
|
||||||
|
|
||||||
|
client1.sync().await;
|
||||||
|
let fb = feedback.last().with_context(|| "feedback 2")?;
|
||||||
|
tassert_eq!(fb.tranches.len(), 2);
|
||||||
|
tassert_eq!(
|
||||||
|
fb.tranches[0].target_device,
|
||||||
|
scanout_feedback.tranches[0].device
|
||||||
|
);
|
||||||
|
tassert_eq!(fb.tranches[0].flags, SCANOUT);
|
||||||
|
tassert_eq!(fb.tranches[1].flags, 0);
|
||||||
|
|
||||||
|
run.cfg.set_fullscreen(ds.seat.id(), false)?;
|
||||||
|
|
||||||
|
client1.sync().await;
|
||||||
|
let fb = feedback.last().with_context(|| "feedback 2")?;
|
||||||
|
tassert_eq!(fb.tranches.len(), 1);
|
||||||
|
tassert_eq!(fb.tranches[0].flags, 0);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue