diff options
| author | Adam Malczewski <[email protected]> | 2026-06-12 20:34:03 +0900 |
|---|---|---|
| committer | Adam Malczewski <[email protected]> | 2026-06-12 20:34:03 +0900 |
| commit | a112b41d51ef8b114bbbbebb59eab1972750a23c (patch) | |
| tree | 0d221f8913da50cb2609ef2961f9cb9e878b0615 /packages/kernel/tests/test_kernel.cpp | |
| parent | 8d7749516d70b8a27df4441c2b3e717de1a7a724 (diff) | |
| download | unbox-a112b41d51ef8b114bbbbebb59eab1972750a23c.tar.gz unbox-a112b41d51ef8b114bbbbebb59eab1972750a23c.zip | |
Slice 3: THE SPIKE — RMLUi→wlr_scene bridge lands, GO
Plan A verified on hardware (HD 4400/crocus): RMLUi renders into a GLES
3.2 sibling-context FBO backed by a dmabuf wlr_buffer (wlr_allocator +
EGLImage import), composited as a wlr_scene_buffer with per-frame damage.
Plan B (glReadPixels→shm) implemented and verified as runtime fallback;
auto-engages when any Plan-A precondition fails. Plan C not needed.
- Hello-world RML doc: text, data-bound frame counter, pointer input
proof (hover/:active) — verified upright on screen via screenshot after
fixing the classic FBO Y-flip (buffer-level V-flip keeps display ==
document coords for input); position-aware orientation guard added.
- Temporary spike surface: Options::ui_spike + frame-count/orientation
probes, host-bin --ui-spike flag; replaced by the real ui substrate
contract in slice 4+.
- kernel suite 6 cases / 416 assertions green; ASan/UBSan clean in our
code (Mesa leak noise + 2 benign UBSan downcast reports inside vendored
RMLUi are known); idle RSS ≈83 MiB.
- Deferred (notes/plan.md §7): glFinish→EGL fence + swapchain; dmabuf
render-format negotiation (private API in wlroots 0.20).
Diffstat (limited to 'packages/kernel/tests/test_kernel.cpp')
| -rw-r--r-- | packages/kernel/tests/test_kernel.cpp | 72 |
1 files changed, 72 insertions, 0 deletions
diff --git a/packages/kernel/tests/test_kernel.cpp b/packages/kernel/tests/test_kernel.cpp index 0172901..6f8ce9b 100644 --- a/packages/kernel/tests/test_kernel.cpp +++ b/packages/kernel/tests/test_kernel.cpp @@ -27,3 +27,75 @@ TEST_CASE("server boots and shuts down on the headless backend") { } // Destruction runs the full tinywl shutdown sequence. } + +TEST_CASE("ui spike defaults off and is the slice-2 server") { + setenv("WLR_BACKENDS", "headless", 1); + setenv("WLR_RENDERER", "pixman", 1); + + auto server = unbox::kernel::Server::create({}); + CHECK(server->ui_spike_frame_count() == 0); + for (int i = 0; i < 3; ++i) { + CHECK(server->dispatch(10)); + } + CHECK(server->ui_spike_frame_count() == 0); +} + +TEST_CASE("ui spike boots, renders frames, and shuts down cleanly") { + // Drive the RMLUi -> wlr_scene bridge on the headless backend with the + // gles2 renderer so the real GL path is exercised (Plan A attempted, + // Plan B as fallback). The headless backend uses an EGL render node; if + // GL is unavailable the bridge disables itself gracefully and frame_count + // stays 0 (asserted as the no-crash fallback). A headless output must be + // created so the frame handler (which drives tick()) fires. + setenv("WLR_BACKENDS", "headless", 1); + setenv("WLR_RENDERER", "gles2", 1); + setenv("WLR_HEADLESS_OUTPUTS", "1", 1); + + auto server = unbox::kernel::Server::create({.ui_spike = true}); + CHECK(!server->socket_name().empty()); + + // Pump enough turns for the headless output to emit frames. + for (int i = 0; i < 200; ++i) { + CHECK(server->dispatch(10)); + } + + const int frames = server->ui_spike_frame_count(); + INFO("ui_spike_frame_count() = ", frames); + // Either the bridge ran (frames advanced) or it disabled itself on a + // headless box without a usable GL path. Both are acceptable; a crash is + // not. Clean shutdown is exercised on destruction below. + CHECK(frames >= 0); +} + +TEST_CASE("ui spike submits an upright (non-flipped) buffer") { + // Orientation regression guard. The spike document carries distinctive + // solid bands at its top and bottom edges; on the CPU-readback (Plan B) + // path the bridge inspects the SUBMITTED buffer and reports +1 if the top + // band is in the top rows (upright), -1 if vertically flipped. GL's + // bottom-left framebuffer origin vs the wlr_buffer top-first convention + // makes the flip the default failure mode, so this must never silently + // regress. Force the shm path so the readback exists; if GL is + // unavailable the spike disables itself and orientation() returns 0 + // (skipped, not failed — same graceful-degrade contract as above). + setenv("WLR_BACKENDS", "headless", 1); + setenv("WLR_RENDERER", "gles2", 1); + setenv("WLR_HEADLESS_OUTPUTS", "1", 1); + setenv("UNBOX_UI_SPIKE_FORCE_SHM", "1", 1); + + auto server = unbox::kernel::Server::create({.ui_spike = true}); + for (int i = 0; i < 200; ++i) { + CHECK(server->dispatch(10)); + } + + const int orient = server->ui_spike_orientation(); + INFO("ui_spike_orientation() = ", orient); + // MUST NOT be flipped. +1 = upright (the bridge ran), 0 = indeterminate + // (no GL path on this box). A flip (-1) is the bug and fails here. + CHECK(orient != -1); + if (server->ui_spike_frame_count() > 0) { + // The shm bridge ran: orientation must be positively confirmed upright. + CHECK(orient == 1); + } + + unsetenv("UNBOX_UI_SPIKE_FORCE_SHM"); +} |
