Graphs

How Kestrel VR meets SteamVR, at the level of names and connections.

OpenXR and SteamVR

Where the application API sits, where the driver API sits, and how far apart they are. The boundary Kestrel is written against is at the bottom of this picture; OpenXR is at the top, in a different process, and nothing Kestrel does touches it.

Valve's code Kestrel Application API, public specification The application
Game process vrcompositor process vrserver process The game VRChat, Half-Life: Alyx OpenXR loader libopenxr_loader, picks a runtime SteamVR's runtime inside vrclient.so, loaded into this process OpenXR calls dispatch Compositor layers, reprojection, the menu and overlays Eye textures shared GPU memory, fences Present, PostPresent frames, poses,over IPC Device and property store poses, input, settings Driver host loads registered drivers Kestrel driver direct mode component, vsync grid, encode worker openvr_driver.h only Vulkan Video encoder radv, HEVC and H264 QUIC to the headset loads the driver seam, 50 calls Below this line is where Kestrel lives. Above it is where an OpenXR runtime lives. The two never meet in one process. A Kestrel runtime would replace everything above the line the loader entry, the session and frame loop, swapchains, the compositor, the device and input model. Monado is the working example. The encoder and the transport carry over. The driver seam does not.

What lives on each side

BoundaryWho defines itWho implements itDoes Kestrel touch it
OpenXR, the application APIKhronos, public specificationSteamVR inside vrclient.so, in the game's own process; Monado in its ownNot on the streamer. The headset client is an OpenXR application, which is the only OpenXR in the tree
OpenVR, the application APIValve, public headersThe same vrclient.so, the same processNo
The driver APIValve, openvr_driver.h, pinned at one commitvrserver loads the driver library into itselfYes. This is the whole of Kestrel's streamer seam
The compositor's shared textures, fences and per-image semaphoresNobody. Undocumentedvrcompositor publishes, the driver consumesYes, and this is the one fragile dependency, guarded by a fallback

Why the distinction matters

A SteamVR update cannot break Kestrel through OpenXR, because Kestrel is never in the process where OpenXR is implemented. It can break Kestrel through the fourth row, which is why that path disarms and falls back to plain Vulkan fences. And an OpenXR runtime of Kestrel's own is not an extension of the driver work; it is the whole upper half of this picture, with the encoder and the transport as the only pieces that cross the line.

The driver seam

Every call between SteamVR and the Kestrel driver: the openvr_driver.h interface on the left, the part of Kestrel that handles it on the right. Hover a box to follow its calls.

SteamVR interfacecallKestrel partSteamVR calls KestrelKestrel calls SteamVRHmdDriverFactory · driver load · SteamVR calls KestrelHmdDriverFactoryInit · IServerTrackedDeviceProvider_004 · SteamVR calls KestrelInitCleanup · IServerTrackedDeviceProvider_004 · SteamVR calls KestrelCleanupGetInterfaceVersions · IServerTrackedDeviceProvider_004 · SteamVR calls KestrelGetInterfaceVersionsRunFrame · IServerTrackedDeviceProvider_004 · SteamVR calls KestrelRunFrameShouldBlockStandbyMode · IServerTrackedDeviceProvider_004 · SteamVR calls KestrelShouldBlockStandbyModeEnterStandby (provider) · IServerTrackedDeviceProvider_004 · SteamVR calls KestrelEnterStandby (provider)LeaveStandby · IServerTrackedDeviceProvider_004 · SteamVR calls KestrelLeaveStandbyActivate · ITrackedDeviceServerDriver_005 · SteamVR calls KestrelActivateDeactivate · ITrackedDeviceServerDriver_005 · SteamVR calls KestrelDeactivateEnterStandby (device) · ITrackedDeviceServerDriver_005 · SteamVR calls KestrelEnterStandby (device)GetComponent · ITrackedDeviceServerDriver_005 · SteamVR calls KestrelGetComponentDebugRequest · ITrackedDeviceServerDriver_005 · SteamVR calls KestrelDebugRequestGetPose · ITrackedDeviceServerDriver_005 · SteamVR calls KestrelGetPoseGetWindowBounds · IVRDisplayComponent_003 · SteamVR calls KestrelGetWindowBoundsIsDisplayOnDesktop · IVRDisplayComponent_003 · SteamVR calls KestrelIsDisplayOnDesktopIsDisplayRealDisplay · IVRDisplayComponent_003 · SteamVR calls KestrelIsDisplayRealDisplayGetRecommendedRenderTargetSize · IVRDisplayComponent_003 · SteamVR calls KestrelGetRecommendedRenderTargetSizeGetEyeOutputViewport · IVRDisplayComponent_003 · SteamVR calls KestrelGetEyeOutputViewportGetProjectionRaw · IVRDisplayComponent_003 · SteamVR calls KestrelGetProjectionRawComputeDistortion · IVRDisplayComponent_003 · SteamVR calls KestrelComputeDistortionComputeInverseDistortion · IVRDisplayComponent_003 · SteamVR calls KestrelComputeInverseDistortionCreateSwapTextureSet · IVRDriverDirectModeComponent_009 · SteamVR calls KestrelCreateSwapTextureSetDestroySwapTextureSet · IVRDriverDirectModeComponent_009 · SteamVR calls KestrelDestroySwapTextureSetDestroyAllSwapTextureSets · IVRDriverDirectModeComponent_009 · SteamVR calls KestrelDestroyAllSwapTextureSetsGetNextSwapTextureSetIndex · IVRDriverDirectModeComponent_009 · SteamVR calls KestrelGetNextSwapTextureSetIndexSubmitLayer · IVRDriverDirectModeComponent_009 · SteamVR calls KestrelSubmitLayerPresent · IVRDriverDirectModeComponent_009 · SteamVR calls KestrelPresentPostPresent · IVRDriverDirectModeComponent_009 · SteamVR calls KestrelPostPresentGetFrameTiming · IVRDriverDirectModeComponent_009 · SteamVR calls KestrelGetFrameTimingVR_INIT_SERVER_DRIVER_CONTEXT · driver context · Kestrel calls SteamVRVR_INIT_SERVER_DRIVER_CONTEXTVR_CLEANUP_SERVER_DRIVER_CONTEXT · driver context · Kestrel calls SteamVRVR_CLEANUP_SERVER_DRIVER_CONTEXTTrackedDeviceAdded · IVRServerDriverHost_006 · Kestrel calls SteamVRTrackedDeviceAddedTrackedDevicePoseUpdated · IVRServerDriverHost_006 · Kestrel calls SteamVRTrackedDevicePoseUpdatedVsyncEvent · IVRServerDriverHost_006 · Kestrel calls SteamVRVsyncEventVsyncEvent (SendVSync) · IVRServerDriverHost_006 · Kestrel calls SteamVRVsyncEvent (SendVSync)VendorSpecificEvent · IVRServerDriverHost_006 · Kestrel calls SteamVRVendorSpecificEventPollNextEvent · IVRServerDriverHost_006 · Kestrel calls SteamVRPollNextEventIsExiting · IVRServerDriverHost_006 · Kestrel calls SteamVRIsExitingSetDisplayEyeToHead · IVRServerDriverHost_006 · Kestrel calls SteamVRSetDisplayEyeToHeadSetDisplayProjectionRaw · IVRServerDriverHost_006 · Kestrel calls SteamVRSetDisplayProjectionRawSetBoolProperty · IVRProperties_001 · Kestrel calls SteamVRSetBoolPropertySetFloatProperty · IVRProperties_001 · Kestrel calls SteamVRSetFloatPropertyTrackedDeviceToPropertyContainer · IVRProperties_001 · Kestrel calls SteamVRTrackedDeviceToPropertyContainerGetFloat · IVRSettings_003 · Kestrel calls SteamVRGetFloatSetFloat · IVRSettings_003 · Kestrel calls SteamVRSetFloatCreateBooleanComponent · IVRDriverInput_004 · Kestrel calls SteamVRCreateBooleanComponentCreateScalarComponent · IVRDriverInput_004 · Kestrel calls SteamVRCreateScalarComponentUpdateBooleanComponent · IVRDriverInput_004 · Kestrel calls SteamVRUpdateBooleanComponentUpdateScalarComponent · IVRDriverInput_004 · Kestrel calls SteamVRUpdateScalarComponentdriver loadIServerTrackedDeviceProvider_004ITrackedDeviceServerDriver_005IVRDisplayComponent_003IVRDriverDirectModeComponent_009driver contextIVRServerDriverHost_006IVRProperties_001IVRSettings_003IVRDriverInput_004Rust coreDriver entryTracked deviceTracker proxyHeadsetLinux direct displayPlay areaControllers

Which thread makes each call

The same seam, sorted by the SteamVR thread each call runs on. Frame delivery has a thread of its own, so a driver can pace frames without holding up the rest of SteamVR.

