139 lines
5.8 KiB
Markdown
139 lines
5.8 KiB
Markdown
# OpenXR Simulator
|
|
|
|
A Windows OpenXR **runtime** that pretends to be a headset. OpenXR applications
|
|
run unmodified and render into runtime-owned Vulkan swapchains; the runtime
|
|
displays those swapchain images in a desktop window (GLFW + Dear ImGui docking).
|
|
Primary target is the `gl3-vulkan` / "Get Beaned" engine.
|
|
|
|
See [`docs/DESIGN.md`](docs/DESIGN.md) for the full architecture and roadmap.
|
|
|
|
## Status
|
|
|
|
Implemented:
|
|
|
|
- Loader negotiation, dispatch table, instance/system/view-config metadata.
|
|
- Vulkan instance/device creation on behalf of the app (augmenting the required
|
|
surface/swapchain extensions).
|
|
- Session lifecycle + session-state events, multiview swapchains, frame loop
|
|
(`xrWaitFrame`/`xrBeginFrame`/`xrEndFrame`), `xrLocateViews` with mouse/WASD
|
|
tracking.
|
|
- Action API with **emulated controllers**: advertises an Oculus Touch profile
|
|
and maps keyboard/mouse to trigger, grip, thumbstick, buttons, menu, and
|
|
aim/grip poses. The mapping is shown in the ImGui `Controls` window.
|
|
- GLFW + Dear ImGui (docking) compositor showing each eye in a dockable window.
|
|
|
|
Set `XRSIM_NO_WINDOW=1` to run headless (no window, no eye display).
|
|
Remaining: Phase 5 polish (session-state edges, haptics, settings UI).
|
|
|
|
## Layout
|
|
|
|
```text
|
|
CMakeLists.txt Build the runtime DLL + configurator.
|
|
CMakePresets.json Configure/build presets.
|
|
config/openxr_simulator_config.json Reference config.
|
|
docs/DESIGN.md Architecture and roadmap.
|
|
manifest/openxr_simulator.json.in Runtime manifest template.
|
|
src/Common.h Logging.
|
|
src/Config.h / Config.cpp Shared config (JSON), used by DLL + configurator.
|
|
src/Runtime.h / Runtime.cpp Runtime state + xr* entry points.
|
|
src/Negotiation.cpp Export + xrGetInstanceProcAddr dispatch.
|
|
src/Compositor.cpp GLFW + ImGui display window.
|
|
tools/configurator/ GLFW + ImGui config editor.
|
|
third_party/openxr/ Vendored OpenXR 1.0.28 headers + negotiation.
|
|
```
|
|
|
|
## Requirements
|
|
|
|
- CMake 3.24+, Ninja
|
|
- MinGW-w64 GCC or MSVC
|
|
- [Vulkan SDK](https://vulkan.lunarg.com/sdk/home) (`VULKAN_SDK` on `PATH`)
|
|
|
|
## Build
|
|
|
|
```powershell
|
|
cmake --preset windows-mingw-release
|
|
cmake --build --preset windows-mingw-release
|
|
```
|
|
|
|
Outputs `out/build/windows-mingw-release/openxr_simulator.dll` and
|
|
`openxr_simulator.json` side by side.
|
|
|
|
## Configuration
|
|
|
|
The runtime reads a JSON config from `XRSIM_CONFIG`, or else
|
|
`openxr_simulator_config.json` next to `openxr_simulator.dll`. A reference file
|
|
lives in [`config/openxr_simulator_config.json`](config/openxr_simulator_config.json).
|
|
|
|
Editable via the configurator (built alongside the runtime):
|
|
|
|
```powershell
|
|
.\out\build\windows-msvc-release\openxr_simulator_configurator.exe
|
|
```
|
|
|
|
The configurator includes headset presets — Meta Quest 2 / 3 / 3S / Pro, Valve
|
|
Index, Valve Steam Frame, HTC Vive / Vive Pro 2 / Focus 3, HP Reverb G2, Pico 4 /
|
|
4 Ultra / Neo 3, Varjo Aero / XR-4, Pimax Crystal, Bigscreen Beyond 2,
|
|
PlayStation VR2, Samsung Galaxy XR, and a generic 1280 headset — or you can set
|
|
values manually.
|
|
|
|
Settings:
|
|
|
|
| Key | Meaning |
|
|
| --- | --- |
|
|
| `systemName`, `vendorId` | Reported by `xrGetSystemProperties`. |
|
|
| `eyeWidth`, `eyeHeight` | Per-eye recommended render resolution. |
|
|
| `refreshRateHz` | Virtual headset strobe rate used by `xrWaitFrame`. |
|
|
| `fovLeftDeg`/`Right`/`Up`/`Down` | Per-eye half-angle field of view. |
|
|
| `ipdMeters` | Default interpupillary distance. |
|
|
| `swapchainImageCount` | Number of images per XR swapchain. |
|
|
| `windowTitle`, `windowWidth`, `windowHeight` | Simulator window. |
|
|
| `presentMode` | `mailbox`/`immediate` (uncapped) or `fifo` (vsync). |
|
|
| `displayMode` | `sideBySide`, `singleEyeLeft`, `singleEyeRight`. |
|
|
|
|
### Frame rate / 60 Hz cap
|
|
|
|
`presentMode: "fifo"` presents with vsync, which caps the whole app to the
|
|
monitor refresh (e.g. 60 Hz) because presentation happens on the app's thread.
|
|
The default `"mailbox"` (or `"immediate"`) presents without vsync. `refreshRateHz`
|
|
controls the simulated headset strobe independently of presentation.
|
|
|
|
## Testing against the engine
|
|
|
|
The engine already honours `XR_RUNTIME_JSON`, so no registry changes are needed.
|
|
From the engine build directory (`gl3-vulkan/code/out/build/<preset>`):
|
|
|
|
```powershell
|
|
$env:XR_RUNTIME_JSON = "C:\Users\<you>\openxr-simulator\out\build\windows-mingw-release\openxr_simulator.json"
|
|
.\Game.exe
|
|
```
|
|
|
|
Expected in this phase: the loader loads the simulator, `xrCreateInstance` and
|
|
`xrGetSystem` succeed, and the engine creates its Vulkan instance/device through
|
|
the runtime. It will then fail on the first unimplemented call (swapchain), which
|
|
is the Phase 2 milestone.
|
|
|
|
## Installing system-wide (optional, later)
|
|
|
|
Point the active runtime at the manifest:
|
|
|
|
```powershell
|
|
New-Item -Path "HKLM:\SOFTWARE\Khronos\OpenXR\1" -Force | Out-Null
|
|
Set-ItemProperty -Path "HKLM:\SOFTWARE\Khronos\OpenXR\1" -Name "ActiveRuntime" `
|
|
-Value "C:\Users\<you>\openxr-simulator\out\build\windows-mingw-release\openxr_simulator.json"
|
|
```
|
|
|
|
Remember to restore the previous value (`SteamVR`/`Oculus`) when done.
|
|
|
|
## Troubleshooting
|
|
|
|
- **Loader logs `Failed to open dynamic library ... error 126`** (`ERROR_MOD_NOT_FOUND`)
|
|
when launching from Explorer: the runtime DLL's dependencies are missing from
|
|
`PATH`. The MinGW build statically links the GCC runtime
|
|
(`-static-libgcc -static-libstdc++ -static`) so this should not happen; if you
|
|
build with another toolchain, either link the CRT statically or ship the
|
|
required runtime DLLs next to `openxr_simulator.dll`. Inspect dependencies with
|
|
`objdump -p openxr_simulator.dll | findstr "DLL Name"`.
|
|
- **`library_path` must contain a path separator.** A bare `openxr_simulator.dll`
|
|
is resolved from the global library search path, not the manifest directory;
|
|
the generated manifest uses `./openxr_simulator.dll` for this reason.
|