174 lines
9.1 KiB
C
174 lines
9.1 KiB
C
// Minimal OpenXR bring-up for questshock's immersive Quest build. Keeps the
|
|
// game's own rendering untouched (see android/engine-patches/
|
|
// 11-android-openxr-present.patch) - this module only owns the OpenXR
|
|
// instance/session/swapchains and the final "submit quad layers instead of
|
|
// presenting to a window" step. No stereo rendering - the game composite is
|
|
// shown as a single flat quad floating in front of the viewer.
|
|
#ifndef QUESTSHOCK_XR_SESSION_H
|
|
#define QUESTSHOCK_XR_SESSION_H
|
|
|
|
#include <stdbool.h>
|
|
|
|
#include "xr_overlay.h"
|
|
|
|
#ifdef __cplusplus
|
|
extern "C" {
|
|
#endif
|
|
|
|
// Call once, right after init_opengl() (OpenGL.cc) has created and made
|
|
// current the GL context SDL/gl4es already use - creates the OpenXR
|
|
// instance/session sharing that same EGL display/context, plus the game
|
|
// quad's own swapchain, sized to the game's logical resolution
|
|
// (game_width/height, i.e. grd_cap->w/h - see Shock.c's InitSDL()). Also
|
|
// creates the menu launcher and keyboard overlay quads (see xr_overlay.h) -
|
|
// two independent XrOverlay instances, each with their own swapchain,
|
|
// visibility, and position, retrievable via xr_session_get_menu_overlay()/
|
|
// xr_session_get_keyboard_overlay() below. Returns false if OpenXR
|
|
// bring-up failed (e.g. no runtime installed) - callers should fall back
|
|
// to the existing window-present path in that case.
|
|
bool xr_init(int game_width, int game_height);
|
|
|
|
// The menu launcher (small "Keyboard" button panel, toggled by the
|
|
// controller's menu button) and keyboard (the on-screen key grid, shown/
|
|
// hidden independently via MenuOverlay's "Keyboard" button and
|
|
// KeyboardOverlay's own Close button) overlay quads - used by xr_input.c
|
|
// for ray/quad hit-testing and touch/cursor dispatch, and by
|
|
// questshock_native.c's JNI glue (nativeShowKeyboard()/
|
|
// nativeRequestClose()). NULL if xr_init() failed before creating them.
|
|
XrOverlay *xr_session_get_menu_overlay(void);
|
|
XrOverlay *xr_session_get_keyboard_overlay(void);
|
|
|
|
// Pumps XR session-state events. Call once per frame, before
|
|
// xr_frame_begin(). Must still be called even when xr_init() returned
|
|
// false (no-op in that case).
|
|
void xr_poll_events(void);
|
|
|
|
// True once the session has reached a state where frames are expected
|
|
// (XR_SESSION_STATE_SYNCHRONIZED or later) - i.e. it's safe/required to
|
|
// start calling xr_frame_begin()/xr_frame_end() each frame.
|
|
bool xr_is_session_running(void);
|
|
|
|
// Begins the XR frame (xrWaitFrame/xrBeginFrame) and, if the runtime wants
|
|
// this frame rendered, acquires the game quad's next swapchain image and
|
|
// binds its framebuffer as the current render target - ready for the
|
|
// caller to draw into exactly as it would have drawn to the default
|
|
// framebuffer. Returns true if the caller should draw this frame;
|
|
// xr_frame_end() must be called unconditionally afterward either way (a
|
|
// begun XR frame must always be ended, rendered or not).
|
|
bool xr_frame_begin(void);
|
|
|
|
// Releases the game quad's swapchain image (if one was acquired this
|
|
// frame), then does the same acquire/render/release cycle for the menu
|
|
// launcher and keyboard overlay quads via xr_overlay_render_and_build_layer()
|
|
// for whichever of them is currently visible, and submits whichever of the
|
|
// three quads actually rendered this frame as composition layers
|
|
// positioned in front of the play space's origin (see
|
|
// xr_session_set_player_transform() above), then ends the XR frame.
|
|
void xr_frame_end(void);
|
|
|
|
void xr_shutdown(void);
|
|
|
|
// Play-space geometry of the single game screen xr_frame_end() submits
|
|
// (distance in front of the play space origin, half-width/half-height,
|
|
// all in meters) - shared with xr_input.c's ray/quad hit-testing so the
|
|
// laser pointer always matches whatever's actually visible. *radius_m/
|
|
// *central_angle_rad are 0 when flat, in which case *half_width_m is the
|
|
// horizontal extent; otherwise the screen is a curved cylinder segment
|
|
// and the caller uses *central_angle_rad instead of *half_width_m for the
|
|
// horizontal bound (*half_height_m stays meaningful either way). Curving
|
|
// is currently unused - qs_config.c has no config surface for it - but
|
|
// kept working so it can be re-enabled later.
|
|
void xr_get_game_quad_extent(float *distance_m, float *half_width_m, float *half_height_m,
|
|
float *radius_m, float *central_angle_rad);
|
|
|
|
// Sets the game screen's current physical width (meters) and curve (wrap
|
|
// angle in degrees, 0 = flat) - a plain state setter, no XR calls, same
|
|
// category as xr_session_set_player_transform() above (safe to call
|
|
// before xr_init() has even run). Read by both xr_frame_end() (to build/
|
|
// submit the actual layer) and, via xr_get_game_quad_extent() above,
|
|
// xr_input.c's hit-testing. Driven by qs_config.c's
|
|
// qs_screen_profile_notify_loop_mode(), which decides which of the
|
|
// "Initial Screen"/"Game Screen" config profiles (see CONFIG.md) is
|
|
// currently active - this function itself has no notion of "profiles",
|
|
// only "what shape is current".
|
|
void xr_session_set_game_screen_shape(float width_m, float curve_deg, float distance_m);
|
|
|
|
// The engine's current screen resolution (gScreenWide/gScreenHigh,
|
|
// InitMac.h) - not the game quad's own swapchain size, which is fixed at
|
|
// launch and never changes - the logical coordinate space xr_input.c needs
|
|
// to convert a game-quad ray hit's normalized u,v into engine mouse
|
|
// coordinates (see xr_mouse.h). Can change at runtime via the in-game
|
|
// options menu (ShockBitmap.c's ChangeScreenSize()), independently of the
|
|
// swapchain.
|
|
void xr_get_game_resolution(int *width, int *height);
|
|
|
|
// Pixel size of the game quad's swapchain image, fixed at launch (0, 0 before
|
|
// xr_init()). The quad's aspect ratio is this size, so full-quad content
|
|
// drawn outside the engine's own frame path (the startup splash) is laid out
|
|
// against it.
|
|
void xr_get_game_swapchain_size(int *width, int *height);
|
|
|
|
// Exponentially-smoothed frame rate/frame time, updated once per real
|
|
// rendered frame in xr_frame_end() - read by MenuOverlay's header (see
|
|
// questshock_native.c's nativeGetFps()/nativeGetFrameTimeMs()). 0 before
|
|
// the second frame has run (nothing to compute a delta from yet).
|
|
float xr_session_get_fps(void);
|
|
float xr_session_get_frame_ms(void);
|
|
|
|
// Device-wide CPU/GPU utilization percentage (0-100), via the
|
|
// XR_META_performance_metrics extension - same per-frame update cadence as
|
|
// the FPS/frame-time getters above. Returns -1.0f if the extension isn't
|
|
// supported by the runtime, or its cpu_utilization/gpu_utilization counter
|
|
// couldn't be found among the ones it advertises.
|
|
float xr_session_get_cpu_utilization(void);
|
|
float xr_session_get_gpu_utilization(void);
|
|
|
|
// The player's virtual position/orientation - horizontal-plane only (x/z
|
|
// plus a yaw rotation about the vertical axis); height always comes from
|
|
// the real tracked HMD pose, never from this. Everything the app submits
|
|
// (game/menu/keyboard/beam quads) and every hand/head pose it locates is
|
|
// expressed relative to a second reference space, derived from the real
|
|
// LOCAL space by this transform's inverse (see xr_session.c's own
|
|
// xr_session_rebuild_play_space()) - so changing it moves/turns the player
|
|
// relative to those otherwise-fixed quads, letting them walk up to and
|
|
// square up with the game quad ("virtual screen") without needing any
|
|
// physical room-scale space to do it in.
|
|
//
|
|
// xr_session_set_player_transform() only stores the three floats (no XR
|
|
// calls) - safe to call before xr_init() has even run, which is how
|
|
// qs_config_load_startup() seeds the saved position before the OpenXR
|
|
// session/spaces exist yet (xr_init() picks it up when it creates its own
|
|
// spaces); also used live by xr_input.c's thumbstick locomotion and
|
|
// "Reset Position" recenter (xr_input_recenter_player_to_screen()) and
|
|
// qs_config.c's "Load Configuration", and read back by "Save Configuration"
|
|
// via the getter.
|
|
void xr_session_set_player_transform(float x, float z, float yaw_deg);
|
|
void xr_session_get_player_transform(float *x, float *z, float *yaw_deg);
|
|
|
|
// The real, physical tracking-origin reference space - used only by
|
|
// xr_input_recenter_player_to_screen() (xr_input.c), which needs the
|
|
// player's CURRENT real head pose (not one already bent through the
|
|
// existing play-space transform - see xr_session_set_player_transform()'s
|
|
// own comment above) to compute a brand new one. Every other consumer in
|
|
// this app (rendering, hit-testing) uses the play space instead - see
|
|
// xr_session_rebuild_play_space()'s own comment for why this one stays a
|
|
// separate, never-swapped-out space.
|
|
XrSpace xr_session_get_local_space(void);
|
|
|
|
// The most recent xrWaitFrame-predicted display time (xr_frame_begin()'s
|
|
// own g_predicted_display_time) - for the same reason as
|
|
// xr_session_get_local_space() above, xr_input_recenter_player_to_screen()
|
|
// needs a valid XrTime to locate a space with but isn't itself called from
|
|
// the normal per-frame xr_frame_end()/xr_input_sync_and_draw() call chain
|
|
// that already threads one through (it's triggered by a button press,
|
|
// dispatched via qs_config.c's cross-thread event queue instead). A frame
|
|
// or so of staleness doesn't matter for a one-shot recenter. 0 before the
|
|
// first frame has ever begun.
|
|
XrTime xr_session_get_predicted_display_time(void);
|
|
|
|
#ifdef __cplusplus
|
|
}
|
|
#endif
|
|
|
|
#endif
|