fix(quest): stabilize desktop and Steam layer presentation Keep Quest SDR scenes and the black backdrop off the Android Vulkan swapchain regardless of session order or HDR preferences. Only report the first frame after client content is submitted. Add backdrop lifecycle and first-frame regression coverage. Record the user's successful Quest 3 Test 2 result. Co-authored-by: xXJsonDeruloXx <danielhimebauch@gmail.com>
7.1 KiB
Quest presentation candidate Test 2 — 2026-10-13
Base: main at a394dfd31b2102e20dd99c6f51b4873bbdedab05.
Reference APK: DroidDeck-PR669-A6xx-Test2(1).apk, SHA-256
95ffb5ea6ae61d6a92c4ce9ad46f329d77a0f113ebbaabc6da30d6c8bd951154.
Its embedded source revision is d3f3439bbc45ca67de86d0093356a449c2c74600.
Evidence and scope
Reported symptoms: Desktop shows vertical corruption; Steam has green marks in its black borders; starting Desktop first also corrupts subsequent Steam, and closing Steam can expose corruption when returning to the menu. The supplied modified PR669 APK reportedly avoids the transition problem.
Source inspection confirms that the compositor outlives sessions. The old
Quest workaround enables only hdr.steam, and the initial HDR-gated layer
selection happens only at compositor startup. Whole desktops and black borders
can still use the Android Vulkan swapchain even when a fullscreen game uses a
SurfaceControl layer. This is consistent with the reported symptoms, but is not
an on-device reproduction or proof of the exact driver defect.
The supplied APK has the same Steam-only preference initialization and the
same bundled Quest 3 Turnip binary as main (SHA-256
af0d6bb38756eace17b43311c5c61a7ef3dc6e83d5cb8a63577d6a1d10c0aed0).
It additionally supports a Quest 2 driver. Therefore the APK comparison does
not establish a new Quest 3 driver regression. Device, installed driver,
persistent preferences and session logs still matter.
Test 1 startup regression and Test 2 correction
The Test 1 user confirmed a Quest 3 and a black window after runtime setup.
The supplied state report identifies 0.4.0-questdisplay-test1, runtime r9
installed, an active guest, installing=null, no recorded failure, and saved
session artifacts. The session was suspended at capture. Suspension alone does
not establish the cause of the original black window. The retained main log
starts after the test window was closed, so the startup diagnostics are absent.
Source review found a Test 1 bug: vkp_render unconditionally signaled the first
frame after successfully showing the black backdrop, even with no client draws.
The Android listener then hid the loading UI and marked Steam's window as shown.
This also happened when the content-layer operation returned a successful no-op
(no visible geometry or no available pool slot). Consequently firstFrame=true
in Test 1 is not reliable evidence that Steam had displayed a frame.
Test 2 removes the backdrop notification. The existing content-layer presenter
owns the notification after submitting client content. A host regression test
compiles the production vkp_render entrypoint with controlled layer outcomes;
it failed on Test 1's empty scene and passes with this change. It also covers
successful no-ops, content presentation, errors and the existing non-Quest,
frame-generation and Cardboard routes. All seven focused host tests pass.
On 2026-10-13, the user tested Test 2 on Quest 3 and confirmed that it was functioning as intended. This is a user-reported hardware result; a separate pass/fail record for each checklist item below was not supplied. Broader headset, game, colour and performance coverage remains outstanding. The user's separate Quest 2 success report concerns the Adreno 6xx compatibility PR, not this build.
Candidate change
- Recognize Quest 2/3/3S for presentation independently of HDR and session order. This does not install an A6xx driver or expand main's Quest 2 driver support.
- Enable the layer protocol before the first client connects.
- Compose complete SDR scenes into the existing GPU-rendered gralloc layer, including Desktop's multiple windows. Keep the existing path for a fullscreen AHB that cannot be imported into the compositor.
- Present the black backdrop as an opaque SurfaceControl layer, initialized once through a CPU-mapped 16-by-16 buffer. Game/desktop rendering stays on the GPU. The backdrop does not use a Turnip Android swapchain.
- Use actual native-window dimensions for layer geometry without creating a Vulkan swapchain solely for sizing.
- Retire the backdrop on detach/rebind and hide it before a real swapchain frame. Reject a black buffer whose CPU lock/unlock failed.
Other devices retain their normal presentation paths. Frame generation and Cardboard still use the original swapchain path and are outside this test's coverage; leave them off for the first test. HDR negotiation itself is unchanged.
Validation
The full ARM64 native compositor, including the existing frame-generation engines, is compiled from this checkout with Android NDK r27d, API 26 and 16 KB ELF alignment. Host tests exercise the actual backdrop implementation with mock Android APIs: initialized pixels and padded rows, immutable reuse, resize, hide/show, retirement/recreation, absent windows, allocation/lock/ unlock/transaction failure recovery, and the Quest device gate. These checks cannot validate Horizon's compositor, colours, latency or graphics output.
Run the focused host tests (LeakSanitizer may need ASAN_OPTIONS=detect_leaks=0
when the test runner itself operates under ptrace):
python3 -m unittest tools.tests.test_quest_presentation tools.tests.test_quest_kgsl -v
The test APK uses the verified official CI APK for this exact base commit,
with only lib/arm64-v8a/libdroiddeckwayland.so rebuilt and replaced, an added
assets/quest-presentation-test.json provenance record, and a new APK signature.
DEX, other native libraries and runtime assets stay unchanged. The manifest is
repackaged with the repository's variant helper as com.droiddeck.questdisplaytest
so it can install beside the normal app and the supplied APK, whose signing key
is different. Provider authorities and the signature permission are renamed too.
It uses separate app data and needs its own initial setup. It is not a complete
Gradle rebuild. The displayed version is 0.4.0-questdisplay-test2. It is signed
using the repository's public AOSP test key. The launcher label remains DroidDeck;
check the version in app info to distinguish the test installation.
Test 2 retains Test 1's package, version code and signing certificate. Install it over Test 1 without uninstalling or clearing data; its installed runtime and session logs are retained. The original main application remains separate.
Headset test
Fully close DroidDeck before starting. Keep the same driver and resolution settings that produced the problem; turn off frame generation and Cardboard.
- Cold launch Desktop. Check the desktop, windows and black borders.
- Switch Desktop to Steam without restarting the app.
- Close Steam and return to the DroidDeck menu.
- Fully close the app, then launch Steam first; inspect the top/bottom borders.
- Switch to Desktop and repeat both transitions several times.
- Resize the Quest panel and suspend/resume the app once.
Record the headset model and whether each step is fixed. If it fails, export
that session's logs, including wayland.log and device information. Look for
Quest presentation: display layers for Steam and desktop to confirm this
renderer was loaded. Test 2's reported Quest 3 result is recorded above.