Compare commits
2 Commits
d6e438a541
...
e0a2e1f4bf
| Author | SHA1 | Date | |
|---|---|---|---|
| e0a2e1f4bf | |||
| 504ed3e1cb |
@@ -383,10 +383,14 @@ each one does:
|
|||||||
emulation) for real GLES-native equivalents: a custom `pointSize`
|
emulation) for real GLES-native equivalents: a custom `pointSize`
|
||||||
uniform, a shader-side alpha discard, and native point-sprite
|
uniform, a shader-side alpha discard, and native point-sprite
|
||||||
rasterization.
|
rasterization.
|
||||||
- **`05-android-audio-driver.patch`** - forces SDL2's older OpenSL ES
|
- **`05-android-audio-driver.patch`** - forces SDL2's OpenSL ES audio
|
||||||
audio backend instead of AAudio, because AAudio only allows one open
|
backend instead of AAudio via
|
||||||
playback device and the engine opens two (cutscene audio plus
|
`SDL_SetHintWithPriority(..., SDL_HINT_OVERRIDE)` (so nothing else can
|
||||||
`Mix_OpenAudio` for SFX/MIDI).
|
take precedence and silently leave AAudio selected), and logs the
|
||||||
|
actual `SDL_GetCurrentAudioDriver()` result right after `SDL_Init()`
|
||||||
|
as a standing on-device diagnostic. Does not by itself fix SFX/MIDI
|
||||||
|
playback - see `16-android-audio-single-device.patch` and README 5.5.3
|
||||||
|
for the actual cause and fix.
|
||||||
- **`06-android-resize-event.patch`** - makes the engine react to
|
- **`06-android-resize-event.patch`** - makes the engine react to
|
||||||
`SDL_WINDOWEVENT_RESIZED`, not just `SIZE_CHANGED`, since Android's SDL
|
`SDL_WINDOWEVENT_RESIZED`, not just `SIZE_CHANGED`, since Android's SDL
|
||||||
backend only ever sends the former for surface-driven resizes.
|
backend only ever sends the former for surface-driven resizes.
|
||||||
@@ -398,6 +402,10 @@ each one does:
|
|||||||
`SDL_SetWindowFullscreen`/`SetWindowSize`/`SetWindowPosition` calls on
|
`SDL_SetWindowFullscreen`/`SetWindowSize`/`SetWindowPosition` calls on
|
||||||
Android, since there's no real desktop-style window to resize and
|
Android, since there's no real desktop-style window to resize and
|
||||||
calling them desyncs SDL's cached window size from the real surface.
|
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
|
- **`10-android-openxr-cmake.patch`** - wires up the OpenXR loader plus
|
||||||
EGL as link/include dependencies for the CMake build.
|
EGL as link/include dependencies for the CMake build.
|
||||||
- **`11-android-openxr-present.patch`** - the core OpenXR present-path
|
- **`11-android-openxr-present.patch`** - the core OpenXR present-path
|
||||||
@@ -434,6 +442,72 @@ each one does:
|
|||||||
inherited non-default unpack state left behind elsewhere in the frame
|
inherited non-default unpack state left behind elsewhere in the frame
|
||||||
and misread every row. Fixed by resetting both to the packed defaults
|
and misread every row. Fixed by resetting both to the packed defaults
|
||||||
immediately before this upload.
|
immediately before this upload.
|
||||||
|
- **`15-android-audio-error-detail.patch`** - `snd_start_digital()`'s
|
||||||
|
`Mix_OpenAudio()` failure only logged a fixed string, not the actual
|
||||||
|
reason. Adds `SDL_GetError()` to the log line so the next on-device
|
||||||
|
failure reports the real underlying error.
|
||||||
|
- **`16-android-audio-single-device.patch`** - fixes SFX/MIDI music not
|
||||||
|
playing (see README 5.5.3). `snd_start_digital()` opened two SDL audio
|
||||||
|
devices: a raw `SDL_OpenAudioDevice()` for cutscene/audiolog audio and
|
||||||
|
a second one via `Mix_OpenAudio()` for SFX/MIDI - SDL's Android audio
|
||||||
|
backends only ever allow one open non-capture device, so the second
|
||||||
|
open always failed. Removes the raw device entirely and feeds cutscene/
|
||||||
|
audiolog PCM into the `Mix_OpenAudio()` device's output instead, via
|
||||||
|
`Mix_SetPostMix()` (mixing in rather than overwriting, so it doesn't
|
||||||
|
silence any SFX/MIDI music playing at the same time). `cutsloop.c`'s
|
||||||
|
and `audiolog.c`'s `SDL_PauseAudioDevice()` calls (no longer meaningful
|
||||||
|
without a dedicated device) are removed; the cutscene-audio-stream
|
||||||
|
pointer swaps they guarded are now protected by `SDL_LockAudio()`/
|
||||||
|
`SDL_UnlockAudio()` instead, since SDL_mixer's audio thread now reads
|
||||||
|
that pointer continuously via the post-mix hook.
|
||||||
|
- **`17-android-native-midi-unsupported.patch`** - `NativeMidiInit()` in
|
||||||
|
`MusicDevice.c` has real implementations only under `#ifdef WIN32` and
|
||||||
|
`#elif defined(USE_ALSA)`; on every other platform (Android included)
|
||||||
|
it fell through to `return 0` (success) without ever opening a device,
|
||||||
|
so a `midi-backend` preference of "Native" (see README 5.5.4) played
|
||||||
|
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/`:
|
`android/gl4es-patches/`:
|
||||||
|
|
||||||
@@ -517,10 +591,241 @@ left behind elsewhere in the frame and misreads every row - a diagonal
|
|||||||
shear/banding pattern. Menus never trigger it because nothing in the
|
shear/banding pattern. Menus never trigger it because nothing in the
|
||||||
menu-only code paths sets a custom unpack stride first.
|
menu-only code paths sets a custom unpack stride first.
|
||||||
|
|
||||||
**Fix** (`14-android-quad-unpack-state.patch`): explicitly reset both
|
#### 5.5.3. In-game SFX and MIDI music didn't play
|
||||||
`GL_UNPACK_ROW_LENGTH` and `GL_UNPACK_ALIGNMENT` to their tightly-packed
|
|
||||||
values immediately before this `glTexImage2D` call, removing the
|
Intro cutscene audio played correctly, but in-game SFX and MIDI/background
|
||||||
dependency on whatever GL state preceded it.
|
music never played, on-device.
|
||||||
|
|
||||||
|
**Root cause:** `snd_start_digital()` in `engine/src/MacSrc/SDLSound.c`
|
||||||
|
opened two separate SDL audio devices - a raw `SDL_OpenAudioDevice()`
|
||||||
|
call (used for cutscene/movie audio, always succeeded) and a second
|
||||||
|
device via `Mix_OpenAudio()` (used for SFX and, via `Mix_HookMusic`,
|
||||||
|
MIDI music - always failed, `SDL_GetError()` reporting "Audio device
|
||||||
|
already open"). SDL's Android audio backends (both AAudio and OpenSL ES)
|
||||||
|
only ever allow one open non-capture device - forcing OpenSL ES instead
|
||||||
|
of AAudio (`05-android-audio-driver.patch`) doesn't change this, since
|
||||||
|
the restriction isn't backend-specific.
|
||||||
|
|
||||||
|
**Fix (`16-android-audio-single-device.patch`):** removed the raw
|
||||||
|
`SDL_OpenAudioDevice()` call entirely. Cutscene/audiolog PCM (previously
|
||||||
|
delivered via that device's own callback) is now mixed into the single
|
||||||
|
`Mix_OpenAudio()` device's output via `Mix_SetPostMix()`, additively
|
||||||
|
(`SDL_MixAudioFormat`) rather than overwritten, so SFX/MIDI music
|
||||||
|
already mixed into the buffer isn't silenced whenever a cutscene or
|
||||||
|
audiolog plays. The stream-pointer swaps in `cutsloop.c`/`audiolog.c`
|
||||||
|
that used to be guarded by pausing the now-removed device are guarded by
|
||||||
|
`SDL_LockAudio()`/`SDL_UnlockAudio()` instead, since the post-mix hook
|
||||||
|
runs continuously on SDL_mixer's own audio thread.
|
||||||
|
|
||||||
|
#### 5.5.4. MIDI music silent even after audio otherwise worked
|
||||||
|
|
||||||
|
After fixing 5.5.3, SFX and cutscene audio played correctly, but MIDI
|
||||||
|
background music was still silent, with logcat showing `Creating native
|
||||||
|
MIDI device` instead of `Creating FluidSynth MIDI device`.
|
||||||
|
|
||||||
|
**Root cause:** the engine's `midi-backend` preference (`0` = ADLMIDI,
|
||||||
|
`1` = Native, `2` = FluidSynth) is persisted in a `prefs.txt` file in the
|
||||||
|
app's private storage, which Android retains across every APK
|
||||||
|
reinstall/upgrade (only a full uninstall clears it). A stale value of
|
||||||
|
`1` (Native) from earlier testing was carried forward through every
|
||||||
|
rebuild this whole session. `SetDefaultPrefs()` (`Prefs.c`) does default
|
||||||
|
to `2` (FluidSynth) whenever `USE_FLUIDSYNTH` is defined, which it
|
||||||
|
always has been for this build - the default was never wrong, only the
|
||||||
|
persisted override. `NativeMidiInit()` has no real implementation on
|
||||||
|
Android (see `17-android-native-midi-unsupported.patch`), so it silently
|
||||||
|
reported success without opening anything.
|
||||||
|
|
||||||
|
**Fix:** a full uninstall (not just installing a new build over the old
|
||||||
|
one) clears the stale preference, after which MIDI plays correctly.
|
||||||
|
`17-android-native-midi-unsupported.patch` additionally makes
|
||||||
|
`NativeMidiInit()` report failure on unsupported platforms, so a stale
|
||||||
|
"Native" preference falls back to `InitDecXMI()`'s existing dummy-driver
|
||||||
|
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
|
## 6. License
|
||||||
|
|
||||||
|
|||||||
@@ -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; }
|
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.environmentBlendMode = XR_ENVIRONMENT_BLEND_MODE_OPAQUE;
|
||||||
endInfo.layerCount = layerCount;
|
endInfo.layerCount = layerCount;
|
||||||
endInfo.layers = layerCount > 0 ? layers : NULL;
|
endInfo.layers = layerCount > 0 ? layers : NULL;
|
||||||
|
|
||||||
xr_check(xrEndFrame(g_session, &endInfo), "xrEndFrame");
|
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) {
|
void xr_get_game_resolution(int *width, int *height) {
|
||||||
*width = g_game_swapchain.width;
|
// Not g_game_swapchain.width/height: that's the fixed physical size of
|
||||||
*height = g_game_swapchain.height;
|
// 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.
|
// 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);
|
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/
|
// The engine's current screen resolution (gScreenWide/gScreenHigh,
|
||||||
// game_height as passed to xr_init() (grd_cap->w/h, see Shock.c's
|
// InitMac.h) - not the game quad's own swapchain size, which is fixed at
|
||||||
// InitSDL()) - the logical coordinate space xr_input.c needs to convert a
|
// launch and never changes - the logical coordinate space xr_input.c needs
|
||||||
// game-quad ray hit's normalized u,v into engine mouse coordinates (see
|
// to convert a game-quad ray hit's normalized u,v into engine mouse
|
||||||
// xr_mouse.h).
|
// 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);
|
void xr_get_game_resolution(int *width, int *height);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
|
|||||||
@@ -149,6 +149,7 @@ bool xr_swapchain_acquire(XrInstance instance, XrSwapchainState *state) {
|
|||||||
// (android_draw_surface_as_quad()) land in the right place.
|
// (android_draw_surface_as_quad()) land in the right place.
|
||||||
glBindFramebuffer(GL_FRAMEBUFFER, state->fbos[imageIndex]);
|
glBindFramebuffer(GL_FRAMEBUFFER, state->fbos[imageIndex]);
|
||||||
glViewport(0, 0, state->width, state->height);
|
glViewport(0, 0, state->width, state->height);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,8 +6,6 @@ import android.content.Context;
|
|||||||
import android.content.pm.PackageManager;
|
import android.content.pm.PackageManager;
|
||||||
import android.content.res.AssetManager;
|
import android.content.res.AssetManager;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.Handler;
|
|
||||||
import android.os.Looper;
|
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.view.SurfaceHolder;
|
import android.view.SurfaceHolder;
|
||||||
import androidx.core.app.ActivityCompat;
|
import androidx.core.app.ActivityCompat;
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
--- a/src/MacSrc/Shock.c
|
--- a/src/MacSrc/Shock.c
|
||||||
+++ b/src/MacSrc/Shock.c
|
+++ b/src/MacSrc/Shock.c
|
||||||
@@ -162,6 +162,17 @@
|
@@ -162,9 +162,26 @@
|
||||||
void InitSDL() {
|
void InitSDL() {
|
||||||
SDL_SetHint(SDL_HINT_NO_SIGNAL_HANDLERS, "1");
|
SDL_SetHint(SDL_HINT_NO_SIGNAL_HANDLERS, "1");
|
||||||
SDL_SetHint(SDL_HINT_RENDER_DRIVER, "opengl");
|
SDL_SetHint(SDL_HINT_RENDER_DRIVER, "opengl");
|
||||||
@@ -12,9 +12,18 @@
|
|||||||
+ // src/audio/aaudio/SDL_aaudio.c). SDLSound.c opens two: one directly via
|
+ // src/audio/aaudio/SDL_aaudio.c). SDLSound.c opens two: one directly via
|
||||||
+ // SDL_OpenAudioDevice() for cutscene audio, one via Mix_OpenAudio() for
|
+ // SDL_OpenAudioDevice() for cutscene audio, one via Mix_OpenAudio() for
|
||||||
+ // SFX/MIDI. The older OpenSL ES backend has no such limitation, so force
|
+ // SFX/MIDI. The older OpenSL ES backend has no such limitation, so force
|
||||||
+ // it instead of AAudio.
|
+ // it instead of AAudio. Uses SDL_HINT_OVERRIDE priority so nothing set
|
||||||
+ SDL_SetHint(SDL_HINT_AUDIODRIVER, "openslES");
|
+ // earlier (or by SDL/the platform) can take precedence over it.
|
||||||
|
+ SDL_SetHintWithPriority(SDL_HINT_AUDIODRIVER, "openslES", SDL_HINT_OVERRIDE);
|
||||||
+#endif
|
+#endif
|
||||||
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER | SDL_INIT_AUDIO) < 0) {
|
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER | SDL_INIT_AUDIO) < 0) {
|
||||||
DEBUG("%s: Init failed", __FUNCTION__);
|
DEBUG("%s: Init failed", __FUNCTION__);
|
||||||
}
|
}
|
||||||
|
+#ifdef __ANDROID__
|
||||||
|
+ // Standing diagnostic: reports which audio driver SDL actually selected,
|
||||||
|
+ // to confirm the openslES override above took effect on-device.
|
||||||
|
+ INFO("SDL audio driver: %s", SDL_GetCurrentAudioDriver());
|
||||||
|
+#endif
|
||||||
|
|
||||||
|
// TODO: figure out some universal set of settings that work...
|
||||||
|
#ifdef __ANDROID__
|
||||||
|
|||||||
@@ -1,6 +1,16 @@
|
|||||||
--- a/src/MacSrc/ShockBitmap.c
|
--- a/src/MacSrc/ShockBitmap.c
|
||||||
+++ b/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);
|
SDL_RenderClear(renderer);
|
||||||
|
|
||||||
@@ -27,3 +37,32 @@
|
|||||||
|
|
||||||
SDL_RenderSetLogicalSize(renderer, width, height);
|
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,11 @@
|
|||||||
|
--- a/src/MacSrc/SDLSound.c
|
||||||
|
+++ b/src/MacSrc/SDLSound.c
|
||||||
|
@@ -42,7 +42,7 @@
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Mix_OpenAudio(48000, AUDIO_S16SYS, 2, 2048) < 0) {
|
||||||
|
- ERROR("%s: Couldn't open audio device", __FUNCTION__);
|
||||||
|
+ ERROR("%s: Couldn't open audio device: %s", __FUNCTION__, SDL_GetError());
|
||||||
|
}
|
||||||
|
|
||||||
|
Mix_AllocateChannels(SND_MAX_SAMPLES);
|
||||||
@@ -0,0 +1,181 @@
|
|||||||
|
--- a/src/MacSrc/SDLSound.c
|
||||||
|
+++ b/src/MacSrc/SDLSound.c
|
||||||
|
@@ -19,24 +19,6 @@
|
||||||
|
|
||||||
|
// Startup the sound system
|
||||||
|
|
||||||
|
- SDL_AudioSpec spec, obtained;
|
||||||
|
- spec.freq = 48000;
|
||||||
|
- spec.format = AUDIO_S16SYS;
|
||||||
|
- spec.channels = 2;
|
||||||
|
- spec.samples = 2048;
|
||||||
|
- spec.callback = AudioStreamCallback;
|
||||||
|
- spec.userdata = (void *)&cutscene_audiostream;
|
||||||
|
-
|
||||||
|
- extern SDL_AudioDeviceID device;
|
||||||
|
- device = SDL_OpenAudioDevice(NULL, 0, &spec, &obtained, 0);
|
||||||
|
-
|
||||||
|
- if (device == 0) {
|
||||||
|
- ERROR("Could not open SDL audio: %s", SDL_GetError());
|
||||||
|
- } else {
|
||||||
|
- INFO("Opened Music Stream, deviceID %d, freq %d, size %d, format %d, channels %d, samples %d", device,
|
||||||
|
- obtained.freq, obtained.size, obtained.format, obtained.channels, obtained.samples);
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
if (Mix_Init(MIX_INIT_MP3) < 0) {
|
||||||
|
ERROR("%s: Init failed", __FUNCTION__);
|
||||||
|
}
|
||||||
|
@@ -50,10 +32,18 @@
|
||||||
|
Mix_HookMusic(MusicCallback, (void *)&MusicDev);
|
||||||
|
Mix_VolumeMusic(MIX_MAX_VOLUME); // use max volume for music stream
|
||||||
|
|
||||||
|
+ // Cutscene/audiolog playback (AudioStreamCallback, see cutsloop.c) used to
|
||||||
|
+ // run through its own SDL_OpenAudioDevice() device, separate from this
|
||||||
|
+ // one - SDL only ever allows one open non-capture device on Android's
|
||||||
|
+ // audio backends, so that second open always failed with "Audio device
|
||||||
|
+ // already open" and both SFX and MIDI music (both routed through this
|
||||||
|
+ // Mix_OpenAudio device) silently never played. Mixed into this device's
|
||||||
|
+ // post-mix stage instead, so there is only ever one device open.
|
||||||
|
+ Mix_SetPostMix(AudioStreamCallback, (void *)&cutscene_audiostream);
|
||||||
|
+
|
||||||
|
InitReadXMI();
|
||||||
|
|
||||||
|
atexit(Mix_CloseAudio);
|
||||||
|
- atexit(SDL_CloseAudio);
|
||||||
|
|
||||||
|
return OK;
|
||||||
|
}
|
||||||
|
--- a/src/MacSrc/Shock.c
|
||||||
|
+++ b/src/MacSrc/Shock.c
|
||||||
|
@@ -60,8 +60,6 @@
|
||||||
|
SDL_Palette *sdlPalette;
|
||||||
|
SDL_Renderer *renderer;
|
||||||
|
|
||||||
|
-SDL_AudioDeviceID device;
|
||||||
|
-
|
||||||
|
int num_args;
|
||||||
|
char **arg_values;
|
||||||
|
|
||||||
|
--- a/src/GameSrc/cutsloop.c
|
||||||
|
+++ b/src/GameSrc/cutsloop.c
|
||||||
|
@@ -83,15 +83,25 @@
|
||||||
|
extern char FrnSubtitle[256];
|
||||||
|
extern char GerSubtitle[256];
|
||||||
|
|
||||||
|
-extern SDL_AudioDeviceID device;
|
||||||
|
-
|
||||||
|
-
|
||||||
|
+// Runs as SDL_mixer's post-mix hook (see snd_start_digital() in
|
||||||
|
+// SDLSound.c) rather than a dedicated SDL audio device's callback, so
|
||||||
|
+// SFX/MIDI music sharing the same device are already mixed into `stream`
|
||||||
|
+// by the time this runs - mix in rather than overwrite, so they aren't
|
||||||
|
+// silenced whenever a cutscene or audiolog is playing.
|
||||||
|
void AudioStreamCallback(void *userdata, unsigned char *stream, int len)
|
||||||
|
{
|
||||||
|
+ static Uint8 mixbuf[MOVIE_DEFAULT_BLOCKLEN * 4];
|
||||||
|
SDL_AudioStream *as = *(SDL_AudioStream **)userdata;
|
||||||
|
|
||||||
|
- if (as != NULL && SDL_AudioStreamAvailable(as) > 0)
|
||||||
|
- SDL_AudioStreamGet(as, stream, len);
|
||||||
|
+ if (as == NULL || SDL_AudioStreamAvailable(as) <= 0)
|
||||||
|
+ return;
|
||||||
|
+
|
||||||
|
+ if (len > (int)sizeof(mixbuf))
|
||||||
|
+ len = (int)sizeof(mixbuf);
|
||||||
|
+
|
||||||
|
+ int got = SDL_AudioStreamGet(as, mixbuf, len);
|
||||||
|
+ if (got > 0)
|
||||||
|
+ SDL_MixAudioFormat(stream, mixbuf, AUDIO_S16SYS, got, SDL_MIX_MAXVOLUME);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@@ -153,11 +163,13 @@
|
||||||
|
|
||||||
|
if (cutscene_audiostream != NULL)
|
||||||
|
{
|
||||||
|
- SDL_PauseAudioDevice(device, 1);
|
||||||
|
- SDL_Delay(1);
|
||||||
|
-
|
||||||
|
+ // AudioStreamCallback (SDL_mixer's post-mix hook) runs continuously on
|
||||||
|
+ // SDL_mixer's own audio thread and reads this pointer - lock around
|
||||||
|
+ // freeing it so that thread can't be mid-read when it's freed.
|
||||||
|
+ SDL_LockAudio();
|
||||||
|
SDL_FreeAudioStream(cutscene_audiostream);
|
||||||
|
cutscene_audiostream = NULL;
|
||||||
|
+ SDL_UnlockAudio();
|
||||||
|
|
||||||
|
if (cutscene_audiobuffer)
|
||||||
|
{
|
||||||
|
@@ -184,8 +196,6 @@
|
||||||
|
|
||||||
|
if (cutscene_audiostream)
|
||||||
|
{
|
||||||
|
- SDL_PauseAudioDevice(device, 0);
|
||||||
|
-
|
||||||
|
if (cutscene_audiobuffer_size > 0)
|
||||||
|
{
|
||||||
|
// === adjust volume in buffer here ===
|
||||||
|
@@ -325,10 +335,9 @@
|
||||||
|
|
||||||
|
if (sfx_on)
|
||||||
|
{
|
||||||
|
- SDL_PauseAudioDevice(device, 1);
|
||||||
|
- SDL_Delay(1);
|
||||||
|
-
|
||||||
|
+ SDL_LockAudio();
|
||||||
|
cutscene_audiostream = SDL_NewAudioStream(AUDIO_U8, 1, fix_int(amovie->a.sampleRate), AUDIO_S16SYS, 2, 48000);
|
||||||
|
+ SDL_UnlockAudio();
|
||||||
|
|
||||||
|
cutscene_audiobuffer_pos = cutscene_audiobuffer;
|
||||||
|
}
|
||||||
|
--- a/src/GameSrc/audiolog.c
|
||||||
|
+++ b/src/GameSrc/audiolog.c
|
||||||
|
@@ -61,8 +61,6 @@
|
||||||
|
extern uchar curr_alog_vol;
|
||||||
|
extern char which_lang;
|
||||||
|
|
||||||
|
-extern SDL_AudioDeviceID device;
|
||||||
|
-
|
||||||
|
errtype audiolog_init(void) { return OK; }
|
||||||
|
|
||||||
|
errtype audiolog_play(int email_id) {
|
||||||
|
@@ -114,10 +112,9 @@
|
||||||
|
|
||||||
|
DEBUG("%s: Playing email", __FUNCTION__);
|
||||||
|
|
||||||
|
- SDL_PauseAudioDevice(device, 1);
|
||||||
|
- SDL_Delay(1);
|
||||||
|
-
|
||||||
|
+ SDL_LockAudio();
|
||||||
|
cutscene_audiostream = SDL_NewAudioStream(AUDIO_U8, 1, fix_int(palog->a.sampleRate), AUDIO_S16SYS, 2, 48000);
|
||||||
|
+ SDL_UnlockAudio();
|
||||||
|
|
||||||
|
audiolog_audiobuffer_pos = audiolog_audiobuffer;
|
||||||
|
|
||||||
|
@@ -150,11 +147,14 @@
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cutscene_audiostream != NULL) {
|
||||||
|
- SDL_PauseAudioDevice(device, 1);
|
||||||
|
- SDL_Delay(1);
|
||||||
|
-
|
||||||
|
+ // AudioStreamCallback (SDL_mixer's post-mix hook, see cutsloop.c)
|
||||||
|
+ // runs continuously on SDL_mixer's own audio thread and reads this
|
||||||
|
+ // pointer - lock around freeing it so that thread can't be
|
||||||
|
+ // mid-read when it's freed.
|
||||||
|
+ SDL_LockAudio();
|
||||||
|
SDL_FreeAudioStream(cutscene_audiostream);
|
||||||
|
cutscene_audiostream = NULL;
|
||||||
|
+ SDL_UnlockAudio();
|
||||||
|
|
||||||
|
if (audiolog_audiobuffer) {
|
||||||
|
free(audiolog_audiobuffer);
|
||||||
|
@@ -177,8 +177,6 @@
|
||||||
|
|
||||||
|
errtype audiolog_loop_callback(void) {
|
||||||
|
if (cutscene_audiostream) {
|
||||||
|
- SDL_PauseAudioDevice(device, 0);
|
||||||
|
-
|
||||||
|
if (audiolog_audiobuffer_size > 0) {
|
||||||
|
int i, vol = curr_alog_vol * 127 / 100; // convert from 0-100 to 0-127
|
||||||
|
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
--- a/src/MusicSrc/MusicDevice.c
|
||||||
|
+++ b/src/MusicSrc/MusicDevice.c
|
||||||
|
@@ -569,6 +569,14 @@
|
||||||
|
// connected
|
||||||
|
ndev->dev.isOpen = 1;
|
||||||
|
ndev->dev.outputIndex = outputIndex;
|
||||||
|
+#else
|
||||||
|
+ // No native MIDI backend implemented for this platform (e.g. Android) -
|
||||||
|
+ // report failure so InitDecXMI()'s caller falls back to another device
|
||||||
|
+ // instead of treating an unopened device as successfully initialized.
|
||||||
|
+ (void)outputIndex;
|
||||||
|
+ (void)samplerate;
|
||||||
|
+ WARN("NativeMidiInit(): native MIDI is not implemented on this platform");
|
||||||
|
+ return -1;
|
||||||
|
#endif
|
||||||
|
// suppress compiler warnings
|
||||||
|
(void)outputIndex;
|
||||||
@@ -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