it: add fifo test
This commit is contained in:
parent
f45cbed53b
commit
73bf4465e2
9 changed files with 264 additions and 3 deletions
|
|
@ -8,9 +8,10 @@ use {
|
||||||
test_ifs::{
|
test_ifs::{
|
||||||
test_compositor::TestCompositor, test_cursor_shape_manager::TestCursorShapeManager,
|
test_compositor::TestCompositor, test_cursor_shape_manager::TestCursorShapeManager,
|
||||||
test_data_device_manager::TestDataDeviceManager,
|
test_data_device_manager::TestDataDeviceManager,
|
||||||
test_jay_compositor::TestJayCompositor, test_keyboard::TestKeyboard,
|
test_fifo_manager::TestFifoManager, test_jay_compositor::TestJayCompositor,
|
||||||
test_pointer::TestPointer, test_registry::TestRegistry, test_seat::TestSeat,
|
test_keyboard::TestKeyboard, test_pointer::TestPointer,
|
||||||
test_shm::TestShm, test_single_pixel_buffer_manager::TestSinglePixelBufferManager,
|
test_registry::TestRegistry, test_seat::TestSeat, test_shm::TestShm,
|
||||||
|
test_single_pixel_buffer_manager::TestSinglePixelBufferManager,
|
||||||
test_subcompositor::TestSubcompositor, test_viewporter::TestViewporter,
|
test_subcompositor::TestSubcompositor, test_viewporter::TestViewporter,
|
||||||
test_xdg_activation::TestXdgActivation, test_xdg_base::TestXdgWmBase,
|
test_xdg_activation::TestXdgActivation, test_xdg_base::TestXdgWmBase,
|
||||||
},
|
},
|
||||||
|
|
@ -38,6 +39,7 @@ pub struct TestClient {
|
||||||
pub activation: Rc<TestXdgActivation>,
|
pub activation: Rc<TestXdgActivation>,
|
||||||
pub data_device_manager: Rc<TestDataDeviceManager>,
|
pub data_device_manager: Rc<TestDataDeviceManager>,
|
||||||
pub cursor_shape_manager: Rc<TestCursorShapeManager>,
|
pub cursor_shape_manager: Rc<TestCursorShapeManager>,
|
||||||
|
pub fifo_manager: Rc<TestFifoManager>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct DefaultSeat {
|
pub struct DefaultSeat {
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@ pub mod test_dmabuf;
|
||||||
pub mod test_dmabuf_feedback;
|
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_fifo_manager;
|
||||||
pub mod test_input_method;
|
pub mod test_input_method;
|
||||||
pub mod test_input_method_keyboard_grab;
|
pub mod test_input_method_keyboard_grab;
|
||||||
pub mod test_input_method_manager;
|
pub mod test_input_method_manager;
|
||||||
|
|
|
||||||
76
src/it/test_ifs/test_fifo_manager.rs
Normal file
76
src/it/test_ifs/test_fifo_manager.rs
Normal file
|
|
@ -0,0 +1,76 @@
|
||||||
|
use {
|
||||||
|
crate::{
|
||||||
|
it::{
|
||||||
|
test_error::TestError, test_ifs::test_surface::TestSurface, test_object::TestObject,
|
||||||
|
test_transport::TestTransport,
|
||||||
|
},
|
||||||
|
wire::{WpFifoManagerV1Id, WpFifoV1Id, wp_fifo_manager_v1::*, wp_fifo_v1},
|
||||||
|
},
|
||||||
|
std::rc::Rc,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub struct TestFifoManager {
|
||||||
|
pub id: WpFifoManagerV1Id,
|
||||||
|
pub tran: Rc<TestTransport>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct TestFifo {
|
||||||
|
pub id: WpFifoV1Id,
|
||||||
|
pub tran: Rc<TestTransport>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TestFifoManager {
|
||||||
|
pub fn new(tran: &Rc<TestTransport>) -> Self {
|
||||||
|
Self {
|
||||||
|
id: tran.id(),
|
||||||
|
tran: tran.clone(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_fifo(&self, surface: &TestSurface) -> Result<Rc<TestFifo>, TestError> {
|
||||||
|
let obj = Rc::new(TestFifo {
|
||||||
|
id: self.tran.id(),
|
||||||
|
tran: self.tran.clone(),
|
||||||
|
});
|
||||||
|
self.tran.send(GetFifo {
|
||||||
|
self_id: self.id,
|
||||||
|
id: obj.id,
|
||||||
|
surface: surface.id,
|
||||||
|
})?;
|
||||||
|
self.tran.add_obj(obj.clone())?;
|
||||||
|
Ok(obj)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[expect(dead_code)]
|
||||||
|
pub fn destroy(&self) -> Result<(), TestError> {
|
||||||
|
self.tran.send(Destroy { self_id: self.id })?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TestFifo {
|
||||||
|
pub fn set_barrier(&self) -> Result<(), TestError> {
|
||||||
|
self.tran.send(wp_fifo_v1::SetBarrier { self_id: self.id })
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn wait_barrier(&self) -> Result<(), TestError> {
|
||||||
|
self.tran.send(wp_fifo_v1::WaitBarrier { self_id: self.id })
|
||||||
|
}
|
||||||
|
|
||||||
|
#[expect(dead_code)]
|
||||||
|
pub fn destroy(&self) -> Result<(), TestError> {
|
||||||
|
self.tran.send(wp_fifo_v1::Destroy { self_id: self.id })?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
test_object! {
|
||||||
|
TestFifoManager, WpFifoManagerV1;
|
||||||
|
}
|
||||||
|
|
||||||
|
test_object! {
|
||||||
|
TestFifo, WpFifoV1;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TestObject for TestFifoManager {}
|
||||||
|
impl TestObject for TestFifo {}
|
||||||
|
|
@ -11,6 +11,7 @@ use {
|
||||||
test_data_control_manager::TestDataControlManager,
|
test_data_control_manager::TestDataControlManager,
|
||||||
test_data_device_manager::TestDataDeviceManager, test_dmabuf::TestDmabuf,
|
test_data_device_manager::TestDataDeviceManager, test_dmabuf::TestDmabuf,
|
||||||
test_ext_foreign_toplevel_list::TestExtForeignToplevelList,
|
test_ext_foreign_toplevel_list::TestExtForeignToplevelList,
|
||||||
|
test_fifo_manager::TestFifoManager,
|
||||||
test_input_method_manager::TestInputMethodManager,
|
test_input_method_manager::TestInputMethodManager,
|
||||||
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,
|
||||||
|
|
@ -60,6 +61,7 @@ pub struct TestRegistrySingletons {
|
||||||
pub zwp_input_method_manager_v2: u32,
|
pub zwp_input_method_manager_v2: u32,
|
||||||
pub zwp_text_input_manager_v3: u32,
|
pub zwp_text_input_manager_v3: u32,
|
||||||
pub wl_fixes: u32,
|
pub wl_fixes: u32,
|
||||||
|
pub wp_fifo_manager_v1: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct TestRegistry {
|
pub struct TestRegistry {
|
||||||
|
|
@ -88,6 +90,7 @@ pub struct TestRegistry {
|
||||||
pub input_method_manager: CloneCell<Option<Rc<TestInputMethodManager>>>,
|
pub input_method_manager: CloneCell<Option<Rc<TestInputMethodManager>>>,
|
||||||
pub text_input_manager: CloneCell<Option<Rc<TestTextInputManager>>>,
|
pub text_input_manager: CloneCell<Option<Rc<TestTextInputManager>>>,
|
||||||
pub wl_fixes: CloneCell<Option<Rc<TestWlFixes>>>,
|
pub wl_fixes: CloneCell<Option<Rc<TestWlFixes>>>,
|
||||||
|
pub fifo_manager: CloneCell<Option<Rc<TestFifoManager>>>,
|
||||||
pub seats: CopyHashMap<GlobalName, Rc<WlSeatGlobal>>,
|
pub seats: CopyHashMap<GlobalName, Rc<WlSeatGlobal>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -160,6 +163,7 @@ impl TestRegistry {
|
||||||
zwp_input_method_manager_v2,
|
zwp_input_method_manager_v2,
|
||||||
zwp_text_input_manager_v3,
|
zwp_text_input_manager_v3,
|
||||||
wl_fixes,
|
wl_fixes,
|
||||||
|
wp_fifo_manager_v1,
|
||||||
};
|
};
|
||||||
self.singletons.set(Some(singletons.clone()));
|
self.singletons.set(Some(singletons.clone()));
|
||||||
Ok(singletons)
|
Ok(singletons)
|
||||||
|
|
@ -276,6 +280,13 @@ impl TestRegistry {
|
||||||
TestTextInputManager
|
TestTextInputManager
|
||||||
);
|
);
|
||||||
create_singleton!(get_wl_fixes, wl_fixes, wl_fixes, 1, TestWlFixes);
|
create_singleton!(get_wl_fixes, wl_fixes, wl_fixes, 1, TestWlFixes);
|
||||||
|
create_singleton!(
|
||||||
|
get_fifo_manager,
|
||||||
|
fifo_manager,
|
||||||
|
wp_fifo_manager_v1,
|
||||||
|
1,
|
||||||
|
TestFifoManager
|
||||||
|
);
|
||||||
|
|
||||||
pub fn bind<O: TestObject>(
|
pub fn bind<O: TestObject>(
|
||||||
&self,
|
&self,
|
||||||
|
|
|
||||||
|
|
@ -74,6 +74,7 @@ impl TestTransport {
|
||||||
input_method_manager: Default::default(),
|
input_method_manager: Default::default(),
|
||||||
text_input_manager: Default::default(),
|
text_input_manager: Default::default(),
|
||||||
wl_fixes: Default::default(),
|
wl_fixes: Default::default(),
|
||||||
|
fifo_manager: Default::default(),
|
||||||
seats: Default::default(),
|
seats: Default::default(),
|
||||||
});
|
});
|
||||||
self.send(wl_display::GetRegistry {
|
self.send(wl_display::GetRegistry {
|
||||||
|
|
|
||||||
|
|
@ -91,6 +91,7 @@ impl TestRun {
|
||||||
activation: registry.get_activation().await?,
|
activation: registry.get_activation().await?,
|
||||||
data_device_manager: registry.get_data_device_manager().await?,
|
data_device_manager: registry.get_data_device_manager().await?,
|
||||||
cursor_shape_manager: registry.get_cursor_shape_manager().await?,
|
cursor_shape_manager: registry.get_cursor_shape_manager().await?,
|
||||||
|
fifo_manager: registry.get_fifo_manager().await?,
|
||||||
registry,
|
registry,
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -80,6 +80,7 @@ mod t0046_buffer_release;
|
||||||
mod t0047_surface_damage;
|
mod t0047_surface_damage;
|
||||||
mod t0048_frame_callback;
|
mod t0048_frame_callback;
|
||||||
mod t0049_surface_damage_backend;
|
mod t0049_surface_damage_backend;
|
||||||
|
mod t0050_fifo;
|
||||||
|
|
||||||
pub trait TestCase: Sync {
|
pub trait TestCase: Sync {
|
||||||
fn name(&self) -> &'static str;
|
fn name(&self) -> &'static str;
|
||||||
|
|
@ -148,5 +149,6 @@ pub fn tests() -> Vec<&'static dyn TestCase> {
|
||||||
t0047_surface_damage,
|
t0047_surface_damage,
|
||||||
t0048_frame_callback,
|
t0048_frame_callback,
|
||||||
t0049_surface_damage_backend,
|
t0049_surface_damage_backend,
|
||||||
|
t0050_fifo,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
153
src/it/tests/t0050_fifo.rs
Normal file
153
src/it/tests/t0050_fifo.rs
Normal file
|
|
@ -0,0 +1,153 @@
|
||||||
|
use {
|
||||||
|
crate::{
|
||||||
|
it::{
|
||||||
|
test_error::{TestError, TestResult},
|
||||||
|
testrun::TestRun,
|
||||||
|
},
|
||||||
|
theme::Color,
|
||||||
|
},
|
||||||
|
std::rc::Rc,
|
||||||
|
};
|
||||||
|
|
||||||
|
testcase!();
|
||||||
|
|
||||||
|
/// Test wp_fifo_v1 protocol implementation
|
||||||
|
/// This test verifies that the compositor correctly implements the fifo-v1 protocol
|
||||||
|
/// for synchronizing surface updates with display refresh cycles.
|
||||||
|
async fn test(run: Rc<TestRun>) -> TestResult {
|
||||||
|
run.backend.install_default()?;
|
||||||
|
let client = run.create_client().await?;
|
||||||
|
|
||||||
|
// Create a visible window so fifo constraints can be tested
|
||||||
|
let window = client.create_window().await?;
|
||||||
|
window.map().await?;
|
||||||
|
client.sync().await;
|
||||||
|
|
||||||
|
let fifo_manager = &client.fifo_manager;
|
||||||
|
let surface = &window.surface.surface;
|
||||||
|
let connector_id = run.backend.default_connector.id;
|
||||||
|
|
||||||
|
// Get a fifo object for the surface
|
||||||
|
let fifo = fifo_manager.get_fifo(surface)?;
|
||||||
|
client.sync().await;
|
||||||
|
|
||||||
|
// Test 1: Basic fifo barrier functionality without wait_barrier
|
||||||
|
// This should not block the commit - the old buffer should be released immediately
|
||||||
|
fifo.set_barrier()?;
|
||||||
|
let buffer1 = client.spbm.create_buffer(Color::from_srgb(255, 0, 0))?;
|
||||||
|
surface.attach(buffer1.id)?;
|
||||||
|
surface.commit()?;
|
||||||
|
client.sync().await;
|
||||||
|
|
||||||
|
// Test 1a: Critical test - without wait_barrier, commit should be applied immediately
|
||||||
|
let buffer1a = client.spbm.create_buffer(Color::from_srgb(255, 128, 0))?;
|
||||||
|
|
||||||
|
// Reset buffer tracking to detect when buffer1 gets released
|
||||||
|
buffer1.released.set(false);
|
||||||
|
|
||||||
|
// Attach new buffer and commit WITHOUT wait_barrier
|
||||||
|
surface.attach(buffer1a.id)?;
|
||||||
|
surface.commit()?;
|
||||||
|
client.sync().await;
|
||||||
|
|
||||||
|
// WITHOUT wait_barrier, the commit should be applied immediately even if barrier is set
|
||||||
|
// So buffer1 should be released immediately, without needing a vblank
|
||||||
|
if !buffer1.released.get() {
|
||||||
|
return Err(TestError::new(
|
||||||
|
"Without wait_barrier, commit should be applied immediately - buffer1 was not released",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test 2: Critical test - wait_barrier SHOULD block commit until next after_latch
|
||||||
|
// This contrasts with Test 1a above where commits were applied immediately
|
||||||
|
|
||||||
|
// Set the barrier and immediately test wait_barrier (without intermediate commits that might clear it)
|
||||||
|
fifo.set_barrier()?;
|
||||||
|
fifo.wait_barrier()?;
|
||||||
|
|
||||||
|
let buffer2_current = client.spbm.create_buffer(Color::from_srgb(0, 255, 0))?;
|
||||||
|
let buffer2_next = client.spbm.create_buffer(Color::from_srgb(0, 0, 255))?;
|
||||||
|
|
||||||
|
// First attach current buffer
|
||||||
|
surface.attach(buffer2_current.id)?;
|
||||||
|
surface.commit()?;
|
||||||
|
client.sync().await;
|
||||||
|
|
||||||
|
// Reset tracking for the buffer we want to monitor
|
||||||
|
buffer2_current.released.set(false);
|
||||||
|
|
||||||
|
// Now attach the new buffer - this should trigger the wait_barrier blocking
|
||||||
|
surface.attach(buffer2_next.id)?;
|
||||||
|
surface.commit()?;
|
||||||
|
client.sync().await;
|
||||||
|
|
||||||
|
// CRITICAL: The commit should be blocked, so buffer2_current should NOT be released yet
|
||||||
|
if buffer2_current.released.get() {
|
||||||
|
return Err(TestError::new(
|
||||||
|
"wait_barrier did not block the commit - buffer2_current was released immediately",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
// The commit was successfully blocked! This proves wait_barrier works.
|
||||||
|
// Now trigger after_latch to clear the barrier and apply the queued commit
|
||||||
|
run.state.latch(connector_id);
|
||||||
|
client.sync().await;
|
||||||
|
|
||||||
|
// After after_latch, the barrier should be cleared and the commit applied
|
||||||
|
if !buffer2_current.released.get() {
|
||||||
|
return Err(TestError::new(
|
||||||
|
"after_latch should have cleared barrier and applied commit - buffer2_current was not released",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test 3: Test tearing mode - barrier should be cleared on vblank instead of immediately
|
||||||
|
fifo.set_barrier()?;
|
||||||
|
fifo.wait_barrier()?;
|
||||||
|
|
||||||
|
let buffer3_current = client.spbm.create_buffer(Color::from_srgb(128, 128, 0))?;
|
||||||
|
let buffer3_next = client.spbm.create_buffer(Color::from_srgb(0, 128, 128))?;
|
||||||
|
|
||||||
|
// First attach current buffer
|
||||||
|
surface.attach(buffer3_current.id)?;
|
||||||
|
surface.commit()?;
|
||||||
|
client.sync().await;
|
||||||
|
|
||||||
|
// Reset tracking for the buffer we want to monitor
|
||||||
|
buffer3_current.released.set(false);
|
||||||
|
|
||||||
|
// Now attach the new buffer - this should trigger the wait_barrier blocking
|
||||||
|
surface.attach(buffer3_next.id)?;
|
||||||
|
surface.commit()?;
|
||||||
|
client.sync().await;
|
||||||
|
|
||||||
|
// Verify the commit is blocked
|
||||||
|
if buffer3_current.released.get() {
|
||||||
|
return Err(TestError::new(
|
||||||
|
"wait_barrier did not block the commit in tearing test - buffer3_current was released immediately",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Trigger latch with tearing=true - this should defer clearing to vblank
|
||||||
|
run.state.latch_tearing(connector_id);
|
||||||
|
client.sync().await;
|
||||||
|
|
||||||
|
// With tearing=true, the barrier should NOT be cleared yet, commit should still be blocked
|
||||||
|
if buffer3_current.released.get() {
|
||||||
|
return Err(TestError::new(
|
||||||
|
"In tearing mode, latch should not clear barrier immediately - buffer3_current was released",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now trigger vblank - this should clear the barrier and apply the commit
|
||||||
|
run.state.vblank(connector_id);
|
||||||
|
client.sync().await;
|
||||||
|
|
||||||
|
// After vblank, the commit should be applied
|
||||||
|
if !buffer3_current.released.get() {
|
||||||
|
return Err(TestError::new(
|
||||||
|
"vblank should have cleared barrier and applied commit - buffer3_current was not released",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
14
src/state.rs
14
src/state.rs
|
|
@ -1445,6 +1445,20 @@ impl State {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "it")]
|
||||||
|
pub fn latch(&self, connector: ConnectorId) {
|
||||||
|
if let Some(output) = self.root.outputs.get(&connector) {
|
||||||
|
output.latched(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "it")]
|
||||||
|
pub fn latch_tearing(&self, connector: ConnectorId) {
|
||||||
|
if let Some(output) = self.root.outputs.get(&connector) {
|
||||||
|
output.latched(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(feature = "it")]
|
#[cfg(feature = "it")]
|
||||||
pub async fn idle(&self) {
|
pub async fn idle(&self) {
|
||||||
loop {
|
loop {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue