fast3d: clamp MSAA to GL_MAX_SAMPLES and bind framebuffer 0 as GL name 0

An MSAA level above the driver limit left the multisample renderbuffers
unallocated (black screen). Two glBindFramebuffer() calls also passed an
index into framebuffers[] instead of a GL name. Finding D323.
This commit is contained in:
italoarruda committed 2026-09-24 09:54:07 -04:00
1 parent 5ea053667f
commit fc092d99b6
3 files changed
+27 -2

No files matched your search

+1
View File
@@ -200,3 +200,4 @@ D319,FIXED,"Frame cap slider was mostly a dead zone; replaced with a plain 30/60
D320,OPEN,"D318-class sweep: aim-hold → _update AI lists in 8 levels carry the same as-authored softlock race (static analysis, 2026-09-20).","OPEN — static sweep complete (Facility ai_19, Control ai_9, Depot ai_12 flagged high/med-high; Bond-combat loops likely safe); no live confirmation beyond D318 itself; probe-verify path + generalized…" D320,OPEN,"D318-class sweep: aim-hold → _update AI lists in 8 levels carry the same as-authored softlock race (static analysis, 2026-09-20).","OPEN — static sweep complete (Facility ai_19, Control ai_9, Depot ai_12 flagged high/med-high; Bond-combat loops likely safe); no live confirmation beyond D318 itself; probe-verify path + generalized…"
D321,CLOSED,D318 trigger chain fully mapped:,CLOSED — trigger chain mapped and faithful; D318 watchdog validated in live play. D321,CLOSED,D318 trigger chain fully mapped:,CLOSED — trigger chain mapped and faithful; D318 watchdog validated in live play.
D322,OPEN,Long-session audio degradation:,"OPEN — static triage done (teardown audit, mixer statelessness, D202-coverage check); ranked hypotheses:" D322,OPEN,Long-session audio degradation:,"OPEN — static triage done (teardown audit, mixer statelessness, D202-coverage check); ranked hypotheses:"
D323,FIXED,OpenGL: MSAA above GL_MAX_SAMPLES gives a black screen; glBindFramebuffer is given a framebuffer index as a GL name.,FIXED (port/fast3d/gfx_opengl.cpp).
1 # GENERATED by tools_pc/gen_findings_index.py -- do not hand-edit.
200 D320
201 D321
202 D322
203 D323
+9
View File
@@ -608,6 +608,7 @@ covers D24–D69; the log continues in §H (D32 procedure, D70–D121).
| D320 | **D318-class sweep: aim-hold → `_update` AI lists in 8 levels carry the same as-authored softlock race (static analysis, 2026-09-20).** — full `## D320` entry at file tail | OPEN — static sweep complete (Facility ai_19, Control ai_9, Depot ai_12 flagged high/med-high; Bond-combat loops likely safe); no live confirmation beyond D318 itself; probe-verify path + generalized-watchdog recommendation documented. | | D320 | **D318-class sweep: aim-hold → `_update` AI lists in 8 levels carry the same as-authored softlock race (static analysis, 2026-09-20).** — full `## D320` entry at file tail | OPEN — static sweep complete (Facility ai_19, Control ai_9, Depot ai_12 flagged high/med-high; Bond-combat loops likely safe); no live confirmation beyond D318 itself; probe-verify path + generalized-watchdog recommendation documented. |
| D321 | **D318 trigger chain fully mapped: tanks → combat bit is an authored ~3.5 s gas-cascade delay (chr 254 script); derail lands same-tick; PC behavior confirmed faithful to N64 (probe captures, 2026-09-20).** — full `## D321` entry at file tail | CLOSED — trigger chain mapped and faithful; D318 watchdog validated in live play. Optional deferred tuning: `D318_DEADLOCK_TICKS` 600→300 (user's call). | | D321 | **D318 trigger chain fully mapped: tanks → combat bit is an authored ~3.5 s gas-cascade delay (chr 254 script); derail lands same-tick; PC behavior confirmed faithful to N64 (probe captures, 2026-09-20).** — full `## D321` entry at file tail | CLOSED — trigger chain mapped and faithful; D318 watchdog validated in live play. Optional deferred tuning: `D318_DEADLOCK_TICKS` 600→300 (user's call). |
| D322 | **Long-session audio degradation: full campaign on v0.3.0 — audio progressively worsens from Silo, by Caverns/Cradle the OST is inaudible and SFX "come and go"; restarting the game restores it (issue #87, user report, 2026-09-21).** — full `## D322` entry at file tail | OPEN — static triage done (teardown audit, mixer statelessness, D202-coverage check); ranked hypotheses: voice-pool exhaustion/counter drift > queue starvation > evtq saturation. `GE_D322` pool-telemetry probe shipped; needs a campaign capture with `GE_D322=1 GE_D204=1`. | | D322 | **Long-session audio degradation: full campaign on v0.3.0 — audio progressively worsens from Silo, by Caverns/Cradle the OST is inaudible and SFX "come and go"; restarting the game restores it (issue #87, user report, 2026-09-21).** — full `## D322` entry at file tail | OPEN — static triage done (teardown audit, mixer statelessness, D202-coverage check); ranked hypotheses: voice-pool exhaustion/counter drift > queue starvation > evtq saturation. `GE_D322` pool-telemetry probe shipped; needs a campaign capture with `GE_D322=1 GE_D204=1`. |
| D323 | **OpenGL: MSAA above `GL_MAX_SAMPLES` gives a black screen; `glBindFramebuffer` is given a framebuffer index as a GL name.** — full `## D323` entry at file tail | FIXED (`port/fast3d/gfx_opengl.cpp`). |
Phase 2 replaced the Phase-1 demo loop with the real `mainproc()` on real OS Phase 2 replaced the Phase-1 demo loop with the real `mainproc()` on real OS
threads, compiled GE's real `src/sched.c`, and brought in PD's fast3d software threads, compiled GE's real `src/sched.c`, and brought in PD's fast3d software
@@ -12239,3 +12240,11 @@ Full campaign (or at minimum Silo → Caverns/Cradle) with **`GE_D322=1 GE_D204=
- All pools healthy while audio is bad → back to the mixer/reverb state classes (re-open M-65's ruled-out list with a long-session `GE_AUDIODUMP`). - All pools healthy while audio is bad → back to the mixer/reverb state classes (re-open M-65's ruled-out list with a long-session `GE_AUDIODUMP`).
**Status:** OPEN — static triage complete, probe shipped, awaiting campaign capture. Cross-ref: D202/M-65+M-66b (ownerless-loop leak class + expiration), D207 (8-cap starvation design notes), D305 (pool/list desync observation), D204 (pipeline pacing + `GE_D204` monitor), D248/D250 (frame pacing, fixed), issue #87. **Status:** OPEN — static triage complete, probe shipped, awaiting campaign capture. Cross-ref: D202/M-65+M-66b (ownerless-loop leak class + expiration), D207 (8-cap starvation design notes), D305 (pool/list desync observation), D204 (pipeline pacing + `GE_D204` monitor), D248/D250 (frame pacing, fixed), issue #87.
## D323 — OpenGL MSAA clamp and framebuffer binding
A request above `GL_MAX_SAMPLES` (4 on Apple Silicon) leaves the multisample
renderbuffers unallocated (incomplete FBO, black screen); the level is now
clamped to the driver limit. `gfx_opengl_resolve_msaa_color_buffer()` and
`gfx_opengl_copy_framebuffer()` passed `current_framebuffer` (an index into
`framebuffers[]`) to `glBindFramebuffer()`; framebuffer 0 is GL name 0.
+17 -2
View File
@@ -1188,6 +1188,20 @@ static void gfx_opengl_update_framebuffer_parameters(int fb_id, uint32_t width,
width = max(width, 1U); width = max(width, 1U);
height = max(height, 1U); height = max(height, 1U);
/* macOS GL 4.1 core: asking for more samples than GL_MAX_SAMPLES (e.g. 8x)
* makes the multisample renderbuffers incomplete and the screen goes dark
* (glReadPixels 0x506). Clamp to what the driver reports. */
if (msaa_level > 1) {
static GLint maxSamples = -1;
if (maxSamples < 0) {
glGetIntegerv(GL_MAX_SAMPLES, &maxSamples);
if (maxSamples < 1) maxSamples = 1;
}
if ((GLint)msaa_level > maxSamples) {
msaa_level = (uint32_t)maxSamples;
}
}
if (gfx_framebuffers_enabled) { if (gfx_framebuffers_enabled) {
glBindFramebuffer(GL_FRAMEBUFFER, fb.fbo); glBindFramebuffer(GL_FRAMEBUFFER, fb.fbo);
@@ -1284,7 +1298,8 @@ void gfx_opengl_resolve_msaa_color_buffer(int fb_id_target, int fb_id_source) {
glBindFramebuffer(GL_READ_FRAMEBUFFER, fb_src.fbo); glBindFramebuffer(GL_READ_FRAMEBUFFER, fb_src.fbo);
glBlitFramebuffer(0, 0, fb_src.width, fb_src.height, 0, 0, fb_dst.width, fb_dst.height, GL_COLOR_BUFFER_BIT, glBlitFramebuffer(0, 0, fb_src.width, fb_src.height, 0, 0, fb_dst.width, fb_dst.height, GL_COLOR_BUFFER_BIT,
GL_NEAREST); GL_NEAREST);
glBindFramebuffer(GL_FRAMEBUFFER, current_framebuffer); /* current_framebuffer is an INDEX into framebuffers[], not a GL name; fb 0 is the window's default framebuffer (name 0). */
glBindFramebuffer(GL_FRAMEBUFFER, current_framebuffer == 0 ? 0 : framebuffers[current_framebuffer].fbo);
glEnable(GL_SCISSOR_TEST); glEnable(GL_SCISSOR_TEST);
} }
@@ -1348,7 +1363,7 @@ void gfx_opengl_copy_framebuffer(int fb_dst, int fb_src, int left, int top, bool
glBlitFramebuffer(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, GL_COLOR_BUFFER_BIT, GL_NEAREST); glBlitFramebuffer(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, GL_COLOR_BUFFER_BIT, GL_NEAREST);
glBindFramebuffer(GL_FRAMEBUFFER, framebuffers[current_framebuffer].fbo); glBindFramebuffer(GL_FRAMEBUFFER, current_framebuffer == 0 ? 0 : framebuffers[current_framebuffer].fbo);
glReadBuffer(GL_BACK); glReadBuffer(GL_BACK);