A little atmosphere

Read with sound?

Sound brings the lights to life. Enable it at half volume, or continue silently. Use the sound control to change it later.

A starting point for more open VR

Help change what standalone VR can be.

DroidXR starts with a working PSVR 2 route powered by an Android phone. I want to turn that into VR people can run, understand and improve on hardware they choose.

If you build drivers, OpenXR apps, or have another headset to explore, come help build the next phone-powered VR route.

Hardware integration and software glue by AstroPC

DroidXR

VR on your terms.
PSVR 2 is the starting point.

I want to choose what runs my headset, keep the session on hardware I control, and keep using the good hardware I already own. DroidXR is my attempt to make that practical, starting with PSVR 2 powered by an Android phone.

The aim is more choice over the software, less dependence on one closed ecosystem, and local operation with clearer control over your data. There is working hardware behind the idea, and plenty left to build.

Working on my phone: Skyrim VR, eye tracking, eye-tracked foveation and SGSR2.
Built around PSVR 2, the Linux runtime and the phone I already owned.

The breakthrough / Recorded on my setup

That's Skyrim VR. Running from my phone.

This is the point where the idea became real for me. Skyrim VR runs from my phone, with eye tracking and eye-tracked foveation working in PSVR 2. SGSR2 processing works well too and improves the image. I wanted to keep the headset I liked and take the computing with me. Here it is.

Original recording · 1080p / 60 fps

My recording shows the two eye views, moving gaze overlays and Skyrim gameplay. It compares two versions of the rendering. It plays muted when you scroll it into view; the controls let you pause or turn its sound on.

The phone runs the game and the Linux VR stack. PSVR 2 supplies the OLED display, tracking and controls.

A separate test / Meta Quest experiment

Another headset. Another way to make it my own.

I also tried a different route with my Quest 3: a modified WiVRn client, USB Ethernet, and the headset's Wi-Fi switched off. In my setup, the game streamed over that local wired connection. It didn't need DisplayPort; Quest was receiving compressed video through the network.

Original recording · 1080p / 60 fps

My recording, shown at 4× speed. Choose the speed button to watch at the original pace. It starts muted when the player comes into view; sound and fullscreen are optional.

This gave me another way to explore the same idea: choose how the headset connects and what drives the experience. WiVRn provides the streaming work behind this route. Setup and network notes ↗

Working on the phone / Eye-tracked rendering

Put the GPU work where it matters.

Once the headset was running, I wanted the phone to spend its graphics work where I was actually looking. Eye tracking and eye-tracked foveation now work in my phone-powered Skyrim setup.

HOW THE RENDERING CONNECTS

Follow the signal.

Gaze guides detail. The image takes its own route.

From eye tracking to the image in PSVR2Gaze from PSVR2 goes through Monado to a shared record. DXVK reads that record for game shading maps. Monado reads it separately for SGSR2 zones. Graphics go from Skyrim through DXVK and the game Turnip ICD. OpenComposite submits rendered eye views through OpenXR to Monado. The compositor reconstructs, sharpens and distorts the image, uses a separate Turnip ICD, and presents through Android DRM/KMS and later DSC to PSVR2. OpenVR calls use a separate dashed route.GAZE / CONTROLGRAPHICS / VIEWSOpenVR callsFINAL IMAGE / DISPLAYDXVK / Monado: Linux container. Display path: Android kernel.Gaze control and the rendered image remain separate paths.PSVR2 eye trackingGaze input from the headsetMonado gaze bridgeProjects eye coordinatesShared gaze recordRead by DXVK and MonadoSkyrim VRD3D11 graphicsCustom DXVKD3D11 → VulkanGaze shading mapsGame Turnip ICDRenders eye viewsand depthOpenCompositeOpenVR → OpenXRSubmits eye viewsMonadoSGSR2 → RCASThen lens distortionCompositor ICDSeparate TurnipDisplay pathDRM/KMS → DSCLater link compressionPSVR2Final eye imagesFrom eye tracking to the image in PSVR2Gaze from PSVR2 goes through Monado to a shared record. DXVK reads that record for game shading maps. Monado reads it separately for SGSR2 zones. Graphics go from Skyrim through DXVK and the game Turnip ICD. OpenComposite submits rendered eye views through OpenXR to Monado. The compositor reconstructs, sharpens and distorts the image, uses a separate Turnip ICD, and presents through Android DRM/KMS and later DSC to PSVR2. OpenVR calls use a separate dashed route.GAZE / CONTROLGRAPHICS / VIEWSDisplay path: Android kernel.PSVR2 receives the final images.PSVR2 eye trackingGaze input from the headsetMonado gaze bridgeProjects eye coordinatesShared gaze recordRead by DXVK and MonadoSkyrim VRD3D11 graphicsCustom DXVKD3D11 → VulkanGaze shading mapsGame TurnipRenders eye viewsand depthOpenCompositeOpenVR → OpenXRSubmits eye viewsMonadoSGSR2 → RCASLens distortionCompositor ICDSeparate TurnipDisplay pathDRM/KMS → DSCLink compressionPSVR2Final eye images
Graphics / imageGaze controlVR calls
The music sets the pace. Follow each pulse to the next part. Gaze guides game shading and image reconstruction separately. The pulse travel times illustrate the flow; DSC acts later on the display link.
Read the signal paths