threadcallinterfaceSteamVR calls the driverThe driver calls SteamVRHmdDriverFactory · driver load · runs on: StartupHmdDriverFactoryInit · IServerTrackedDeviceProvider_004 · runs on: StartupInitCleanup · IServerTrackedDeviceProvider_004 · runs on: Server main loopCleanupGetInterfaceVersions · IServerTrackedDeviceProvider_004 · runs on: StartupGetInterfaceVersionsRunFrame · IServerTrackedDeviceProvider_004 · runs on: Server main loopRunFrameShouldBlockStandbyMode · IServerTrackedDeviceProvider_004 · runs on: No caller foundShouldBlockStandbyModeEnterStandby (provider) · IServerTrackedDeviceProvider_004 · runs on: Server main loopEnterStandby (provider)LeaveStandby · IServerTrackedDeviceProvider_004 · runs on: Server main loopLeaveStandbyActivate · ITrackedDeviceServerDriver_005 · runs on: Device activationActivateDeactivate · ITrackedDeviceServerDriver_005 · runs on: Server main loopDeactivateEnterStandby (device) · ITrackedDeviceServerDriver_005 · runs on: Server main loopEnterStandby (device)GetComponent · ITrackedDeviceServerDriver_005 · runs on: Device activationGetComponentDebugRequest · ITrackedDeviceServerDriver_005 · runs on: Compositor connectionDebugRequestGetPose · ITrackedDeviceServerDriver_005 · runs on: No caller foundGetPoseGetWindowBounds · IVRDisplayComponent_003 · runs on: Device activationGetWindowBoundsIsDisplayOnDesktop · IVRDisplayComponent_003 · runs on: Device activationIsDisplayOnDesktopIsDisplayRealDisplay · IVRDisplayComponent_003 · runs on: Device activationIsDisplayRealDisplayGetRecommendedRenderTargetSize · IVRDisplayComponent_003 · runs on: Device activationGetRecommendedRenderTargetSizeGetEyeOutputViewport · IVRDisplayComponent_003 · runs on: Device activationGetEyeOutputViewportGetProjectionRaw · IVRDisplayComponent_003 · runs on: Device activationGetProjectionRawComputeDistortion · IVRDisplayComponent_003 · runs on: Compositor connectionComputeDistortionComputeInverseDistortion · IVRDisplayComponent_003 · runs on: Compositor connectionComputeInverseDistortionCreateSwapTextureSet · IVRDriverDirectModeComponent_009 · runs on: Compositor connectionCreateSwapTextureSetDestroySwapTextureSet · IVRDriverDirectModeComponent_009 · runs on: Compositor connectionDestroySwapTextureSetDestroyAllSwapTextureSets · IVRDriverDirectModeComponent_009 · runs on: Compositor connectionDestroyAllSwapTextureSetsGetNextSwapTextureSetIndex · IVRDriverDirectModeComponent_009 · runs on: Frame submit threadGetNextSwapTextureSetIndexSubmitLayer · IVRDriverDirectModeComponent_009 · runs on: Frame submit threadSubmitLayerPresent · IVRDriverDirectModeComponent_009 · runs on: Frame submit threadPresentPostPresent · IVRDriverDirectModeComponent_009 · runs on: Frame submit threadPostPresentGetFrameTiming · IVRDriverDirectModeComponent_009 · runs on: Frame submit threadGetFrameTimingGetGenericInterface · IVRDriverContext · runs on: StartupGetGenericInterfaceTrackedDeviceAdded · IVRServerDriverHost_006 · runs on: Device activationTrackedDeviceAddedTrackedDevicePoseUpdated · IVRServerDriverHost_006 · runs on: Driver's own threadsTrackedDevicePoseUpdatedVsyncEvent · IVRServerDriverHost_006 · runs on: Driver's own threadsVsyncEventVendorSpecificEvent · IVRServerDriverHost_006 · runs on: Server main loopVendorSpecificEventIsExiting · IVRServerDriverHost_006 · runs on: Server main loopIsExitingPollNextEvent · IVRServerDriverHost_006 · runs on: Server main loopPollNextEventGetRawTrackedDevicePoses · IVRServerDriverHost_006 · runs on: Driver's own threadsGetRawTrackedDevicePosesRequestRestart · IVRServerDriverHost_006 · runs on: Driver's own threadsRequestRestartGetFrameTimings · IVRServerDriverHost_006 · runs on: No caller foundGetFrameTimingsSetDisplayEyeToHead · IVRServerDriverHost_006 · runs on: Driver's own threadsSetDisplayEyeToHeadSetDisplayProjectionRaw · IVRServerDriverHost_006 · runs on: Driver's own threadsSetDisplayProjectionRawSetRecommendedRenderTargetSize · IVRServerDriverHost_006 · runs on: Driver's own threadsSetRecommendedRenderTargetSizeTrackedDeviceToPropertyContainer · IVRProperties_001 · runs on: Driver's own threadsTrackedDeviceToPropertyContainerReadPropertyBatch · IVRProperties_001 · runs on: Driver's own threadsReadPropertyBatchWritePropertyBatch · IVRProperties_001 · runs on: Driver's own threadsWritePropertyBatchGetFloat · IVRSettings_003 · runs on: Driver's own threadsGetFloatSetFloat · IVRSettings_003 · runs on: Driver's own threadsSetFloatCreateBooleanComponent · IVRDriverInput_004 · runs on: Device activationCreateBooleanComponentCreateScalarComponent · IVRDriverInput_004 · runs on: Device activationCreateScalarComponentUpdateBooleanComponent · IVRDriverInput_004 · runs on: Driver's own threadsUpdateBooleanComponentUpdateScalarComponent · IVRDriverInput_004 · runs on: Driver's own threadsUpdateScalarComponentStartupServer main loopNo caller foundDevice activationCompositor connectionFrame submit threadDriver's own threadsdriver loadIServerTrackedDeviceProvider_004ITrackedDeviceServerDriver_005IVRDisplayComponent_003IVRDriverDirectModeComponent_009IVRDriverContextIVRServerDriverHost_006IVRProperties_001IVRSettings_003IVRDriverInput_004

