Files
Toastconcern--FrameStudio/src/vr_compat.h
T
ToastconcernandClaude Opus 5.5 4d058a9e7a Frame Home runs on SteamVR on a Windows PC (tested on SteamVR's virtual headset)
- vr_compat.h: GetProjectionMatrix / GetEyeToHeadTransform called with Microsoft's member ABI on MinGW builds
  (SteamVR is built with MSVC; the hidden return pointer goes after this): the first frame crashed there.
- When SteamVR asks it to quit (a game, room setup), Frame Home leaves at once instead of waiting for a load (SteamVR
  killed it after 5 s).
- The panel keeps SteamVR's home app running on a PC: SteamVR doesn't start it again after it stopped on its own.
- Logs on Windows: the environment and the panel always write logs/frame-home.log and logs/frame-switcher.log (SteamVR
  gives them a hidden console); the panel logs to a file on the headset too now. A crash writes the exception and the
  stack's return addresses there.
- Tested on SteamVR's null driver: Frame Home started by SteamVR as its home app, the panel with it, SteamVR's
  background made from the home, a crash recovered in seconds, 0.79 ms GPU at 1976x1976 per eye with 4x MSAA.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 15:45:28 -05:00

31 lines
1.7 KiB
C++

#pragma once
// The OpenVR calls that return a struct, made the way SteamVR expects when Frame Home is built for Windows with
// MinGW (GCC or Clang). SteamVR is built with Microsoft's compiler, whose C++ ABI passes a member function's hidden
// return pointer after `this`; MinGW's (the Itanium ABI) passes it before. Called through the C++ interface, these
// jumped to garbage (a crash in the first frame). Here they go through the interface's vtable with Microsoft's order.
// Everywhere else (Linux, the Steam Frame, an MSVC build): the plain calls.
#include <openvr.h>
namespace vrcompat {
#if defined(_WIN32) && !defined(_MSC_VER)
// slot: the method's place in the interface's vtable, as openvr.h declares it for the version asked for (IVRSystem_026).
template <typename R, typename... A> R msvcMember(void* object, int slot, A... args) {
R result;
using Fn = R* (*)(void*, R*, A...); // x64: this, the return pointer, then the arguments, as Microsoft's members take them
(*reinterpret_cast<Fn* const*>(object))[slot](object, &result, args...);
return result;
}
inline vr::HmdMatrix44_t projectionMatrix(vr::EVREye eye, float nearZ, float farZ) {
return msvcMember<vr::HmdMatrix44_t>(vr::VRSystem(), 1, eye, nearZ, farZ); // IVRSystem::GetProjectionMatrix
}
inline vr::HmdMatrix34_t eyeToHead(vr::EVREye eye) {
return msvcMember<vr::HmdMatrix34_t>(vr::VRSystem(), 5, eye); // IVRSystem::GetEyeToHeadTransform
}
#else
inline vr::HmdMatrix44_t projectionMatrix(vr::EVREye eye, float nearZ, float farZ) { return vr::VRSystem()->GetProjectionMatrix(eye, nearZ, farZ); }
inline vr::HmdMatrix34_t eyeToHead(vr::EVREye eye) { return vr::VRSystem()->GetEyeToHeadTransform(eye); }
#endif
} // namespace vrcompat