PSVR2 gaze → Monado bridge → shared gaze record. DXVK reads it for selected shading-rate maps; Monado reads it for SGSR2 zones.

Skyrim D3D11 → custom DXVK → game Turnip ICD → rendered views via OpenComposite / OpenXR → Monado SGSR2, RCAS and lens distortion → compositor Turnip ICD → Android DRM/KMS and DSC → PSVR2.

OpenVR calls go directly from the game to OpenComposite. The separate radius record controls both foveation zones; these arrows do not imply exact geometric alignment.

Monado, the VR runtime, supplies where my eyes are looking. My modified DXVK translates the game's graphics commands and uses that gaze to move a shading-rate map: a guide to where the game should keep detail and where it can shade more coarsely. The aim is to spend less GPU work on the periphery and keep the part I'm looking at clear.

SGSR2 reconstructs the image before it reaches the headset, followed by a sharpening pass. I tested it on the phone, and it works well and improves the image. Together, these are useful parts of the setup I wanted to build.

We've recovered and matched the source behind the phone-tested graphics build. The source patches and build instructions are available now. Read how the rendering works, the upstream work behind it and the technical test records ↗

01 / The reason to build it

I want owning the headset to mean more choice.

I already had a PSVR 2 I liked: OLED screens, eye tracking, and a very obvious cable back to another machine. Steam Frame got me thinking about taking the computer with me. Then I looked at the powerful phone I already carried and wondered whether that could do the job.

That question became a larger one. Could the headset be the hardware I choose to keep, while I choose the computer and software that drive it? I want fewer parts of a VR session tied to one vendor's store, account or runtime, and more of the setup open to inspection and improvement.

PSVR 2 is where I started because I owned it and wanted to give it another life. Getting it working from a phone is the first proof. Building a useful, understandable route that other people can reproduce is the next part.

02 / How the pieces fit

The phone does the computing. The headset still needs a driver.

  1. Prepare the display. The Android app checks the connection and coordinates the external screen. Device-specific display work makes the PSVR 2 image mode possible. An output handoff, called a DRM lease, gives the VR software control of that display while Android keeps its own screen.
  2. Run the graphics and tracking. In the working Linux route, Droidspaces hosts the container, modified Mesa draws on the phone GPU, and Monado coordinates the headset views and movement. The current tracking route also uses a compatibility bridge to Sony's Windows driver.
  3. Give an application a way in. OpenXR is the shared interface between a compatible app and the runtime: ask for movement, render the views, submit the frames. Each headset still needs its own hardware support underneath it.

This is where "just use the phone" turns into driver work. After getting the Linux route running, I started bringing the headset connection directly into Android: USB, graphics, tracking and a normal app lifecycle. The build story below follows how we got here.

Local experiences Phone runs a compatible experience PSVR 2

PC streaming / planned PC wireless link phone PSVR 2

The headset connects to the phone through a cable and suitable adapter. The phone replaces the computer at the other end of the PC tether.
Read the build story: the display breakthrough, tracking, games and native Android

01 / The reason for the project

What if the PSVR 2 I already owned could go standalone?

Steam Frame got me thinking about it. I liked the idea of that freedom, but its screen and price weren't what I wanted. I already had a really nice PSVR 2. Its biggest downside for me was simple: it was wired to another machine.

And I wanted to keep what made it good. PSVR 2 has OLED screens and eye tracking. OLED gives you those deep blacks; eye tracking lets software know where you're looking. I wasn't excited about buying another headset and giving up hardware I already liked.

So I started researching how to get away from the PC tether. I considered different platforms, then looked at my own phone. My RedMagic 11 Pro already had a Snapdragon 8 Elite Gen 5. There was a lot of computing power sitting right there. Why not put the work into that, and figure out how to drive the headset?