Inside the streamer: Rust and C++

The streamer is two languages in one library. These are the names that cross between them, as counted on 2026-09-08, with the part of each side that uses them.

C++ partnameRust partRust implements, C++ callsC++ implements, Rust callsRust writes, C++ readsLogErrorLogErrorLogWarnLogWarnLogInfoLogInfoLogDebugLogDebugLogEncoderLogEncoderLogPeriodicallyLogPeriodicallyDriverReadyIdleDriverReadyIdleSetVideoConfigNalsSetVideoConfigNalsVideoSendVideoSendHapticsSendHapticsSendShutdownRuntimeShutdownRuntimeSetEncoderMaxFrameSetEncoderMaxFrameSetIntraRefreshCycleSetIntraRefreshCycleSetEncoderFailedIdleSetEncoderFailedIdlePathStringToHashPathStringToHashReportPresentReportPresentReportComposedReportComposedGetDynamicEncoderParamsGetDynamicEncoderParamsGetClientDisplayPeriodNsGetClientDisplayPeriodNsGetSerialNumberGetSerialNumberSetOpenvrPropsSetOpenvrPropsRegisterButtonsRegisterButtonsWaitForVSyncWaitForVSyncInitializeRuntimeInitializeRuntimeCppInitCppInitCppOpenvrEntryPointCppOpenvrEntryPointInitializeStreamingInitializeStreamingDeinitializeStreamingDeinitializeStreamingSendVSyncSendVSyncRequestIDRRequestIDRSetTrackingSetTrackingRequestDriverResyncRequestDriverResyncShutdownSteamvrShutdownSteamvrSetOpenvrPropertySetOpenvrPropertySetOpenvrPropByDeviceIDSetOpenvrPropByDeviceIDRegisterButtonRegisterButtonSetLocalViewParamsSetLocalViewParamsSetBatterySetBatterySetButtonSetButtonSetProximityStateSetProximityStateInitOpenvrClientInitOpenvrClientShutdownOpenvrClientShutdownOpenvrClientSetChaperoneAreaSetChaperoneAreaCaptureFrameCaptureFrameFRAME_RENDER_VS_CSO_PTR/LENFRAME_RENDER_VS_CSO_PTR/LENFRAME_RENDER_PS_CSO_PTR/LENFRAME_RENDER_PS_CSO_PTR/LENQUAD_SHADER_CSO_PTR/LENQUAD_SHADER_CSO_PTR/LENCOMPRESS_AXIS_ALIGNED_CSO_PTR/LENCOMPRESS_AXIS_ALIGNED_CSO_PTR/LENCOLOR_CORRECTION_CSO_PTR/LENCOLOR_CORRECTION_CSO_PTR/LENRGBTOYUV420_CSO_PTR/LENRGBTOYUV420_CSO_PTR/LENQUAD_SHADER_COMP_SPV_PTR/LENQUAD_SHADER_COMP_SPV_PTR/LENFFR_SHADER_COMP_SPV_PTR/LENFFR_SHADER_COMP_SPV_PTR/LENRGBTOYUV420_SHADER_COMP_SPV_PTR/LENRGBTOYUV420_SHADER_COMP_SPV_PTR/LENQUAD_NV12_SHADER_COMP_SPV_PTR/LENQUAD_NV12_SHADER_COMP_SPV_PTR/LENFFR_NV12_SHADER_COMP_SPV_PTR/LENFFR_NV12_SHADER_COMP_SPV_PTR/LENg_sessionPathg_sessionPathg_driverRootDirg_driverRootDirLoggingHeadsetBitstream parsingEncodersDriver entryLinux direct displayInput pathsControllersWindows displayTracked devicePlay areaWindows rendererRust corePropertiesGraphics