mirror of
https://github.com/Droid-Deck/DroidDeck.git
synced 2026-11-11 02:51:10 +00:00
215 lines
13 KiB
Markdown
215 lines
13 KiB
Markdown
# Experimental Quest 2/3/3S support
|
|
|
|
Quest runs DroidDeck as a 2D panel, with an experimental immersive SteamVR path
|
|
in the standard app. Quest 2 has a separate experimental Adreno 6xx driver path.
|
|
|
|
Quest 2 selects `turnip26.3.0-quest2`, a pinned Banners-Turnip A6xx/A7xx build
|
|
with the same GPUOBJ adapter. It does not select the Quest 3 A7xx binary.
|
|
The first Quest 2 launch applies manual driver selection and the SurfaceControl
|
|
workaround, even if `questDefaultsV1` exists from an earlier install. Later user
|
|
choices are retained. The separate `turnip26.3.0-a6xx` bundle provides a plain
|
|
Adreno 6xx offline fallback for other devices; their driver Auto behavior remains.
|
|
Neither family-wide hardware coverage nor Quest 2 operation has been verified.
|
|
|
|
The A6xx input URL and SHA-256 are pinned in `tools/quest/build-driver.py`.
|
|
Both A6xx bundles are prepared by the existing local/CI wrapper build step and
|
|
covered by runtime-input attestation. Quest 3/3S retains its original driver.
|
|
Early installation and compositor breadcrumbs are written to `driver-setup.txt`
|
|
in private files and, when available, the app's external files directory. They
|
|
do not require a Steam session. The existing Quest Java crash file now also
|
|
applies to Quest 2; native crashes still require Android logcat.
|
|
|
|
Install the standard DroidDeck APK on Quest 3/3S. Device detection on the
|
|
first launch selects manual drivers, the bundled
|
|
`turnip25.1.0-quest1` Android driver and the default Linux driver. It enables
|
|
`hdr.steam` to use the existing SurfaceControl presentation workaround from
|
|
[the Quest report](https://github.com/Droid-Deck/DroidDeck/issues/245).
|
|
Presentation also has a Quest-specific layer path for complete SDR scenes and
|
|
the black backdrop, independent of this HDR preference; see
|
|
[the presentation candidate and headset test](quest-presentation-test.md).
|
|
These defaults apply once; subsequent user changes are retained. Colour
|
|
accuracy and headset performance still need testing.
|
|
|
|
Selecting driver Auto also uses the bundled Quest display driver and runtime
|
|
default, without downloading a generic Adreno driver pair. Manual selections
|
|
remain available and are not replaced on startup. Android's Auto-by-GPU fallback
|
|
uses the Quest bundle too; the external `Download/droiddeck-driver` override
|
|
still takes precedence. Fully close and restart DroidDeck after changing the
|
|
display driver, since the compositor loads it once per app process.
|
|
|
|
Both the Android driver wrapper and the Linux session preload use
|
|
`tools/quest/kgsl_compat.c`. Successful legacy allocation/free calls are
|
|
unchanged. Only `EACCES`/`EPERM` failures retry through `GPUOBJ_ALLOC`,
|
|
`GPUOBJ_INFO` and `GPUOBJ_FREE`. The Android wrapper depends on the original
|
|
bundled Turnip binary. Unhandled controller ioctls forward to the next
|
|
preload hook rather than skipping it with a raw syscall.
|
|
|
|
`tools/build_local.sh` and CI build the wrapper from source using the pinned
|
|
Android NDK; the normal preload builds include the shared Linux adapter.
|
|
Runtime input verification checks the resulting driver archive and invalidates
|
|
stale prepared assets after compatibility sources change. To rebuild only the
|
|
Android driver archive:
|
|
|
|
```sh
|
|
python3 tools/quest/build-driver.py "$ANDROID_HOME/ndk/27.3.13750724"
|
|
```
|
|
|
|
Quest compatibility is included in the standard APK; no separate Quest
|
|
package is needed. Other devices retain their existing defaults. The remaining
|
|
performance-name APK variants continue to use separate app data and bwrap sockets.
|
|
|
|
Quest 3 testers have successfully launched games.
|
|
|
|
On a headset, check the Steam login panel, sign-in, game installation and
|
|
then an owned game's rendering/input. Capture the device/OS, session logs,
|
|
`files/quest-startup.txt` and any `files/quest-last-crash.txt` failure.
|
|
Host allocation and preload-chain tests do not establish headset operation.
|
|
|
|
## Experimental immersive SteamVR
|
|
|
|
In a running Steam session, open the session menu's SteamVR tab, install SteamVR
|
|
once, then choose Enter SteamVR. Cardboard has its own tab and can remain off. On supported Quest devices this opens the standard app's
|
|
OpenXR activity. The guest driver receives headset poses, Touch controller input
|
|
and display timing, and returns owned stereo images tagged with the latest eye poses.
|
|
SteamVR haptics return through the tracking bridge. No root or separate XR APK
|
|
is required. Other devices retain their existing SteamVR start behavior.
|
|
|
|
The left Touch menu button is SteamVR's system button and toggles its dashboard.
|
|
The right Meta button remains reserved by the headset OS.
|
|
|
|
The driver supplies SteamVR's version-5 chaperone configuration through
|
|
`Prop_DriverProvidedChaperoneJson_String`, following Valve's driver API. Standing
|
|
tracking keeps the OpenXR stage coordinates; seated tracking starts at the head's
|
|
position and yaw. No synthetic play area or collision walls are advertised.
|
|
Without a seated transform, SteamVR reported valid raw and standing poses but
|
|
invalid seated poses, causing Thumper to remain behind its tracking-loss fade.
|
|
On Quest, supplying both transforms made all three origins valid; seated height
|
|
started within a millimetre of zero while standing and raw height stayed unchanged.
|
|
|
|
Quest uses SteamVR's normal Linux compositor through its display component.
|
|
The Vulkan layer captures only the HMD display swapchain, retains its present
|
|
waits, and chains a GPU copy before presentation. Three bounded capture buffers
|
|
allow rendering and transfer to overlap. Where native GLES supports
|
|
`GL_EXT_external_buffer` and texture views, Android allocates three
|
|
`AHardwareBuffer` BLOBs and passes their DMA-BUF allocations to Linux through
|
|
`SCM_RIGHTS`. Vulkan imports them as buffers and transfers foreign queue-family
|
|
ownership before and after copying the display image. A native compute shader
|
|
reads each shared buffer into a private RGBA8 texture; an sRGB texture view
|
|
preserves hardware transfer decoding. Native GPU completion precedes the
|
|
acknowledgement, so Linux cannot overwrite an active reader. This removes the CPU
|
|
pixel copy and upload, but still includes GPU copies and explicit completion waits.
|
|
|
|
If capability negotiation or buffer import fails before frames are submitted,
|
|
the existing CPU transport remains available. It brackets the DMA-BUF read with
|
|
`DMA_BUF_IOCTL_SYNC`, copies to owned pixels, then acknowledges and uploads them.
|
|
Both channels authenticate the peer UID and bound buffer sizes. Missing
|
|
acknowledgements after submission quarantine the transfer and fail presentation
|
|
instead of falling back while a reader might still own a buffer.
|
|
|
|
The helper temporarily enables SteamVR's Linux Vulkan asynchronous queue and disables motion
|
|
smoothing, backing up those preferences and restoring them on stop or the next start
|
|
after an interrupted session. It excludes the pinned Steam Frame package's optional CVP backend,
|
|
whose library constructor exits on Quest's unsupported SoC. The original library
|
|
is retained with a `.droiddeck-disabled` suffix. SteamVR binaries are not patched.
|
|
Cardboard retains its existing direct-mode path; `questframes` remains an explicit
|
|
diagnostic override for the earlier driver presenter.
|
|
|
|
The panel compositor detaches before OpenXR starts displaying guest frames.
|
|
Session suspension accounts for the immersive activity while the panel is hidden.
|
|
Closing the immersive activity stops SteamVR. The panel remains available for
|
|
returning to the normal Steam session.
|
|
The immersive activity belongs to the Steam session that opened it. Ending that
|
|
session closes XR; a failed startup or stopped SteamVR also returns to the panel
|
|
with an error instead of leaving a black scene. Enter SteamVR can then start a
|
|
fresh connection.
|
|
|
|
SteamVR scene-application and dashboard events also guide Gamescope's game focus.
|
|
While the dashboard is hidden, the guest agent resolves the live scene process's
|
|
Steam app ID and promotes its focusable window. Opening the dashboard or ending
|
|
the scene restores the previous order if no other client has replaced it. This
|
|
keeps keyboard/gamepad input with the VR scene without game-specific IDs.
|
|
|
|
With Steam already running, `python3 tools/test_quest_xr.py --serial SERIAL`
|
|
launches the immersive activity and verifies advancing owned images, tracked
|
|
6DoF, focus and SteamVR status. Add `--require-controllers` while holding both
|
|
controllers. Results include telemetry and logcat in `build/questxr-validation`.
|
|
Host tests and APK installation alone do not establish headset operation.
|
|
|
|
On Quest 3, a 45-second hardware run maintained focused tracked 6DoF, both
|
|
controllers active, and advancing stereo images. A headset tester confirmed
|
|
controller models and trigger animation. That run delivered about 8.5 new stereo
|
|
images per second despite a 90 Hz XR session.
|
|
That earlier measurement used the driver-direct presenter. With SteamVR's normal
|
|
compositor, a native Vulkan scene passed its first submission and continued
|
|
submitting stereo frames. Thumper and The Thrill of the Fight produced clean
|
|
immersive scenes. SUPERHOT's Windows OpenXR build initially showed shared-image
|
|
corruption: DXVK supplied a restricted mutable-image format list while SteamVR
|
|
created its corresponding opaque shared images without one. Turnip can select
|
|
different memory layouts for those descriptions. On the Quest bridge, the layer
|
|
normalizes the known opaque-FD stereo-array creation chains to SteamVR's unrestricted list,
|
|
without changing application-owned single-layer OpenVR images, DMA-BUF/modifier
|
|
images or other targets. A headset capture then
|
|
showed clean SUPERHOT scene geometry; interactive gameplay remains unverified.
|
|
A subsequent 60-second shared-buffer dashboard run delivered about
|
|
43 new stereo images per second. Reusing Android pixel buffers raised the next
|
|
60-second run to about 68 received and 65 uploaded images per second, with valid
|
|
focused tracking and no rejected frames. These dashboard results do not establish refresh-rate gameplay: game input,
|
|
sustained game performance, and exact association with SteamVR's final compositor
|
|
pose still require validation.
|
|
|
|
The native GPU-buffer channel passed 256 full-size buffer reuse cycles, including
|
|
head/tail pixel checks and sRGB texture-view decoding. A subsequent SUPERHOT
|
|
scene run delivered about 43 new stereo images/s and 58 XR draws/s over one
|
|
minute, with focused tracking and no rejected frames. The earlier CPU-path
|
|
SUPERHOT run uploaded about 28 images/s; these are separate runs, not a controlled
|
|
benchmark. Capturing the source and eye images can delay frame consumption;
|
|
the GPU receiver acknowledges stale frames without reading them and keeps the
|
|
last private image until a fresh frame arrives. Missing completion still fails
|
|
closed. `frame_verify` explicitly selects the CPU path for byte comparison.
|
|
|
|
The frame channel supports linear and sRGB RGBA8. SteamVR's final display image
|
|
uses sRGB display values; Android decodes those during texture sampling and writes
|
|
to an sRGB eye swapchain, encoding the display values once. A Quest color-chart
|
|
capture verified that 64, 128, 192 and 224 grey patches retain those values;
|
|
the previous sRGB source upload darkened them to approximately 13, 55, 134 and
|
|
190. The transport remains 8-bit SDR; this correction does not add HDR or higher
|
|
precision gradients.
|
|
|
|
When available, `XR_EXT_performance_settings` requests sustained-high CPU and GPU
|
|
performance for the running OpenXR session. These are runtime-managed hints;
|
|
device power and thermal policies remain authoritative. Unsupported or rejected
|
|
requests do not prevent startup. This does not change render resolution.
|
|
|
|
The manifest declares the supported Quest models using Meta's standard
|
|
`com.oculus.supportedDevices` metadata. Without it, Quest 3 used compatibility
|
|
mode and its refresh-rate API returned an empty capability list. With the
|
|
declaration, the test runtime reported 72/80/90/120 Hz; a fresh normal launch
|
|
used 72 Hz in both OpenXR telemetry and SteamVR's HMD property. DroidDeck leaves
|
|
the refresh choice to the runtime. No forced refresh override is included.
|
|
|
|
The OpenVR driver maps OpenXR's asymmetric eye FOV to OpenVR's lower/upper Y
|
|
projection bounds. A live Quest 3 check confirmed that SteamVR and native
|
|
OpenXR now report the same vertical projection center; the previous mapping
|
|
mirrored that offset. This applies to every VR scene, without game-specific
|
|
rendering changes.
|
|
|
|
### Experimental flat games in SteamVR
|
|
|
|
A game Steam lists without any VR support, launched while SteamVR runs, opens as
|
|
a SteamVR panel automatically: the Proton and FEX launchers ask Steam whether the
|
|
app supports VR (`droiddeck-agent --steam-supports-vr`; optional-VR games such as
|
|
Thumper launch normally) and wrap it in
|
|
`droiddeck-steamvr-game` (`droiddeck-steamvr-flat`). Adding
|
|
`droiddeck-steamvr-game %command%` to a game's launch options does the same by hand.
|
|
This runs a nested, unmodified Gamescope OpenVR backend for that game. Gamescope
|
|
owns the GPU overlay, placement and input routing; no CPU screen capture or
|
|
PipeWire service is used. The existing DroidDeck Vulkan synchronization layer
|
|
remains enabled. On Quest, a connected XR receiver selects Gamescope's Vulkan
|
|
texture sharing instead of DMA-BUF modifiers. Explicit environment overrides
|
|
are preserved; other devices retain the upstream modifier default.
|
|
|
|
An upstream GPU cube overlay has been observed on Quest 3. Flat-game stability,
|
|
controller interaction and performance still require hardware validation. This
|
|
uses Valve's public Gamescope backend; the exact Steam Frame OS launch setup
|
|
has not been verified.
|