That's where this started. Let the phone become the computer and the operating system, keep PSVR 2 as the headset, and carry the setup with you. Eventually, I want it to run compatible experiences itself or receive a wireless VR stream from a PC. The headset-to-phone cable and portable power still need to be part of the physical setup. Freeing it from a cable running back to a PC is the point.

02 / Before VR, there was a display problem

First, make the phone send the right picture.

Having a powerful phone didn't mean I could just plug the headset in and start a game. First, the phone had to send the right kind of picture: the right size, refresh rate and arrangement for two eyes. Getting an ordinary second screen working was only the start.

We built an Android app to see what the connection was doing and control it. It reads what the connected display supports, checks the mode actually being used, and handles starting and stopping the experimental display setup. My prototype uses a rooted phone, which gives the app system access that normal Android apps don't have.

The DSC breakthrough: fitting the picture through the link.

I needed the headset's 4000 × 2040 display mode at 120 Hz to work over the phone's DisplayPort connection. Display Stream Compression, or DSC, packs the picture data for that link so the headset can unpack it at the other end. Getting this working was one of the breakthroughs in my phone-hosted setup.

The retained working configuration shows how we kept it enabled. The Android app prepares and checks the connection. Alongside it, a separate, phone-specific system add-on called psvr2_dsc.ko is loaded by KernelSU, the software managing root access on this phone. It hooks into the Qualcomm display driver's DSC configuration for the 4000 × 2040 DisplayPort mode.

The saved profile uses 10 compressed bits per pixel and 120-line slices: settings for how the picture is packed into blocks on the display link. The startup script also checks that the connected screen identifies itself as Sony, makes the headset mode available when it is missing, and asks the phone to detect the connection again. That gave the rest of the VR setup an output it could build on.

Display-module, startup and kernel notes ↗

Once we could control the output, the next problem appeared: Android and the VR software both wanted to use it.

03 / Giving the display to Linux

Android had to let go without losing its own screen.

I started with Linux running in a container on the phone. Think of it as a Linux workspace using the same phone processor and graphics hardware while Android keeps running. That gave me access to graphics drivers and VR software that already existed, rather than having to write everything from scratch.

But the two systems couldn't fight over the headset's display. Some early attempts froze or crashed the phone. Turning off Android's entire display system wasn't much of a solution either; I still needed to use the phone's own screen.

We worked toward a proper handoff. The app asks Android to release the external screen, Linux gets permission to use that output, and stopping the session gives it back. We also had to clear old connections and processes so one failed attempt wouldn't break the next one.

Now the phone could keep its own screen while Linux drove the external one. That opened the door to VR, but we still needed something that understood PSVR 2's movement and its two eye views.

04 / Turning display output into VR

The picture and the tracking had to meet.

A picture on the headset isn't automatically VR. Turn your head and the view has to follow, at the right time, with a different image for each eye. The piece coordinating all that is called a VR runtime. We used an open source one called Monado.

The Linux graphics stack, including Mesa and its Turnip driver for the phone's graphics chip, handled drawing. Monado handled the headset side. OpenXR gave compatible applications a common way to ask for head movement and submit eye images.

The working route also needed a compatibility bridge to Sony's Windows driver. That driver was built for the x86-64 processors used in many PCs; the phone uses ARM64. Getting those pieces to communicate was another layer of the experiment. Running them on the phone didn't turn them into native Android software.

So the chain was finally coming together: the app prepared the display, Linux could use it, the graphics driver could draw, and the runtime could connect the game to the headset. Then came the part I actually wanted to see.

05 / A game in the headset

Skyrim became visible. Then it had to feel right.

Skyrim VR ran through the phone's container setup, and I confirmed that it was visible inside PSVR 2. Both Sense controllers were switched on. At that first checkpoint, controller tracking and audio still needed their own checks.

Later, performance work exposed a problem a frame counter could hide. A game could report more frames while head turns still produced ghosting: old-looking images trailing the movement. The software doing the final headset update was waiting behind the game's graphics work.

We changed how the phone prioritized those headset updates and passed frames along. In a later comparison, the delay dropped. I put the headset on again: the ghosting was gone, the controllers were fine, and motion felt much smoother. A bigger number on a counter wouldn't have told that story.

Eye tracking was the next connection I wanted to make useful. I confirmed eye tracking and foveation working in my tests, and the recording shows moving gaze overlays. The recovered DXVK source explains the game-side path: it reads Monado's gaze and updates shading-rate maps for selected Skyrim passes. SGSR2 processing in the compositor is a separate step; I tested that on the phone too and found that it improved the image.

