Fix persistent GL_INVALID_OPERATION spam and resolution-change bugs on Android
build / build (push) Successful in 2m44s
build / build (push) Successful in 2m44s
- opengl_end_frame() bound framebuffer 0 on Android, which has no valid target there (real presentation goes through the OpenXR swapchain's own FBOs) - gl4es reported this every frame. Skip it there. - render_hack_cameras() re-entered opengl_start_frame()/opengl_end_frame() for camera-monitor renders that never touch the GL screen; excluded them to match use_opengl()'s existing behavior. Also drains a stray error left by xrPollEvent(). - ChangeScreenSize() never called opengl_resize() on Android, freezing the 3D render target at launch size after an in-game resolution change; and xr_get_game_resolution() used the swapchain's fixed size instead of the current screen resolution, misplacing the laser-pointer cursor after such a change. - Removes all temporary bisection diagnostics added while tracking down the GL_INVALID_OPERATION source.
This commit is contained in:
@@ -402,6 +402,10 @@ each one does:
|
||||
`SDL_SetWindowFullscreen`/`SetWindowSize`/`SetWindowPosition` calls on
|
||||
Android, since there's no real desktop-style window to resize and
|
||||
calling them desyncs SDL's cached window size from the real surface.
|
||||
Also calls `opengl_resize()` directly in `ChangeScreenSize()`
|
||||
(see README 5.5.6) - skipping `SDL_SetWindowSize()` means no native
|
||||
resize event ever fires on Android to trigger it when the in-game
|
||||
resolution changes, so it would otherwise never re-run there.
|
||||
- **`10-android-openxr-cmake.patch`** - wires up the OpenXR loader plus
|
||||
EGL as link/include dependencies for the CMake build.
|
||||
- **`11-android-openxr-present.patch`** - the core OpenXR present-path
|
||||
@@ -464,6 +468,46 @@ each one does:
|
||||
silently forever instead of falling back. Adds an `#else` branch that
|
||||
returns failure and logs a `WARN`, so `InitDecXMI()`'s existing
|
||||
fallback-to-dummy-driver logic actually triggers.
|
||||
- **`18-android-opengl-immediate-mode-attribs.patch`** - fixes a black (and,
|
||||
after this patch's first revision, flat-colored/untextured) screen when
|
||||
switching to the OpenGL/3D render mode in-game (see README 5.5.5). Every
|
||||
`glBegin(GL_TRIANGLE_STRIP)`-based draw in `OpenGL.cc` -
|
||||
`opengl_swap_and_restore()`'s screen-composite quad,
|
||||
`opengl_light_tmap()` (every textured wall/floor polygon, via
|
||||
`draw_vertex()`), `opengl_bitmap()` (sprites), and `opengl_draw_poly()`
|
||||
(flat/gouraud polygons, also via `draw_vertex()`) - used
|
||||
`glVertexAttrib2f`/`glVertexAttrib1f`/`glVertex3f` immediate mode to set
|
||||
per-vertex texcoords/light, which gl4es only captures per-vertex for
|
||||
attribute 0 (position); custom attributes end up constant for the whole
|
||||
draw call. Adds `android_imm_begin()`/`android_imm_vertex()`/
|
||||
`android_imm_end()`, a small shared immediate-mode-emulation shim that
|
||||
buffers vertices and flushes them as one real `glDrawArrays()` call, and
|
||||
switches every one of those call sites to it, Android-only. The
|
||||
starfield (`opengl_begin_stars()`/`opengl_draw_star()`/
|
||||
`opengl_end_stars()`, `GL_POINTS` rather than `GL_TRIANGLE_STRIP`, up to
|
||||
`NUM_STARS`=1000 per frame) had the same bug for its per-star brightness
|
||||
and gets its own similarly-shaped `android_stars_begin()`/
|
||||
`android_stars_vertex()`/`android_stars_end()` buffer. Also skips the
|
||||
unconditional `SDL_GL_MakeCurrent(window, context)` call every
|
||||
rendering function in this file had at its top, Android-only - a
|
||||
worthwhile cleanup on its own merits, but *not* the source of the
|
||||
persistent per-frame `GL_INVALID_OPERATION` (1282) logcat spam during
|
||||
3D gameplay that this was first suspected to be (see README 5.5.8 for
|
||||
that bug's actual root cause and fix, in patch 20 below).
|
||||
- **`19-android-fix-hackcam-opengl-frame.patch`** - `fr_start_view()`/
|
||||
`fr_send_view()` (`frsetup.c`) called `opengl_start_frame()`/
|
||||
`opengl_end_frame()` again for hack-camera security-monitor renders
|
||||
(which target a private software bitmap, not the GL-backed screen),
|
||||
since `should_opengl_swap()` doesn't exclude them the way
|
||||
`use_opengl()` already does. Adds the same exclusion to both call
|
||||
sites (see README 5.5.8).
|
||||
- **`20-android-no-default-framebuffer.patch`** - the actual fix for the
|
||||
persistent per-frame `GL_INVALID_OPERATION` (1282) in README 5.5.8:
|
||||
`opengl_end_frame()`'s `BindFrameBuffer(nullptr)` bind to framebuffer/
|
||||
renderbuffer id 0 has no valid target on Android (real presentation
|
||||
goes through the OpenXR swapchain's own FBOs, set up separately in
|
||||
`xr_swapchain.c`) and is also functionally dead there regardless -
|
||||
skipped entirely, Android-only.
|
||||
|
||||
`android/gl4es-patches/`:
|
||||
|
||||
@@ -600,6 +644,189 @@ path (still silent, but with a clear `WARN` in logcat) instead of
|
||||
silently reporting success while never opening anything - a much faster
|
||||
diagnosis if this ever recurs.
|
||||
|
||||
#### 5.5.5. Black, then flat-colored/untextured screen after switching to OpenGL rendering in-game
|
||||
|
||||
The engine has a `Ctrl+G`-bound in-game toggle (`toggle_opengl()`,
|
||||
`Prefs.c`) that cycles between the plain software renderer and OpenGL
|
||||
rendering. On Quest, switching to OpenGL rendering (either mode:
|
||||
unfiltered or bilinear) produced a black screen for the 3D view, with
|
||||
`OpenGL error: 1282` (`GL_INVALID_OPERATION`) in logcat right at the switch.
|
||||
|
||||
**Root cause:** `opengl_swap_and_restore()` draws the offscreen-rendered
|
||||
3D view onto a screen-filling quad using `glBegin(GL_TRIANGLE_STRIP)` /
|
||||
`glVertexAttrib2f()`/`glVertexAttrib1f()` / `glVertex3f()` immediate
|
||||
mode to supply per-vertex texcoords and light. This is exactly the same
|
||||
bug already found and fixed for the UI-overlay quad in
|
||||
`11-android-openxr-present.patch`'s `android_draw_surface_as_quad()`:
|
||||
under gl4es's GLES2 immediate-mode emulation, `glVertexAttrib*()` sets a
|
||||
*constant* value for the whole draw call, not a fresh value per vertex
|
||||
the way desktop GL's immediate mode does - only attribute 0 (position,
|
||||
driven by `glVertex3f`) gets real per-vertex capture. The 3D-view quad
|
||||
draw predates that fix and was never updated to match, so it kept using
|
||||
the broken pattern.
|
||||
|
||||
This patch's first revision only fixed that one quad, which did clear the
|
||||
black screen - but the next on-device test showed a "blocky" world
|
||||
instead: every wall/floor polygon rendered as a single solid color
|
||||
instead of its texture. The same broken immediate-mode pattern turned out
|
||||
to be used by every other textured/lit polygon draw in the file too:
|
||||
`opengl_light_tmap()` (every textured wall/floor polygon in the 3D view,
|
||||
via the shared `draw_vertex()` helper), `opengl_bitmap()` (sprites), and
|
||||
`opengl_draw_poly()` (flat/gouraud-shaded polygons, also via
|
||||
`draw_vertex()`) - each polygon's texcoord/light attribute ended up
|
||||
constant across all of its vertices (whatever was last set), instead of
|
||||
varying per vertex, hence the flat, solid-colored blocks.
|
||||
|
||||
**Fix:** `18-android-opengl-immediate-mode-attribs.patch` adds
|
||||
`android_imm_begin()`/`android_imm_vertex()`/`android_imm_end()`, a small
|
||||
shared immediate-mode-emulation shim (buffers vertices, then issues one
|
||||
real `glDrawArrays()` call), and switches every glBegin/glVertexAttrib/
|
||||
glVertex3f call site above to it, Android-only - the desktop path is
|
||||
unaffected and keeps the original immediate-mode code, which works
|
||||
correctly under real desktop OpenGL.
|
||||
|
||||
The same patch also fixes the starfield
|
||||
(`opengl_begin_stars()`/`opengl_draw_star()`/`opengl_end_stars()`), found
|
||||
while auditing the file for every other instance of this pattern rather
|
||||
than through an on-device symptom: each star is a separate `GL_POINTS`
|
||||
vertex with its own brightness set via the same
|
||||
`glVertexAttrib1f`/`glVertex3f` immediate-mode pair, so every star drawn
|
||||
between one begin/end call would have rendered at whatever brightness was
|
||||
set for the last one. Gets its own `android_stars_begin()`/
|
||||
`android_stars_vertex()`/`android_stars_end()` buffer, shaped for
|
||||
`GL_POINTS` and up to `NUM_STARS` (`GameSrc/rendtool.c`) vertices rather
|
||||
than `android_imm_*()`'s small triangle-strip buffer.
|
||||
|
||||
#### 5.5.6. Display breaks and stops responding to input after changing resolution in-game
|
||||
|
||||
Picking a different resolution from the in-game options menu (e.g.
|
||||
`1024x768`) produced a single `OpenGL error: 1282` in logcat and left the
|
||||
display frozen on the last frame - `VrApi`'s FPS counter kept running at
|
||||
90fps (the OpenXR compositor was still submitting frames), but nothing
|
||||
in the menu responded to further input afterward.
|
||||
|
||||
**Root cause:** `ChangeScreenSize()` (`ShockBitmap.c`) calls
|
||||
`SDL_RenderSetLogicalSize()` to the new resolution and reallocates
|
||||
`drawSurface`/`offscreenDrawSurface` accordingly, but on desktop the
|
||||
actual GL-side work - `opengl_resize()` in `OpenGL.cc`, which
|
||||
(re)creates `backupBuffer` (the offscreen render target the 3D view
|
||||
draws into) at the new size - only runs later, from the
|
||||
`SDL_WINDOWEVENT_RESIZED`/`SIZE_CHANGED` event that
|
||||
`SDL_SetWindowSize()` generates. `09-android-no-window-resize.patch`
|
||||
skips that `SDL_SetWindowSize()` call on Android for an unrelated
|
||||
reason (see its own entry above) - which also means no such event, and
|
||||
therefore no `opengl_resize()` call, ever happens on Android when the
|
||||
in-game resolution changes. `backupBuffer` stayed stuck at whatever size
|
||||
it was created with at launch while the rest of the game moved on to
|
||||
the new resolution, and that mismatch is what broke rendering and left
|
||||
the menu unable to correctly hit-test further clicks.
|
||||
|
||||
**Fix:** `ChangeScreenSize()` now calls `opengl_resize()` directly on
|
||||
Android, right after updating `gScreenWide`/`gScreenHigh` (matching the
|
||||
desktop event's actual timing, since `SDL_PollEvent()` always delivers
|
||||
it after `ChangeScreenSize()` has already returned) - added to
|
||||
`09-android-no-window-resize.patch` since this is a direct, foreseeable
|
||||
consequence of that patch's own change, not a new mechanism.
|
||||
|
||||
#### 5.5.7. Laser-pointer cursor disappears after changing resolution in-game
|
||||
|
||||
After 5.5.6's fix, changing resolution stopped breaking the display, but
|
||||
the laser-pointer-driven mouse cursor (see section 5's architecture
|
||||
overview) would disappear and stop being able to click anything
|
||||
afterward.
|
||||
|
||||
**Root cause:** `xr_get_game_resolution()` (`xr_session.c`), which
|
||||
`xr_input.c` uses to convert a game-quad ray hit's normalized u,v into
|
||||
the pixel coordinates it feeds the engine via `xr_mouse_move()`, returned
|
||||
the game quad's OpenXR swapchain's fixed pixel size
|
||||
(`g_game_swapchain.width/height`) - set once at launch and never resized.
|
||||
`android_draw_surface_as_quad()` (`OpenGL.cc`,
|
||||
`11-android-openxr-present.patch`) always stretch-blits `drawSurface` to
|
||||
fill that swapchain image via a single full-quad draw, regardless of
|
||||
`drawSurface`'s own pixel size, so the swapchain's fixed size has no
|
||||
actual relationship to the coordinate space `MouseX`/`MouseY` live in -
|
||||
that's `drawSurface`'s own current size (`gScreenWide`/`gScreenHigh`),
|
||||
which `ChangeScreenSize()` can change at runtime independently of the
|
||||
swapchain. Using the stale swapchain size there mapped every ray hit
|
||||
into the wrong coordinate space, moving the mouse position out of the
|
||||
engine's actual screen bounds.
|
||||
|
||||
**Fix:** `xr_get_game_resolution()` now returns `gScreenWide`/
|
||||
`gScreenHigh` instead. `xr_get_game_quad_extent()` (the quad's shape and
|
||||
distance in the VR world) is intentionally left as-is, still tied to the
|
||||
swapchain's fixed size - that's a real, unchanging property of the
|
||||
swapchain image itself, unrelated to this bug.
|
||||
|
||||
#### 5.5.8. Persistent `OpenGL error: 1282` logcat spam during 3D gameplay
|
||||
|
||||
Every logcat capture during actual 3D gameplay showed a continuous
|
||||
stream of `OpenGL error: 1282` (roughly twice per frame) from the one
|
||||
pre-existing `glGetError()` check in `opengl_swap_and_restore()`, with
|
||||
no visible effect on rendering (textures, resolution changes, and the
|
||||
cursor all worked correctly per sections 5.5.5-5.5.7's fixes). 1282 is
|
||||
`GL_INVALID_OPERATION` (0x0502) - not `GL_INVALID_ENUM` (0x0500/1280),
|
||||
which earlier debugging had mistakenly assumed given the visual
|
||||
symptoms resembled the immediate-mode bug in 5.5.5.
|
||||
|
||||
Two real, unrelated bugs turned out to be involved, found by adding a
|
||||
temporary chain of `glGetError()` checkpoints across the whole
|
||||
per-frame render path (`mainloop()` → `render_run()` → `fr_rend()` →
|
||||
`fr_prepare_view()`/`fr_start_view()`/`fr_send_view()`) and bisecting
|
||||
which ones read clean:
|
||||
|
||||
**xrPollEvent() leaves a stray error.** `xr_poll_events()`
|
||||
(`xr_session.c`) left gl4es reporting `GL_INVALID_OPERATION` on every
|
||||
call while the session is running, regardless of what this app's own
|
||||
code does. Fixed by draining it (`while (glGetError() != GL_NO_ERROR)`)
|
||||
right after the polling loop.
|
||||
|
||||
**A hack-camera render re-entered the OpenXR frame setup.**
|
||||
`render_hack_cameras()` (`render.c`) calls `fr_rend()` - and so
|
||||
`fr_start_view()`/`fr_send_view()` - a second time per real frame for
|
||||
each active security-camera monitor, rendering into a private software
|
||||
(`FR_DOUBLEB_MASK`) bitmap rather than the actual GL-backed screen.
|
||||
`use_opengl()` already excludes hack-camera/pickup-mode views from the
|
||||
GL texture-mapping functions (`FR_PICKUPM_MASK|FR_HACKCAM_MASK`), but
|
||||
`should_opengl_swap()` - the separate check gating
|
||||
`opengl_start_frame()`/`opengl_end_frame()` in those two functions -
|
||||
didn't, so those ran again mid-frame for a render that was never
|
||||
GL-backed. `19-android-fix-hackcam-opengl-frame.patch` adds the same
|
||||
exclusion to both call sites. This is a genuine bug fixed on its own
|
||||
merits, but bisecting further showed it wasn't actually the source of
|
||||
the 1282 spam - removing it just relocated the checkpoint that caught
|
||||
the error, without changing whether it fired.
|
||||
|
||||
**Actual root cause: binding "framebuffer 0" on Android has no valid
|
||||
target.** `opengl_end_frame()`'s `BindFrameBuffer(nullptr)` binds
|
||||
framebuffer/renderbuffer id 0 to mean "the real window surface" - true
|
||||
on desktop, where that's SDL's actual GL window. On Android, this app's
|
||||
real presentation target is never that window's own EGL surface at
|
||||
all - it's the OpenXR swapchain's per-image FBOs
|
||||
(`xr_swapchain_create()`/`xr_swapchain_acquire()`, `xr_swapchain.c`).
|
||||
Whatever framebuffer/renderbuffer 0 actually resolves to for gl4es in
|
||||
this configuration, binding it reliably set a persistent
|
||||
`GL_INVALID_OPERATION` - confirmed by bisecting through the *entire*
|
||||
render path with per-call checkpoints, all clean right up until the one
|
||||
placed immediately after this exact call. The bind is also functionally
|
||||
dead regardless of the error: by the time anything next draws, Shock.c's
|
||||
`SDLDraw()` has already run `xr_frame_begin()` →
|
||||
`xr_swapchain_acquire()`, which rebinds to that frame's real swapchain
|
||||
FBO before `opengl_swap_and_restore()` (the only thing that draws before
|
||||
the next `opengl_start_frame()`'s own `BindFrameBuffer(&backupBuffer)`)
|
||||
runs - so nothing ever actually renders while framebuffer 0 is bound.
|
||||
`20-android-no-default-framebuffer.patch` skips the call on Android
|
||||
entirely. Confirmed via logcat: this fully eliminated the error across
|
||||
an extended play session with 3D rendering active throughout, with no
|
||||
change in what actually renders.
|
||||
|
||||
An earlier theory (that the OpenXR runtime's own GL work inside
|
||||
`xrAcquireSwapchainImage`/`xrWaitSwapchainImage`/`xrEndFrame` was
|
||||
leaving stray driver-level errors on the shared EGL context) prompted a
|
||||
dlsym'd, gl4es-bypassing `glGetError()` drain at those call sites. Once
|
||||
the above was fixed, testing showed that drain was never actually
|
||||
necessary, so it was removed rather than kept as unproven defensive
|
||||
code.
|
||||
|
||||
## 6. License
|
||||
|
||||
The original tooling in this repository (the Docker build image, build
|
||||
|
||||
@@ -319,6 +319,14 @@ void xr_poll_events(void) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// xrPollEvent() leaves gl4es reporting a stray GL_INVALID_OPERATION on
|
||||
// every call while the session is running - drain it (looped, since
|
||||
// more than one can accumulate across xrPollEvent()'s possibly-multiple
|
||||
// iterations above) so it doesn't surface as a false positive at the
|
||||
// next unrelated glGetError() check in this app's own rendering code.
|
||||
while (glGetError() != GL_NO_ERROR) {
|
||||
}
|
||||
}
|
||||
|
||||
bool xr_is_session_running(void) { return g_session_running; }
|
||||
@@ -434,6 +442,7 @@ void xr_frame_end(void) {
|
||||
endInfo.environmentBlendMode = XR_ENVIRONMENT_BLEND_MODE_OPAQUE;
|
||||
endInfo.layerCount = layerCount;
|
||||
endInfo.layers = layerCount > 0 ? layers : NULL;
|
||||
|
||||
xr_check(xrEndFrame(g_session, &endInfo), "xrEndFrame");
|
||||
}
|
||||
|
||||
@@ -471,6 +480,21 @@ void xr_get_game_quad_extent(float *distance_m, float *half_width_m, float *half
|
||||
}
|
||||
|
||||
void xr_get_game_resolution(int *width, int *height) {
|
||||
*width = g_game_swapchain.width;
|
||||
*height = g_game_swapchain.height;
|
||||
// Not g_game_swapchain.width/height: that's the fixed physical size of
|
||||
// the swapchain image the game is composited into (see xr_init()) -
|
||||
// android_draw_surface_as_quad() (OpenGL.cc, engine-patches/11) always
|
||||
// stretch-blits drawSurface to fill that image regardless of
|
||||
// drawSurface's own pixel size, so the swapchain's size has no relation
|
||||
// to the coordinate space MouseX/MouseY actually live in. gScreenWide/
|
||||
// gScreenHigh (InitMac.h) is drawSurface's current size - the
|
||||
// resolution the in-game options menu can change at runtime
|
||||
// (ShockBitmap.c's ChangeScreenSize()) independently of the swapchain,
|
||||
// which is created once at launch and never resized. Using the fixed
|
||||
// swapchain size here after such a change mapped the laser pointer's
|
||||
// quad-space hit into the wrong coordinate space entirely, moving
|
||||
// MouseX/MouseY out of the engine's actual screen bounds - which is
|
||||
// what made the cursor disappear and become unclickable.
|
||||
extern int32_t gScreenWide, gScreenHigh;
|
||||
*width = gScreenWide;
|
||||
*height = gScreenHigh;
|
||||
}
|
||||
|
||||
@@ -74,11 +74,13 @@ void xr_shutdown(void);
|
||||
// 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);
|
||||
|
||||
// Pixel resolution of the game quad's own swapchain - i.e. game_width/
|
||||
// game_height as passed to xr_init() (grd_cap->w/h, see Shock.c's
|
||||
// InitSDL()) - 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).
|
||||
// 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);
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
@@ -149,6 +149,7 @@ bool xr_swapchain_acquire(XrInstance instance, XrSwapchainState *state) {
|
||||
// (android_draw_surface_as_quad()) land in the right place.
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, state->fbos[imageIndex]);
|
||||
glViewport(0, 0, state->width, state->height);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -6,8 +6,6 @@ import android.content.Context;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.res.AssetManager;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.util.Log;
|
||||
import android.view.SurfaceHolder;
|
||||
import androidx.core.app.ActivityCompat;
|
||||
|
||||
@@ -1,6 +1,16 @@
|
||||
--- a/src/MacSrc/ShockBitmap.c
|
||||
+++ b/src/MacSrc/ShockBitmap.c
|
||||
@@ -42,11 +42,26 @@
|
||||
@@ -27,6 +27,9 @@
|
||||
#include "Shock.h"
|
||||
#include "ShockBitmap.h"
|
||||
#include "2d.h"
|
||||
+#ifdef __ANDROID__
|
||||
+#include "OpenGL.h"
|
||||
+#endif
|
||||
|
||||
//--------------------
|
||||
// Globals
|
||||
@@ -42,11 +45,26 @@
|
||||
|
||||
SDL_RenderClear(renderer);
|
||||
|
||||
@@ -27,3 +37,32 @@
|
||||
|
||||
SDL_RenderSetLogicalSize(renderer, width, height);
|
||||
|
||||
@@ -54,6 +72,28 @@
|
||||
|
||||
gScreenWide = width;
|
||||
gScreenHigh = height;
|
||||
+
|
||||
+#ifdef __ANDROID__
|
||||
+ // Skipping SDL_SetWindowSize() above (see the comment for why) also
|
||||
+ // means no native resize event ever fires on Android when the in-game
|
||||
+ // resolution changes - opengl_resize() (OpenGL.cc) only ever runs from
|
||||
+ // that event (see sdl_events.c) or once at OpenGL init, and it's what
|
||||
+ // (re)creates backupBuffer, the offscreen 3D-view render target, sized
|
||||
+ // to the logical resolution just changed above. Without this, it stays
|
||||
+ // stuck at whatever size it was at launch while the rest of the game
|
||||
+ // (drawSurface/gScreenWide/gScreenHigh, just updated above) moves on to
|
||||
+ // the new resolution - the actual cause of the display breaking and
|
||||
+ // becoming unresponsive to further input after changing resolution
|
||||
+ // in-game. Call it directly instead - after gScreenWide/gScreenHigh
|
||||
+ // update above, same as the desktop resize event (delivered via
|
||||
+ // SDL_PollEvent(), so always processed after this function has already
|
||||
+ // returned) - using the same physical window size init_opengl() itself
|
||||
+ // used (SDL_GetWindowSize()); that never changes on Android, only the
|
||||
+ // logical size set above does.
|
||||
+ int physWidth, physHeight;
|
||||
+ SDL_GetWindowSize(window, &physWidth, &physHeight);
|
||||
+ opengl_resize(physWidth, physHeight);
|
||||
+#endif
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------------
|
||||
|
||||
@@ -0,0 +1,455 @@
|
||||
--- a/src/MacSrc/OpenGL.cc
|
||||
+++ b/src/MacSrc/OpenGL.cc
|
||||
@@ -391,7 +391,16 @@
|
||||
}
|
||||
|
||||
void opengl_resize(int width, int height) {
|
||||
+#ifndef __ANDROID__
|
||||
+ // Skipped on Android - see opengl_swap_and_restore()'s comment below for
|
||||
+ // why re-asserting an already-current context/surface every call breaks
|
||||
+ // gl4es's own "current framebuffer" bookkeeping, even though the real
|
||||
+ // driver binding never actually changes. This turned out to be the
|
||||
+ // actual source of the persistent per-frame GL_INVALID_OPERATION (1282)
|
||||
+ // that plagued 3D rendering: this same unconditional call was present at
|
||||
+ // the top of nearly every function in this file, not just here.
|
||||
SDL_GL_MakeCurrent(window, context);
|
||||
+#endif
|
||||
|
||||
int logical_width, logical_height;
|
||||
SDL_RenderGetLogicalSize(renderer, &logical_width, &logical_height);
|
||||
@@ -443,7 +452,10 @@
|
||||
}
|
||||
|
||||
void opengl_end_frame() {
|
||||
+#ifndef __ANDROID__
|
||||
+ // See opengl_resize()'s comment above.
|
||||
SDL_GL_MakeCurrent(window, context);
|
||||
+#endif
|
||||
|
||||
// Done rendering to the frame buffer, reset back to normal
|
||||
BindFrameBuffer(nullptr);
|
||||
@@ -470,7 +482,10 @@
|
||||
static bool nightsight_active() { return WareActive(player_struct.hardwarez_status[HARDWARE_GOGGLE_INFRARED]); }
|
||||
|
||||
void opengl_start_frame() {
|
||||
+#ifndef __ANDROID__
|
||||
+ // See opengl_resize()'s comment above.
|
||||
SDL_GL_MakeCurrent(window, context);
|
||||
+#endif
|
||||
|
||||
// Start rendering to our frame buffer canvas
|
||||
BindFrameBuffer(&backupBuffer);
|
||||
@@ -500,6 +515,130 @@
|
||||
}
|
||||
|
||||
#ifdef __ANDROID__
|
||||
+// Real per-vertex arrays, not glBegin/glVertexAttrib/glVertex3f immediate
|
||||
+// mode: glVertexAttrib*() is GLES2's *constant*-attribute API - without an
|
||||
+// enabled vertex array it sets one value for the whole draw call, not a
|
||||
+// fresh value per vertex. gl4es's immediate-mode emulation only reproduces
|
||||
+// desktop GL's per-vertex-capture behavior for attribute 0 (position,
|
||||
+// driven directly by glVertex3f), not for custom attributes like this
|
||||
+// shader's texcoords/light. That means every one of this file's
|
||||
+// glBegin(GL_TRIANGLE_STRIP)-based draws - not just the UI-overlay quad
|
||||
+// this was first found on, but also opengl_light_tmap() (every textured
|
||||
+// wall/floor polygon), opengl_bitmap() (sprites) and opengl_draw_poly()
|
||||
+// (flat/gouraud polygons) via draw_vertex() - draws every vertex with
|
||||
+// whatever texcoord/light was set for the *last* vertex instead of its
|
||||
+// own, which is what actually produced flat-colored/untextured world
|
||||
+// geometry after this patch's first revision only fixed the final
|
||||
+// screen-composite quad (the one bug this was originally found on).
|
||||
+//
|
||||
+// android_imm_begin()/android_imm_vertex()/android_imm_end() below emulate
|
||||
+// just enough of glBegin(GL_TRIANGLE_STRIP)/glVertexAttrib*()/glVertex3f()
|
||||
+// immediate mode to replace every such call site on Android, by buffering
|
||||
+// vertices and flushing them as one real glDrawArrays() call - the engine's
|
||||
+// other rendering still uses real immediate mode for attribute 0 elsewhere
|
||||
+// on desktop builds, which depends on it not having an enabled array bound,
|
||||
+// so android_imm_end() always disables the arrays it enables before
|
||||
+// returning.
|
||||
+struct AndroidImmVertex {
|
||||
+ float x, y, z;
|
||||
+ float u, v;
|
||||
+ float light;
|
||||
+};
|
||||
+
|
||||
+// Arbitrarily-sized, but generous: the engine's polygons (room walls/
|
||||
+// floors, sprites, screen-filling quads) are all small, hand-authored
|
||||
+// shapes, never anywhere near this many vertices.
|
||||
+static AndroidImmVertex s_immVerts[64];
|
||||
+static int s_immVertCount;
|
||||
+static GLint s_immTcAttrib;
|
||||
+static GLint s_immLightAttrib;
|
||||
+
|
||||
+// tcAttrib < 0 means this draw has no texcoord attribute (flat/gouraud
|
||||
+// polygons via opengl_draw_poly()) - matches draw_vertex()'s own
|
||||
+// `tcAttrib >= 0` check below.
|
||||
+static void android_imm_begin(GLint tcAttrib, GLint lightAttrib) {
|
||||
+ s_immVertCount = 0;
|
||||
+ s_immTcAttrib = tcAttrib;
|
||||
+ s_immLightAttrib = lightAttrib;
|
||||
+}
|
||||
+
|
||||
+static void android_imm_vertex(float x, float y, float z, float u, float v, float light) {
|
||||
+ if (s_immVertCount >= (int)(sizeof(s_immVerts) / sizeof(s_immVerts[0]))) {
|
||||
+ WARN("android_imm_vertex: dropping vertex past the %d-vertex limit", s_immVertCount);
|
||||
+ return;
|
||||
+ }
|
||||
+ AndroidImmVertex &vert = s_immVerts[s_immVertCount++];
|
||||
+ vert.x = x;
|
||||
+ vert.y = y;
|
||||
+ vert.z = z;
|
||||
+ vert.u = u;
|
||||
+ vert.v = v;
|
||||
+ vert.light = light;
|
||||
+}
|
||||
+
|
||||
+static void android_imm_end() {
|
||||
+ if (s_immVertCount < 3)
|
||||
+ return;
|
||||
+ glEnableVertexAttribArray(0);
|
||||
+ if (s_immTcAttrib >= 0)
|
||||
+ glEnableVertexAttribArray(s_immTcAttrib);
|
||||
+ glEnableVertexAttribArray(s_immLightAttrib);
|
||||
+ glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(AndroidImmVertex), &s_immVerts[0].x);
|
||||
+ if (s_immTcAttrib >= 0)
|
||||
+ glVertexAttribPointer(s_immTcAttrib, 2, GL_FLOAT, GL_FALSE, sizeof(AndroidImmVertex),
|
||||
+ &s_immVerts[0].u);
|
||||
+ glVertexAttribPointer(s_immLightAttrib, 1, GL_FLOAT, GL_FALSE, sizeof(AndroidImmVertex),
|
||||
+ &s_immVerts[0].light);
|
||||
+ glDrawArrays(GL_TRIANGLE_STRIP, 0, s_immVertCount);
|
||||
+ glDisableVertexAttribArray(0);
|
||||
+ if (s_immTcAttrib >= 0)
|
||||
+ glDisableVertexAttribArray(s_immTcAttrib);
|
||||
+ glDisableVertexAttribArray(s_immLightAttrib);
|
||||
+}
|
||||
+
|
||||
+// Same underlying bug as android_imm_*() above, for opengl_draw_star():
|
||||
+// opengl_begin_stars()/opengl_draw_star()/opengl_end_stars() draw up to
|
||||
+// NUM_STARS (rendtool.c) individual GL_POINTS between one begin/end pair,
|
||||
+// each with its own per-star brightness via glVertexAttrib1f(lightAttrib,
|
||||
+// ...)/glVertex3f() immediate mode - under gl4es that made every star on
|
||||
+// screen render at whatever brightness was set for the *last* one. Shaped
|
||||
+// differently enough from android_imm_*() above (GL_POINTS instead of
|
||||
+// GL_TRIANGLE_STRIP, far more vertices, no texcoord) to warrant its own
|
||||
+// small buffer rather than overloading that one.
|
||||
+static AndroidImmVertex s_starVerts[1000]; // NUM_STARS, GameSrc/rendtool.c
|
||||
+static int s_starVertCount;
|
||||
+static GLint s_starLightAttrib;
|
||||
+
|
||||
+static void android_stars_begin(GLint lightAttrib) {
|
||||
+ s_starVertCount = 0;
|
||||
+ s_starLightAttrib = lightAttrib;
|
||||
+}
|
||||
+
|
||||
+static void android_stars_vertex(float x, float y, float z, float light) {
|
||||
+ if (s_starVertCount >= (int)(sizeof(s_starVerts) / sizeof(s_starVerts[0]))) {
|
||||
+ WARN("android_stars_vertex: dropping star past the %d-star limit", s_starVertCount);
|
||||
+ return;
|
||||
+ }
|
||||
+ AndroidImmVertex &vert = s_starVerts[s_starVertCount++];
|
||||
+ vert.x = x;
|
||||
+ vert.y = y;
|
||||
+ vert.z = z;
|
||||
+ vert.light = light;
|
||||
+}
|
||||
+
|
||||
+static void android_stars_end() {
|
||||
+ if (s_starVertCount == 0)
|
||||
+ return;
|
||||
+ glEnableVertexAttribArray(0);
|
||||
+ glEnableVertexAttribArray(s_starLightAttrib);
|
||||
+ glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(AndroidImmVertex), &s_starVerts[0].x);
|
||||
+ glVertexAttribPointer(s_starLightAttrib, 1, GL_FLOAT, GL_FALSE, sizeof(AndroidImmVertex),
|
||||
+ &s_starVerts[0].light);
|
||||
+ glDrawArrays(GL_POINTS, 0, s_starVertCount);
|
||||
+ glDisableVertexAttribArray(0);
|
||||
+ glDisableVertexAttribArray(s_starLightAttrib);
|
||||
+}
|
||||
+
|
||||
// Shared by opengl_swap_and_restore()'s UI-overlay blit and
|
||||
// android_composite_software_frame() below - draws an SDL_Surface as a
|
||||
// fullscreen textured quad via gl4es. SDL's own renderer can't be used
|
||||
@@ -537,42 +676,15 @@
|
||||
|
||||
set_blend_mode(blend);
|
||||
|
||||
- // Real per-vertex arrays, not glBegin/glVertexAttrib/glVertex3f
|
||||
- // immediate mode: glVertexAttrib*() is GLES2's *constant*-attribute
|
||||
- // API - without an enabled vertex array it sets one value for the
|
||||
- // whole draw call, not a fresh value per vertex. gl4es's immediate-mode
|
||||
- // emulation only reproduces desktop GL's per-vertex-capture behavior
|
||||
- // for attribute 0 (position, driven directly by glVertex3f), not for
|
||||
- // custom attributes like this shader's texcoords/light.
|
||||
- //
|
||||
// V is flipped (1 at the bottom, 0 at the top): GL texture v=0
|
||||
// addresses the first row of data passed to glTexImage2D, but uiRgba
|
||||
// (an SDL surface) stores its rows top-down.
|
||||
- struct QuadVertex {
|
||||
- float x, y, z;
|
||||
- float u, v;
|
||||
- float light;
|
||||
- };
|
||||
- static const QuadVertex quadVerts[4] = {
|
||||
- {1.0f, -1.0f, 0.0f, 1.0f, 1.0f, 1.0f},
|
||||
- {1.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f},
|
||||
- {-1.0f, -1.0f, 0.0f, 0.0f, 1.0f, 1.0f},
|
||||
- {-1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f},
|
||||
- };
|
||||
- glEnableVertexAttribArray(0);
|
||||
- glEnableVertexAttribArray(tcAttrib);
|
||||
- glEnableVertexAttribArray(lightAttrib);
|
||||
- glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(QuadVertex), &quadVerts[0].x);
|
||||
- glVertexAttribPointer(tcAttrib, 2, GL_FLOAT, GL_FALSE, sizeof(QuadVertex), &quadVerts[0].u);
|
||||
- glVertexAttribPointer(lightAttrib, 1, GL_FLOAT, GL_FALSE, sizeof(QuadVertex),
|
||||
- &quadVerts[0].light);
|
||||
- glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
|
||||
- // The engine's other rendering uses glBegin/glVertexAttrib/glVertex3f
|
||||
- // immediate mode for attribute 0, which depends on it not having an
|
||||
- // enabled array bound.
|
||||
- glDisableVertexAttribArray(0);
|
||||
- glDisableVertexAttribArray(tcAttrib);
|
||||
- glDisableVertexAttribArray(lightAttrib);
|
||||
+ android_imm_begin(tcAttrib, lightAttrib);
|
||||
+ android_imm_vertex(1.0f, -1.0f, 0.0f, 1.0f, 1.0f, 1.0f);
|
||||
+ android_imm_vertex(1.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f);
|
||||
+ android_imm_vertex(-1.0f, -1.0f, 0.0f, 0.0f, 1.0f, 1.0f);
|
||||
+ android_imm_vertex(-1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f);
|
||||
+ android_imm_end();
|
||||
// glFinish(), not glFlush(): xr_swapchain_release() (called by our
|
||||
// caller right after this returns) hands the image to the OpenXR
|
||||
// runtime for cross-process consumption, which requires the GPU write
|
||||
@@ -642,6 +754,16 @@
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
|
||||
+#ifdef __ANDROID__
|
||||
+ // Real per-vertex arrays, not glBegin/glVertexAttrib/glVertex3f
|
||||
+ // immediate mode - see android_imm_begin()'s comment above for why.
|
||||
+ android_imm_begin(tcAttrib, lightAttrib);
|
||||
+ android_imm_vertex(1.0f, -1.0f, 0.0f, 1.0f, 0.0f, 1.0f);
|
||||
+ android_imm_vertex(1.0f, 1.0f, 0.0f, 1.0f, 1.0f, 1.0f);
|
||||
+ android_imm_vertex(-1.0f, -1.0f, 0.0f, 0.0f, 0.0f, 1.0f);
|
||||
+ android_imm_vertex(-1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f);
|
||||
+ android_imm_end();
|
||||
+#else
|
||||
glBegin(GL_TRIANGLE_STRIP);
|
||||
glVertexAttrib2f(tcAttrib, 1.0f, 0.0f);
|
||||
glVertexAttrib1f(lightAttrib, 1.0f);
|
||||
@@ -656,6 +778,7 @@
|
||||
glVertexAttrib1f(lightAttrib, 1.0f);
|
||||
glVertex3f(-1.0f, 1.0f, 0.0f);
|
||||
glEnd();
|
||||
+#endif
|
||||
|
||||
// Finish drawing the 3d view
|
||||
glFlush();
|
||||
@@ -709,7 +832,10 @@
|
||||
render_width = width;
|
||||
render_height = height;
|
||||
|
||||
+#ifndef __ANDROID__
|
||||
+ // See opengl_resize()'s comment above.
|
||||
SDL_GL_MakeCurrent(window, context);
|
||||
+#endif
|
||||
|
||||
int lw, lh;
|
||||
SDL_RenderGetLogicalSize(renderer, &lw, &lh);
|
||||
@@ -729,7 +855,10 @@
|
||||
}
|
||||
|
||||
static bool opengl_cache_texture(CachedTexture toCache, grs_bitmap *bm) {
|
||||
+#ifndef __ANDROID__
|
||||
+ // See opengl_resize()'s comment above.
|
||||
SDL_GL_MakeCurrent(window, context);
|
||||
+#endif
|
||||
|
||||
// GL_UNPACK_ROW_LENGTH must describe the buffer actually passed to
|
||||
// glTexImage2D() below (toCache.converted->pixels, a separately
|
||||
@@ -771,7 +900,10 @@
|
||||
|
||||
DEBUG("Clearing OpenGL texture cache.");
|
||||
|
||||
+#ifndef __ANDROID__
|
||||
+ // See opengl_resize()'s comment above.
|
||||
SDL_GL_MakeCurrent(window, context);
|
||||
+#endif
|
||||
|
||||
for (auto iter = texturesByBitsPtr.begin(); iter != texturesByBitsPtr.end();) {
|
||||
// don't free locked surfaces
|
||||
@@ -896,10 +1028,16 @@
|
||||
light = 1.0f - (clut - grd_screen->ltab) / 4096.0f;
|
||||
}
|
||||
|
||||
+#ifdef __ANDROID__
|
||||
+ (void)tcAttrib; // captured by android_imm_begin() instead - see its comment above
|
||||
+ android_imm_vertex(vertex.x / 65536.0f, vertex.y / 65536.0f, -vertex.z / 65536.0f,
|
||||
+ vertex.uv.u / 256.0f, vertex.uv.v / 256.0f, light);
|
||||
+#else
|
||||
if (tcAttrib >= 0)
|
||||
glVertexAttrib2f(tcAttrib, vertex.uv.u / 256.0, vertex.uv.v / 256.0);
|
||||
glVertexAttrib1f(lightAttrib, light);
|
||||
glVertex3f(vertex.x / 65536.0f, vertex.y / 65536.0f, -vertex.z / 65536.0f);
|
||||
+#endif
|
||||
}
|
||||
|
||||
int opengl_draw_tmap(int n, g3s_phandle *vp, grs_bitmap *bm) { return opengl_light_tmap(n, vp, bm); }
|
||||
@@ -910,7 +1048,10 @@
|
||||
return CLIP_ALL;
|
||||
}
|
||||
|
||||
+#ifndef __ANDROID__
|
||||
+ // See opengl_resize()'s comment above.
|
||||
SDL_GL_MakeCurrent(window, context);
|
||||
+#endif
|
||||
set_blend_mode(bm->flags & BMF_TRANS);
|
||||
|
||||
glUseProgram(textureShaderProgram.shaderProgram);
|
||||
@@ -928,13 +1069,21 @@
|
||||
glUniformMatrix4fv(textureShaderProgram.uniView, 1, false, ViewMatrix);
|
||||
glUniformMatrix4fv(textureShaderProgram.uniProj, 1, false, ProjectionMatrix);
|
||||
|
||||
+#ifdef __ANDROID__
|
||||
+ android_imm_begin(tcAttrib, lightAttrib);
|
||||
+#else
|
||||
glBegin(GL_TRIANGLE_STRIP);
|
||||
+#endif
|
||||
draw_vertex(*(vp[1]), tcAttrib, lightAttrib);
|
||||
draw_vertex(*(vp[0]), tcAttrib, lightAttrib);
|
||||
draw_vertex(*(vp[2]), tcAttrib, lightAttrib);
|
||||
if (n > 3)
|
||||
draw_vertex(*(vp[3]), tcAttrib, lightAttrib);
|
||||
+#ifdef __ANDROID__
|
||||
+ android_imm_end();
|
||||
+#else
|
||||
glEnd();
|
||||
+#endif
|
||||
|
||||
return CLIP_NONE;
|
||||
}
|
||||
@@ -949,7 +1098,10 @@
|
||||
return CLIP_ALL;
|
||||
}
|
||||
|
||||
+#ifndef __ANDROID__
|
||||
+ // See opengl_resize()'s comment above.
|
||||
SDL_GL_MakeCurrent(window, context);
|
||||
+#endif
|
||||
set_blend_mode(bm->flags & BMF_TRANS);
|
||||
|
||||
glUseProgram(textureShaderProgram.shaderProgram);
|
||||
@@ -976,6 +1128,14 @@
|
||||
|
||||
glUniform1i(textureShaderProgram.uniMutant, (bm->type == BMT_TLUC8) || (bm->flags == BMF_TLUC8));
|
||||
|
||||
+#ifdef __ANDROID__
|
||||
+ android_imm_begin(tcAttrib, lightAttrib);
|
||||
+ android_imm_vertex(convx(vpl[1]->x), convy(vpl[1]->y), 0.0f, 1.0f, 0.0f, light);
|
||||
+ android_imm_vertex(convx(vpl[0]->x), convy(vpl[0]->y), 0.0f, 0.0f, 0.0f, light);
|
||||
+ android_imm_vertex(convx(vpl[2]->x), convy(vpl[2]->y), 0.0f, 1.0f, 1.0f, light);
|
||||
+ android_imm_vertex(convx(vpl[3]->x), convy(vpl[3]->y), 0.0f, 0.0f, 1.0f, light);
|
||||
+ android_imm_end();
|
||||
+#else
|
||||
glBegin(GL_TRIANGLE_STRIP);
|
||||
glVertexAttrib2f(tcAttrib, 1.0f, 0.0f);
|
||||
glVertexAttrib1f(lightAttrib, light);
|
||||
@@ -990,6 +1150,7 @@
|
||||
glVertexAttrib1f(lightAttrib, light);
|
||||
glVertex3f(convx(vpl[3]->x), convy(vpl[3]->y), 0.0f);
|
||||
glEnd();
|
||||
+#endif
|
||||
|
||||
glUniform1i(textureShaderProgram.uniMutant, false);
|
||||
|
||||
@@ -1007,7 +1168,10 @@
|
||||
return CLIP_ALL;
|
||||
}
|
||||
|
||||
+#ifndef __ANDROID__
|
||||
+ // See opengl_resize()'s comment above.
|
||||
SDL_GL_MakeCurrent(window, context);
|
||||
+#endif
|
||||
|
||||
glUseProgram(colorShaderProgram.shaderProgram);
|
||||
glUniformMatrix4fv(colorShaderProgram.uniView, 1, false, ViewMatrix);
|
||||
@@ -1064,7 +1228,11 @@
|
||||
GLint lightAttrib = colorShaderProgram.lightAttrib;
|
||||
|
||||
long a = 0, b = n_verts - 1;
|
||||
+#ifdef __ANDROID__
|
||||
+ android_imm_begin(-1, lightAttrib);
|
||||
+#else
|
||||
glBegin(GL_TRIANGLE_STRIP);
|
||||
+#endif
|
||||
while (true) {
|
||||
draw_vertex(*(p[a]), -1, lightAttrib);
|
||||
if (a++ == b)
|
||||
@@ -1073,7 +1241,11 @@
|
||||
if (b-- == a)
|
||||
break;
|
||||
}
|
||||
+#ifdef __ANDROID__
|
||||
+ android_imm_end();
|
||||
+#else
|
||||
glEnd();
|
||||
+#endif
|
||||
|
||||
return CLIP_NONE;
|
||||
}
|
||||
@@ -1085,7 +1257,10 @@
|
||||
}
|
||||
|
||||
void opengl_begin_stars() {
|
||||
+#ifndef __ANDROID__
|
||||
+ // See opengl_resize()'s comment above.
|
||||
SDL_GL_MakeCurrent(window, context);
|
||||
+#endif
|
||||
|
||||
// Bind the star shader before setting the point size: on Android,
|
||||
// questshock_glPointSize() (see the __ANDROID__ block above) needs
|
||||
@@ -1108,11 +1283,19 @@
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
|
||||
|
||||
+#ifdef __ANDROID__
|
||||
+ android_stars_begin(starShaderProgram.lightAttrib);
|
||||
+#else
|
||||
glBegin(GL_POINTS);
|
||||
+#endif
|
||||
}
|
||||
|
||||
void opengl_end_stars() {
|
||||
+#ifdef __ANDROID__
|
||||
+ android_stars_end();
|
||||
+#else
|
||||
glEnd();
|
||||
+#endif
|
||||
|
||||
// Turn off the stencil test and blending
|
||||
opengl_set_stencil(0x00);
|
||||
@@ -1120,9 +1303,11 @@
|
||||
}
|
||||
|
||||
int opengl_draw_star(fix star_x, fix star_y, int c, bool anti_alias) {
|
||||
+#ifndef __ANDROID__
|
||||
+ // See opengl_resize()'s comment above.
|
||||
SDL_GL_MakeCurrent(window, context);
|
||||
-
|
||||
GLint lightAttrib = starShaderProgram.lightAttrib;
|
||||
+#endif
|
||||
|
||||
float x = fix_float(star_x);
|
||||
float y = fix_float(star_y);
|
||||
@@ -1142,8 +1327,12 @@
|
||||
int color = (std_color_base + std_color_range - 1 - c);
|
||||
color = (255 * color) / (std_color_range + 1);
|
||||
|
||||
+#ifdef __ANDROID__
|
||||
+ android_stars_vertex(x, y, -0.25f, color / 255.0f);
|
||||
+#else
|
||||
glVertexAttrib1f(lightAttrib, color / 255.0f);
|
||||
glVertex3f(x, y, -0.25f);
|
||||
+#endif
|
||||
|
||||
return CLIP_NONE;
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
--- 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();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
--- a/src/MacSrc/OpenGL.cc
|
||||
+++ b/src/MacSrc/OpenGL.cc
|
||||
@@ -455,10 +455,30 @@
|
||||
#ifndef __ANDROID__
|
||||
// See opengl_resize()'s comment above.
|
||||
SDL_GL_MakeCurrent(window, context);
|
||||
-#endif
|
||||
|
||||
// Done rendering to the frame buffer, reset back to normal
|
||||
BindFrameBuffer(nullptr);
|
||||
+#else
|
||||
+ // BindFrameBuffer(nullptr) (glBindFramebuffer/glBindRenderbuffer to id
|
||||
+ // 0) means "the real window surface" - on desktop that's SDL's actual
|
||||
+ // GL window, but this app's real presentation target is never that
|
||||
+ // SDLSurface's own EGL surface at all - it's the OpenXR swapchain's
|
||||
+ // per-image FBOs, set up separately in xr_swapchain.c. Whatever
|
||||
+ // framebuffer/renderbuffer 0 actually resolves to for gl4es in this
|
||||
+ // configuration, binding it here reliably left gl4es reporting a
|
||||
+ // persistent GL_INVALID_OPERATION (1282) - confirmed by bisecting
|
||||
+ // through this whole render path with per-call checkpoints, all clean
|
||||
+ // right up until "after opengl_end_frame", the one immediately after
|
||||
+ // this exact call.
|
||||
+ //
|
||||
+ // The bind is also functionally dead on Android regardless of that:
|
||||
+ // by the time anything next draws, Shock.c's SDLDraw() has already
|
||||
+ // run xr_frame_begin() -> xr_swapchain_acquire(), which rebinds to
|
||||
+ // that frame's real swapchain FBO before opengl_swap_and_restore()
|
||||
+ // (the only thing that draws before the next opengl_start_frame()'s
|
||||
+ // own BindFrameBuffer(&backupBuffer)) runs - so skipping it changes
|
||||
+ // nothing about what actually gets rendered where.
|
||||
+#endif
|
||||
palette_dirty = false;
|
||||
}
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 382 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 413 KiB |
Reference in New Issue
Block a user