summaryrefslogtreecommitdiffhomepage
path: root/packages/kernel/kernel.md
diff options
context:
space:
mode:
Diffstat (limited to 'packages/kernel/kernel.md')
-rw-r--r--packages/kernel/kernel.md79
1 files changed, 79 insertions, 0 deletions
diff --git a/packages/kernel/kernel.md b/packages/kernel/kernel.md
index 18d7c85..24a37c9 100644
--- a/packages/kernel/kernel.md
+++ b/packages/kernel/kernel.md
@@ -7,6 +7,15 @@ only), keyboard/pointer/touch via one wlr_cursor path. Slice-2 keybindings:
Alt+Escape = terminate, Alt+F1 = cycle. Slice 4 splits shell policy out
into extensions.
+Slice-3 state: THE SPIKE landed on **Plan A** (RMLUi -> dmabuf-backed
+wlr_buffer -> wlr_scene_buffer), with Plan B (FBO + glReadPixels into a
+data-ptr wlr_buffer) as a verified runtime fallback. All bridge state is
+private in `src/ui_spike.{hpp,cpp}` + the adapted GLES3 renderer
+`src/rmlui_renderer_gl3.{h,cpp}`. Public surface delta: `Options::ui_spike`
++ `Server::ui_spike_frame_count()` (both TEMPORARY, replaced by the real ui
+substrate in slice 4+). Driven from the output frame handler; renders only
+when `ui_spike != nullptr`. Host-bin does NOT yet wire the Option.
+
Gotchas the headers can't express:
- **`wlr.hpp` blanks `static` around the wlr includes.** wlroots headers
@@ -32,3 +41,73 @@ Gotchas the headers can't express:
surface-local motion coords; a surface moving mid-touch (interactive
grab) skews them. Acceptable until slice 5's input routing.
- Everything runs on the single `wl_event_loop` thread.
+
+Slice-3 spike gotchas (EGL/dmabuf — read before touching `ui_spike.cpp`):
+
+- **The sibling GLES 3.2 context shares the EGLDisplay, NOT GL objects.**
+ `eglCreateContext` is called with `share_context = EGL_NO_CONTEXT` on
+ `wlr_egl_get_display(wlr_gles2_renderer_get_egl(renderer))`. Buffers cross
+ the boundary ONLY as dmabuf/EGLImage (Plan A) or CPU copy (Plan B) — never
+ shared GL handles. The wlr renderer's current EGL context/surfaces are
+ saved before `eglMakeCurrent` and restored after every tick/teardown;
+ forgetting the restore corrupts wlr's own rendering.
+- **The context is surfaceless** (`eglMakeCurrent(dpy, EGL_NO_SURFACE,
+ EGL_NO_SURFACE, ctx)`, requires `EGL_KHR_surfaceless_context` — present on
+ crocus). RMLUi's `RmlUi_Renderer_GL3` hardcodes `glBindFramebuffer(0)` in
+ `EndFrame()`; framebuffer 0 is INCOMPLETE in a surfaceless context. The
+ adapted copy adds `SetOutputFramebuffer()` so EndFrame composites into the
+ bridge's own offscreen FBO instead. Re-apply that delta on any RMLUi bump.
+- **GLES path selection is `-DUNBOX_RMLUI_GLES`** (kernel meson.build,
+ scoped to the kernel lib). It mirrors upstream's `__ANDROID__` branch
+ (`#version 320 es`, `<GLES3/gl32.h>`, CLAMP_TO_EDGE, no sRGB framebuffer)
+ WITHOUT defining the `__ANDROID__` builtin (which would poison the whole
+ TU). Four upstream `#if ...__ANDROID__` guards were widened to also test
+ `UNBOX_RMLUI_GLES`; re-audit them on a bump.
+- **Plan A dmabuf import is single-plane LINEAR.** We allocate ARGB8888
+ (FourCC 'AR24') with a LINEAR-only modifier list via
+ `wlr_allocator_create_buffer`, `wlr_buffer_get_dmabuf`, then
+ `eglCreateImageKHR(EGL_LINUX_DMA_BUF_EXT)` + `glEGLImageTargetTexture2DOES`
+ as the FBO color attachment. Preconditions checked at runtime (allocator
+ DMABUF cap, `EGL_EXT_image_dma_buf_import`, the two entrypoints); any miss
+ or an incomplete FBO falls back to Plan B with a log line. Render formats
+ are NOT public in wlroots 0.20 (`wlr_renderer_get_render_formats` is
+ private; only `get_texture_formats` is exported), so we pick the format by
+ hand — revisit if a future GPU rejects linear ARGB8888 as a render target.
+- **Submission sync is `glFinish()` (spike fidelity), not a fence.** Plan A
+ must ensure GL writes land before the compositor samples the shared
+ dmabuf. The real substrate should use an EGL fence
+ (`EGL_KHR_fence_sync` is advertised) instead of the full pipeline stall.
+- **Plan B's wlr_buffer is a custom `WLR_BUFFER_CAP_DATA_PTR` impl**
+ (`ShmBuffer` wrapping a `std::vector`, via `<wlr/interfaces/wlr_buffer.h>`).
+ RMLUi outputs premultiplied RGBA8 (R,G,B,A byte order); the buffer is
+ FourCC 'AR24' = little-endian {B,G,R,A}, so the copy swaps R<->B. The
+ alpha is already premultiplied, which wlroots expects.
+- **`UNBOX_UI_SPIKE_FORCE_SHM=1`** forces the Plan-B path even where Plan A
+ works — kept as fallback-test instrumentation; harmless in production.
+- **Headless (pixman) disables the spike**: no gles2 renderer ⇒ no
+ EGLDisplay ⇒ `start_ui_spike()` no-ops. Headless+gles2 (render node
+ present) DOES exercise Plan A — verified.
+- **GL framebuffer origin is bottom-left; wlr_buffer scan-out is top-first.**
+ RMLUi already maps document-y=0 to the GL framebuffer top via
+ `ProjectOrtho(0,w,h,0,...)`, but reading the FBO out (glReadPixels, Plan B)
+ or scanning out the dmabuf (Plan A) yields row 0 = GL bottom = document
+ bottom ⇒ the whole document composited **upside-down** (caught by grim).
+ Fix: the adapted renderer's `EndFrame()` flips V on the FINAL composite
+ into the output FBO when `SetOutputFramebuffer(fbo, /*flip_y=*/true)`
+ (vertex-uv flip on the passthrough fullscreen quad). Chosen over a scene
+ transform or a flipped projection because (a) it makes the SUBMITTED buffer
+ genuinely top-first — so the orientation assertion can read it back
+ directly and so display == document coords, leaving pointer input
+ un-transformed (an on-screen hover hits the button in document space); and
+ (b) it is ONE localized change that applies identically to Plan A and Plan
+ B and needs no matching scissor/clip-rect change (scissor only affects
+ intermediate layer rendering, not the final composite). Re-apply on an
+ RMLUi bump alongside the `SetOutputFramebuffer` delta. NOTE: a flip done as
+ a display-only transform would have left on-screen hit-testing wrong even
+ while a document-space input test passed — verify display+input together.
+- **Orientation regression guard**: the spike document carries distinctive
+ full-width solid bands at its top (`#18e0a0`) and bottom (`#e09018`) edges.
+ `UiSpike::check_orientation()` (exposed as `Server::ui_spike_orientation()`)
+ inspects the Plan-B readback and returns +1 upright / -1 flipped / 0
+ indeterminate. The `kernel` suite asserts it is never -1 (and ==1 when the
+ bridge ran). Position-aware, not just color-aware — a flip can't slip past.