The source behind that gaze-enabled build is now recovered and matched to the libraries from the game session. That means I can share the actual patches behind this part of the setup. Here's how we connected eye tracking to the game.

The motion comparison and measurement notes ↗

06 / Why one working game was not the finish

Alyx exposed another layer of compatibility.

Once Skyrim was working, I wanted to see how far the setup could go. Half-Life: Alyx took a different route: an ARM64 Linux version of the game with its own connection into the VR runtime. The headset work still helped, but Skyrim's settings weren't a recipe I could simply copy.

We worked through missing connections and graphics failures. Eventually I could recognize the menu, but the views were distorted and the hands were wrong. I called it unusable. It was progress toward finding the problem, but I couldn't call that playable VR.

That led to a SteamVR comparison. The normal download supplied PC-format binaries. A supplied Steam Frame recovery image contained the ARM64 runtime we were missing, so a separate SteamOS container was investigated. It started, but phone reboots made stability the priority. The next plan was to bring the needed runtime files back to the existing Linux setup instead of depending on the whole SteamOS environment.

I kept the working Linux setup as the baseline. The compatibility experiments were useful, but I also wanted a cleaner way to start a headset session from the phone. That led to the next route.

07 / The native Android route

Keep the phone. Remove the container dependency.

The next thing I'm building is a headset runtime that runs directly in Android. That's what I mean by native: the phone's own software talks to PSVR 2, without needing the Linux container and its Windows-driver compatibility path.

That means connecting the existing headset knowledge to Android's USB access, graphics output and application lifecycle. We can reuse open source work where it already solves a problem and study the remaining driver behavior where it does not. Copying a library from Linux is insufficient because Android expects a different set of system interfaces.

The first tests could connect to the real headset, read its motion sensors and start a VR session. But the code wasn't accepting the complete movement reports it needed to work out the view. A session could start and still have no picture to send to the eyes.

A later test with the headset sitting off my head moved forward. Most of those movement reports were accepted, and the software produced a test picture for both eyes. That was a useful step, but a screenshot by itself couldn't tell me whether tracking would work while I was wearing it.

So I put it on for the next test. That earlier run went black and every complete movement report was rejected. We had to keep working on how the code read the headset's reports.

At 03:40 UTC on 14 October, I confirmed the next native milestone: an image in the headset, with the view following my head. The updates were still arriving unevenly. Now I can see and move the view, the next job is to make that movement feel steady and repeat the result reliably.

This is a separate step from both the earlier DSC breakthrough and the working Skyrim container setup. The display link gets images to the headset; native tracking and presentation have to keep those images lined up with my movement.

03 / Reproduce the work

Start with the known setup. Change one piece at a time.

The setup guide starts with the rooted RedMagic reference device, Ubuntu ARM64 container and the components used in the working route.

The modified Mesa drivers and DXVK gaze-foveation source patches are available now, with versioned identities, notices and instructions.

  1. Match the device and kernel requirements, and read the limits of each published component.
  2. Resolve your container, account and installation paths using the configuration guide. Another person's home directory is a poor installation plan.
  3. Follow the component's versioned instructions and checksums. Verify display output, head movement, controller input and calibrated gaze separately, then check that stopping returns the display and settings cleanly.

Use the component guides in order, record what you change, and keep a way back to the working setup.

04 / Build beyond the first headset

Have different hardware? Bring it to the experiment.

I'd love developers with a Bigscreen Beyond, a Pimax or another headset to build the next phone-hosted port with us. Bring the hardware and a concrete first problem: display output, USB, optics, tracking or input. That's how this project started too.

Graphics drivers, USB, OpenXR apps, frame timing, eye tracking, or simply instructions another person can follow: there are useful ways to help. Start with one concrete problem, explain the setup, and show what changed. Small, repeatable results give the next developer something solid to build on.

Bring a reproducible issue or a headset-port proposal ↗
Explore the project and contribute a patch ↗

05 / What I'm building next

A working Linux setup. A native Android route in development.

Native Android: an image and head-follow are working. I'm improving frame pacing and stability, then bringing the rest of the headset and controller features into that route. Native progress and test records ↗

Reproduction and portability: the complete installer, portable power setup and broader device support are in development. PC streaming to PSVR 2, Alyx compatibility, more apps and new headset ports are next steps alongside that work. Component release status ↗

Local control: I want gaze, movement and room data handled locally, with clear control over network connections. Reviewing the software and network behavior is part of that work. Network and privacy notes ↗