Files
spoopyghosty0--frameport/native/vrapi-bridge/runtime.h
T
spoopyghosty0 927dae5ff8 Merge main into aarch32testing; maintainer fixes for the 32-bit bridge
Merged main (agent 90, ANALYSIS_VERSION 17/18 after main's 15/16) and rebuilt the bridge, foveation and a32
libraries from source (deterministic, no local paths).

Fixes on top:
- a32 loader: apply DT_ANDROID_RELR relocations (Journey of the Gods crashed at start)
- VrApi bridge: vrapi_Get/SetTrackingTransform (floor-level tracking; Shadow Point's player was below the floor)
- a32: never submit an untracked default head pose (hold up to 100 ms, repair or drop); pose consistency per display
  time; submit/tracking counters
- VrApi bridge: wait again when xrWaitFrame gives a display time in the refresh just submitted (the runtime rejected
  the second frame with AlreadySubmitted; Journey of the Gods ran at ~120 fps on a 90 Hz display and the scene slid
  and snapped back); pacing counters
- VrApi bridge: GLES re-entry polls events before reusing the kept session (a lost or exiting session is replaced, a
  stopped one waits for READY)
- VrApi bridge: STAGE is the runtime's STAGE space (play-area centre and orientation, unchanged by recentering);
  LOCAL_FLOOR keeps the head-based origin
- signer certificate: pick the certificate the SignerInfo names, not the first in the PKCS#7 block
- flat 32-bit apps keep the bridge (it isn't a VR-only fix)
- Frida gadget loads are stripped before translation
- launcher restart test fixed; native/.cache is no longer committed
- catalog: Shadow Point and Journey of the Gods work (headset, 2026-10-14; min_app 1.1.2)
2026-10-14 20:10:10 -04:00

129 lines
6.2 KiB
C++

// SPDX-License-Identifier: GPL-3.0-only
#pragma once
#include "abi.h"
#include <EGL/egl.h>
#include <openxr/openxr.h>
#include <openxr/openxr_platform.h>
#include <android/log.h>
#include <array>
#include <mutex>
#include <vector>
#define OVP_LOG(...) __android_log_print(ANDROID_LOG_INFO, "OVRPortVrApi", __VA_ARGS__)
#define OVP_ERROR(...) __android_log_print(ANDROID_LOG_ERROR, "OVRPortVrApi", __VA_ARGS__)
#define OVP_XR_FUNCTIONS(X) \
X(xrEnumerateInstanceExtensionProperties) X(xrCreateInstance) X(xrDestroyInstance) \
X(xrGetSystem) X(xrGetSystemProperties) X(xrEnumerateViewConfigurationViews) \
X(xrGetVulkanGraphicsRequirementsKHR) X(xrGetVulkanInstanceExtensionsKHR) \
X(xrGetVulkanDeviceExtensionsKHR) X(xrGetVulkanGraphicsDeviceKHR) \
X(xrCreateSession) X(xrDestroySession) X(xrBeginSession) X(xrEndSession) X(xrRequestExitSession) \
X(xrPollEvent) X(xrCreateReferenceSpace) X(xrDestroySpace) X(xrLocateSpace) \
X(xrWaitFrame) X(xrBeginFrame) X(xrEndFrame) X(xrLocateViews) \
X(xrEnumerateSwapchainFormats) X(xrCreateSwapchain) X(xrDestroySwapchain) \
X(xrEnumerateSwapchainImages) X(xrAcquireSwapchainImage) X(xrWaitSwapchainImage) X(xrReleaseSwapchainImage) \
X(xrStringToPath) X(xrCreateActionSet) X(xrDestroyActionSet) X(xrCreateAction) \
X(xrSuggestInteractionProfileBindings) X(xrAttachSessionActionSets) X(xrCreateActionSpace) \
X(xrSyncActions) X(xrGetActionStateBoolean) X(xrGetActionStateFloat) X(xrGetActionStateVector2f) \
X(xrGetActionStatePose) X(xrApplyHapticFeedback) X(xrStopHapticFeedback)
namespace ovp {
struct XrApi {
PFN_xrGetInstanceProcAddr xrGetInstanceProcAddr = nullptr;
#define OVP_FIELD(name) PFN_##name name = nullptr;
OVP_XR_FUNCTIONS(OVP_FIELD)
#undef OVP_FIELD
};
typedef XrResult (XRAPI_PTR *PFN_axrbGetSystemDisplayRefreshRate)(
XrInstance instance, XrSystemId systemId, float* displayRefreshRate);
enum class DisplayRefreshRateSource : uint8_t {
Unqueried,
Unavailable,
AxrbSystem,
FbSession,
FramePeriod,
};
struct Runtime {
std::recursive_mutex mutex;
XrApi xr;
void* loader = nullptr;
JavaVM* vm = nullptr;
jobject activity = nullptr;
XrInstance instance = XR_NULL_HANDLE;
XrSystemId system = XR_NULL_SYSTEM_ID;
XrSession session = XR_NULL_HANDLE;
XrSpace localSpace = XR_NULL_HANDLE, stageSpace = XR_NULL_HANDLE;
XrSpace viewSpace = XR_NULL_HANDLE, appSpace = XR_NULL_HANDLE;
XrSessionState sessionState = XR_SESSION_STATE_UNKNOWN;
ovrMobile mobile{};
bool inVr = false, running = false, frameBegun = false;
int64_t frameIndex = -1;
XrFrameState frame{XR_TYPE_FRAME_STATE};
XrTime lastEndedDisplayTime = 0; // display time of the last xrEndFrame
uint32_t repeatedWaits = 0; // xrWaitFrame answers that repeated an already submitted display time
std::array<XrViewConfigurationView, 2> viewConfig{};
std::array<XrViewConfigurationViewFovEPIC, 2> viewConfigFov{};
std::array<float, 2> recommendedFovDegrees{};
std::array<float, 2> locatedFovDegrees{};
// FramePort: the last live FOV of an earlier session (framebridge-vrapi-fov.txt in the app's external files).
// Games that read VrApi's FOV properties right after vrapi_Initialize (before any session, e.g. BlazeRush) and
// cache them get this instead of a 90 degree guess when the runtime has no XR_EPIC_view_configuration_fov.
std::array<float, 2> savedFovDegrees{};
bool savedFovValid = false;
int loggedFovSource = -1;
std::array<XrView, 2> views{{{XR_TYPE_VIEW}, {XR_TYPE_VIEW}}};
ovrSystemCreateInfoVulkan vk{};
// GLES apps (no vrapi_CreateSystemVulkan): EGL objects from ovrModeParms.
bool gles = false, glesExtension = false, vulkanExtension = false;
EGLDisplay eglDisplay = EGL_NO_DISPLAY;
EGLContext eglContext = EGL_NO_CONTEXT;
EGLConfig eglConfig = nullptr;
PFN_xrGetOpenGLESGraphicsRequirementsKHR getGlesRequirements = nullptr;
VkQueue queue = VK_NULL_HANDLE;
uint32_t queueFamily = 0;
double timeOffset = 0;
bool timeMapped = false, recommendedFovValid = false, locatedFovValid = false;
bool viewConfigurationFovExtension = false;
bool colorSpaceExtension = false, performanceExtension = false, threadExtension = false;
bool systemDisplayRefreshRateExtension = false, displayRefreshRateExtension = false;
PFN_xrSetColorSpaceFB setColorSpace = nullptr;
PFN_xrPerfSettingsSetPerformanceLevelEXT setPerformance = nullptr;
PFN_xrSetAndroidApplicationThreadKHR setThread = nullptr;
PFN_axrbGetSystemDisplayRefreshRate getSystemDisplayRefreshRate = nullptr;
PFN_xrGetDisplayRefreshRateFB getDisplayRefreshRate = nullptr;
PFN_xrRequestDisplayRefreshRateFB requestDisplayRefreshRate = nullptr;
DisplayRefreshRateSource loggedDisplayRefreshRateSource = DisplayRefreshRateSource::Unqueried;
float loggedDisplayRefreshRate = 0;
ovrPosef trackingTransform{{0, 0, 0, 1}, {0, 0, 0}};
ovrPosef centerEyeTransform{{0, 0, 0, 1}, {0, 0, 0}};
bool initialRecenterPending = false;
// A recenter after the first: the runtime's own (the Frame's long press of the menu button moves LOCAL to the
// head) or vrapi_RecenterPose. Applied in the first frame displayed at or after recenterTime.
bool recenterPending = false;
XrTime recenterTime = 0;
uint32_t recenterCount = 0;
int32_t trackingSpace = 0; // VrApi's: 0 LOCAL (eye level), 1 LOCAL_FLOOR, 3 STAGE
XrPosef eyeOrigin{{0, 0, 0, 1}, {0, 0, 0}}; // the app space's eye-level origin in LOCAL
};
Runtime& runtime();
bool xrOk(XrResult result, const char* operation);
double secondsNow();
XrTime toXrTime(double seconds);
double toSeconds(XrTime time);
XrPosef toXrPose(const ovrPosef& pose);
ovrPosef fromXrPose(const XrPosef& pose);
bool validMobile(const ovrMobile* mobile);
bool ensureGraphicsQueue(VkQueue preferredQueue = VK_NULL_HANDLE);
bool ensureSession(VkQueue preferredQueue = VK_NULL_HANDLE);
bool pollEvents();
bool beginFrame(int64_t index);
XrResult endFrame(const XrCompositionLayerBaseHeader* const* layers, uint32_t count);
bool locate(XrSpace space, XrTime time, ovrRigidBodyPosef& pose, uint32_t& status);
namespace input {
bool initialize();
void shutdown();
void sync();
}
namespace graphics {
void shutdownSession();
void shutdown();
}
}