mirror of
https://github.com/spoopyghosty0/frameport.git
synced 2026-10-24 07:03:03 +02:00
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)
129 lines
6.2 KiB
C++
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();
|
|
}
|
|
}
|