Initial commit: OpenXR simulator runtime, compositor, configurator, docs
This commit is contained in:
@@ -0,0 +1,138 @@
|
||||
# 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.
|
||||
Reference in New Issue
Block a user