Files
questshock/android/app/src/main/cpp/xr_session.h
T
ml 1be0bebda2
build / build (push) Successful in 1m43s
Split the menu quad back onto its own independent OpenXR swapchain
Replaces the single shared, side-by-side swapchain (game + menu content
packed into one image, needing viewport/scissor juggling to keep the
menu's own glClear from bleeding into the game's region) with two fully
independent XrSwapchainState instances, each sized to exactly its own
content and acquired/released on its own within the same
xrBeginFrame/xrEndFrame pair - an ordinary multi-layer OpenXR setup.

The shared-swapchain design was originally adopted to work around what
looked like a Horizon OS compositor limitation with independent
swapchains, but that was diagnosed before the real root cause of the
"menu shows the game's content" bug was found (gl4es's fpe.c
unconditionally substituting its own shader onto the menu's draw call -
see README's Debugging notes). Since the actual fix routes the menu's
blit through a real, non-gl4es glBlitFramebuffer() call - orthogonal to
how many swapchains exist - splitting back onto two swapchains works
fine and removes the GL_SCISSOR_TEST workaround and sub-rectangle
offset math entirely. Confirmed on-device: both quads render correctly,
hit-testing and 90 FPS unaffected.

Also adds a visible cursor to the menu quad itself: xr_input.c now
tracks whichever hand's aim ray hits the menu each frame and forwards it
to a new MenuOverlay.nativeUpdateCursor(), which draws a small dot at
that position (composited through the same Bitmap the menu's own
content already goes through) - previously there was no visual feedback
at all showing where you were pointing before pulling the trigger.
2026-08-02 08:01:14 +02:00

66 lines
2.9 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>
#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()). The
// menu quad's own, independently-sized swapchain (see xr_menu.h) is created
// alongside it. 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);
// 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), acquires/renders/releases the menu quad's own swapchain image if
// it's currently visible (see xr_menu.h), submits whichever of the two
// quads actually rendered this frame as composition layers positioned in
// front of the local reference space's origin, then ends the XR frame.
void xr_frame_end(void);
void xr_shutdown(void);
// Local-space geometry of the single game quad xr_frame_end() submits
// (distance in front of the local 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.
void xr_get_game_quad_extent(float *distance_m, float *half_width_m, float *half_height_m);
#ifdef __cplusplus
}
#endif
#endif