--- a/src/GameSrc/frsetup.c +++ b/src/GameSrc/frsetup.c @@ -635,7 +635,27 @@ uchar old_cam_type; int detail; +#ifdef __ANDROID__ + // should_opengl_swap() (unlike use_opengl(), see its own + // FR_PICKUPM_MASK|FR_HACKCAM_MASK exclusion just below and the + // matching one used for the STEREO_SUPPORT path in fr_send_view() + // below) doesn't know which _fr context is currently being prepared - + // it's a frame-level check, also used by Shock.c's SDLDraw() to pick + // the frame's overall compositing path. render_hack_cameras() (see + // render.c) calls fr_rend() - and so this function - a second time per + // real frame for each active hack-camera monitor, rendering into a + // private software (FR_DOUBLEB_MASK) canvas rather than the actual + // GL-backed screen. Without this exclusion, opengl_start_frame() runs + // again mid-frame for that render even though its own polygon draw + // calls correctly stay on the software path (use_opengl() already + // excludes them) - re-entering the OpenXR swapchain/framebuffer setup + // a second time before the first call's frame has ended. Not known to + // be exercised on desktop (no OpenXR swapchain there), so this fix is + // kept Android-only rather than changing should_opengl_swap() itself. + if (should_opengl_swap() && !(_fr_curflags & (FR_PICKUPM_MASK | FR_HACKCAM_MASK))) { +#else if(should_opengl_swap()) { +#endif opengl_start_frame(); } @@ -823,7 +843,14 @@ g3_end_frame(); +#ifdef __ANDROID__ + // Mirrors the same fix in fr_start_view() above - see its comment. + // opengl_start_frame()/opengl_end_frame() must stay paired per real + // GL frame, so this exclusion has to match that one exactly. + if (should_opengl_swap() && !(_fr_curflags & (FR_PICKUPM_MASK | FR_HACKCAM_MASK))) { +#else if(should_opengl_swap()) { +#endif opengl_end_frame(); }