D320/D288: extend GE_D318T with generic pin detector; refine GE_D288 probe; cache bg.c hot-path getenvs

D320 (Phase 1 item 1): d318TimelineTick (GE_D318T=1) now also runs a
generic D318-class pin DETECTOR for the sweep's flagged lists (Facility
ai_19, Control ai_9, Depot ai_12): any chr in ACT_ATTACK with a live-fire
attacktype (no TARGET_AIM_ONLY bit) whose modelGetAnimFrame is byte-stable
for 600 ticks logs one 'D320T: PIN?' line with AI-list id/offset/atk/ent/
mot/unk54, repeating every further 600 stable ticks. Detection only -- the
recovery watchdog stays hardcoded to ai_22 by design (D320's explicit
instruction: generalize only after a second list actually freezes in play).
Legit states excluded/self-clearing: monologue pre-aims keep the AIM_ONLY
bit; working fire cycles re-init through chrlvTickAttackCommon so their
frame does not stay byte-stable for 10 s. Smoke: 90 s Facility run, zero
false positives, existing D318T timeline unaffected.

D288 (Phase 1 item 3): per M-155's revised next step, the GE_D288 probe
now skips any_behind_camera==1 triangles (the x/w divide is meaningless for
w<=0 -- that subset was pure false-lead noise) and logs the render-mode
word (oml) so a live capture can ID the culprit asset against level tables.
Re-ran bare -level_20: 1965 sane hits in the 550-755 window, still zero
hits for the target color RGB(83,62,44) -- consistent with M-155's working
theory that the bare boot's scripted camera never reaches the bug state;
headless probing is at its limit here, live capture is next.

bg.c getenv caching (D302-class sweep follow-up): GE_D104 (per room pass,
per frame), GE_D63 primary+secondary render logs (per room, per frame), and
the two GE_D154 inner-GDL-walk sites (per bullet hit) now use the cached
static pattern. All #ifdef PORT blocks; zero behavior change when unset.
This commit is contained in:
James Dansereau committed 2026-09-20 23:27:22 -04:00
1 parent 56ac2ab870
commit 8d3bd5732a
3 files changed
+164 -21

No files matched your search

+26 -16
View File
@@ -1761,23 +1761,32 @@ static void gfx_sp_tri1(uint8_t vtx1_idx, uint8_t vtx2_idx, uint8_t vtx3_idx, bo
return;
}
/* D288 diag (M-152, frame-gated M-155): the silo intro's
* screen-filling stray triangle (bounded to level_20 frames
* ~562-741) isn't explained by this function's existing D233/D106
* guards on static review -- log any triangle whose clip-space NDC
* bbox covers an unreasonable chunk of the screen, or whose bbox
* math goes non-finite, together with each vertex's w and
* any_behind_camera, to catch it at draw time. M-155: the first
* ungated run fired 116k times in ~1500-2000 frames -- bbox_frac
* >0.15 alone is a common occurrence for ordinary near-camera
* geometry, not a distinguishing signature. Gated to the reported
* frame window (num_dls, same pattern as GE_D236RM below) and the
/* D288 diag (M-152, frame-gated M-155, any_behind-restricted
* M-155-follow-up): the silo intro's screen-filling stray triangle
* (bounded to level_20 frames ~562-741) isn't explained by this
* function's existing D233/D106 guards on static review -- log any
* triangle whose clip-space NDC bbox covers an unreasonable chunk
* of the screen, together with each vertex's w and the render-mode
* word (oml), to catch it at draw time. M-155: the first ungated
* run fired 116k times in ~1500-2000 frames -- bbox_frac >0.15
* alone is a common occurrence for ordinary near-camera geometry,
* not a distinguishing signature. Gated to the reported frame
* window (num_dls, same pattern as GE_D236RM below) and the
* threshold raised well above the ~0.15 "normal" baseline M-155
* established, so a hit here is actually rare. Remove once D288 is
* root-caused. */
* established, so a hit here is actually rare. M-155 follow-up:
* for any_behind_camera==1 triangles the x/w perspective divide is
* meaningless (w near zero or negative -> arbitrarily large
* "bbox_frac" for perfectly ordinary geometry), which is exactly
* why the code above defers those to GL's own clipper -- so the
* probe now skips them entirely; a hit here has comfortably
* positive w's and is a genuine large-NDC-bbox triangle. oml is
* logged so the culprit's material/model can be identified against
* the level's asset tables (the flat RGB(83,62,44) color grep
* found nothing in the .c sources -- binary model blobs). Remove
* once D288 is root-caused. */
static int ge_d288 = -1;
if (ge_d288 < 0) ge_d288 = getenv("GE_D288") != NULL;
if (ge_d288) {
if (ge_d288 && !any_behind_camera) {
extern uint32_t num_dls;
if (num_dls >= 550 && num_dls <= 755) {
float minx = std::fmin(std::fmin(v1->x / v1->w, v2->x / v2->w), v3->x / v3->w);
@@ -1786,8 +1795,9 @@ static void gfx_sp_tri1(uint8_t vtx1_idx, uint8_t vtx2_idx, uint8_t vtx3_idx, bo
float maxy = std::fmax(std::fmax(v1->y / v1->w, v2->y / v2->w), v3->y / v3->w);
float bbox_frac = (maxx - minx) * (maxy - miny) / 4.0f; // NDC quad is [-1,1]^2
if (!std::isfinite(bbox_frac) || bbox_frac > 0.5f) {
fprintf(stderr, "D288: dl=%u tri w=(%.6f,%.6f,%.6f) bbox_frac=%.3f any_behind=%d color=(%d,%d,%d)\n",
num_dls, v1->w, v2->w, v3->w, bbox_frac, (int)any_behind_camera,
fprintf(stderr, "D288: dl=%u tri w=(%.6f,%.6f,%.6f) bbox_frac=%.3f oml=0x%08x color=(%d,%d,%d)\n",
num_dls, v1->w, v2->w, v3->w, bbox_frac,
(unsigned)rdp.other_mode_l,
v1->color.r, v1->color.g, v1->color.b);
}
}
+123
View File
@@ -93,6 +93,10 @@
/* chrai.c defines these but chrai.h does not declare them. */
extern s32 chraiGetAIListID(AIRecord *AIList, bool *isGlobalAIList);
/* src/bondaicommands.h:467 -- kept local so this port file doesn't pull in
* the AI-command header just for one bit constant. */
#define D320_TARGET_AIM_ONLY 0x0020 /* "Aim at target instead of firing" */
/* chraction.c internal (not in chraction.h): the fresh-attack entry point.
* Signature from the definition; call form mirrors chrlvTickAttack's own
* re-init at chraction.c:7401. */
@@ -434,6 +438,125 @@ void d318TimelineTick(void)
(unsigned)(u32)(g_randomSeed >> 32), (unsigned)(u32)g_randomSeed,
(unsigned)objectiveregisters1, (unsigned)h, (int)g_ActiveChrsCount);
}
/* D320: generic D318-class pin DETECTION for the sweep's flagged lists
* (Facility ai_19, Control ai_9, Depot ai_12 -- see findings D320).
* Detection only: it logs, it never intervenes; the recovery watchdog
* above stays hardcoded to ai_22 by design until a second list actually
* freezes in play (D320's explicit instruction).
*
* Signature: a chr in ACT_ATTACK whose attacktype is LIVE FIRE (no
* TARGET_AIM_ONLY bit -- it is supposed to be shooting) and whose
* modelGetAnimFrame is byte-stable for 600 consecutive ticks (10 s). A
* guard that should be firing but whose animation never advances is the
* exact D318 frozen shape on any list. The two legitimate long-stable
* states are excluded or self-clearing: monologue pre-aims keep the
* AIM_ONLY bit (excluded), and a working fire cycle re-inits through
* chrlvTickAttackCommon's "endframe <= frame" block, so its frame does
* not stay byte-stable for 10 s. One line at first detection, then one
* per further 600 stable ticks while it persists; a frozen run keeps
* printing, an escaped run stops after the single line (or none). */
{
typedef struct { s16 chrnum; u32 framebits; s32 stable; s32 nextlog; }
D320Pin;
static D320Pin s_pins[8];
static int s_npins = 0;
s32 i, j;
for (i = 0; i < g_ActiveChrsCount; i++)
{
ChrRecord *c = &g_ActiveChrs[i];
union { f32 f; u32 u; } fr;
D320Pin *p = NULL;
if (c->actiontype != ACT_ATTACK || c->model == NULL)
{
continue;
}
if ((s32)c->act_attack.attacktype & D320_TARGET_AIM_ONLY)
{
continue; /* legitimate hold pose (monologue pre-aim) */
}
fr.f = modelGetAnimFrame(c->model);
for (j = 0; j < s_npins; j++)
{
if (s_pins[j].chrnum == c->chrnum)
{
p = &s_pins[j];
break;
}
}
if (!p)
{
if (s_npins < 8)
{
p = &s_pins[s_npins++];
}
else
{
continue; /* table full: drop the newest, keep tracking */
}
p->chrnum = c->chrnum;
p->framebits = fr.u;
p->stable = 0;
p->nextlog = 600;
}
if (p->framebits == fr.u)
{
p->stable++;
}
else
{
p->framebits = fr.u;
p->stable = 0;
p->nextlog = 600;
}
if (p->stable >= p->nextlog)
{
bool g = FALSE;
s32 aid = c->ailist ? chraiGetAIListID(c->ailist, &g) : -1;
osSyncPrintf("D320T: t=%d PIN? c%d aiid=0x%04x%s off=%d atk=0x%x ent=%d "
"mot=%d unk54=%u frame=%.2f stable=%d ticks (D318-class pin candidate)\n",
(int)g_GlobalTimer, (int)c->chrnum,
(unsigned)aid, g ? "G" : "", (int)c->aioffset,
(unsigned)c->act_attack.attacktype,
(int)c->act_attack.entityid,
(s32)c->act_attack.type_of_motion,
(unsigned)c->act_attack.unk54,
(double)fr.f, p->stable);
p->nextlog = p->stable + 600;
}
}
/* Drop entries for chrs that left the tracked state so a later
* re-entry starts a fresh confirmation window. */
for (i = 0; i < s_npins; i++)
{
bool alive = FALSE;
for (j = 0; j < g_ActiveChrsCount; j++)
{
ChrRecord *c = &g_ActiveChrs[j];
if (c->chrnum == s_pins[i].chrnum && c->actiontype == ACT_ATTACK
&& c->model != NULL
&& !((s32)c->act_attack.attacktype & D320_TARGET_AIM_ONLY))
{
alive = TRUE;
break;
}
}
if (!alive)
{
s_pins[i] = s_pins[--s_npins];
i--;
}
}
}
}
#endif /* PORT */
+15 -5
View File
@@ -651,7 +651,9 @@ Gfx *sub_GAME_7F0B3C8C(Gfx *gdl)
/* TEMP D104: room render-pass geometry-visibility probe. */
{
static s32 d104c = 0;
if (getenv("GE_D104") && (d104c++ % 600) == 0 && d104c < 12000) {
static int d104on = -1; /* D302-class cache: this runs per room pass, per frame */
if (d104on < 0) d104on = getenv("GE_D104") != NULL;
if (d104on && (d104c++ % 600) == 0 && d104c < 12000) {
s32 rr;
osSyncPrintf("D104 tick=%d curRoom=%d roomsDrawn=%d budget=%d :",
d104c, (s32)g_CurrentPlayer->curRoomIndex,
@@ -2952,7 +2954,9 @@ Gfx *bgRenderRoomPrimary(Gfx *gdl, s32 room_index)
#if defined(PORT)
/* TEMP D63: log primary GDL at render time (env GE_D63=1) */
if (getenv("GE_D63")) {
static int d63on_p = -1; /* D302-class cache: per room, per frame */
if (d63on_p < 0) d63on_p = getenv("GE_D63") != NULL;
if (d63on_p) {
u32 *w = (u32 *)g_BgRoomInfo[room_index].ptr_expanded_mapping_info;
osSyncPrintf("D63 bgRenderRoomPrimary room=%d ptr=%p usize=%d w0..3=(%08x,%08x,%08x,%08x)\n",
room_index, (void *)g_BgRoomInfo[room_index].ptr_expanded_mapping_info,
@@ -2997,7 +3001,9 @@ Gfx *bgRenderRoomSecondary(Gfx *gdl, s32 room_index)
#if defined(PORT)
/* TEMP D63: log secondary GDL at render time (env GE_D63=1) */
if (getenv("GE_D63")) {
static int d63on_s = -1; /* D302-class cache: per room, per frame */
if (d63on_s < 0) d63on_s = getenv("GE_D63") != NULL;
if (d63on_s) {
u32 *w = (u32 *)g_BgRoomInfo[room_index].ptr_secondary_expanded_mapping_info;
osSyncPrintf("D63 bgRenderRoomSecondary room=%d ptr=%p usize=%d w0..3=(%08x,%08x,%08x,%08x)\n",
room_index, (void *)g_BgRoomInfo[room_index].ptr_secondary_expanded_mapping_info,
@@ -3509,7 +3515,9 @@ bool bgTestRayIntersectionInRoom(coord3d *from, coord3d *to, coord3d *dir, RoomV
idx[2] = (((u8 *) gdl)[7] / 10) - vtxoff;
#endif
#ifdef PORT
if (getenv("GE_D154"))
static int d154on = -1; /* D302-class cache: inner GDL walk, fires per bullet hit */
if (d154on < 0) d154on = getenv("GE_D154") != NULL;
if (d154on)
osSyncPrintf("D154 TRI1 w1=%08x idx=%d,%d,%d\n",
(unsigned)(u32)gdl->words.w1, (int)idx[0], (int)idx[1], (int)idx[2]);
#endif
@@ -3694,7 +3702,9 @@ bool bgTestRayIntersectionInRoom(coord3d *from, coord3d *to, coord3d *dir, RoomV
}
#endif
#ifdef PORT
if (getenv("GE_D154"))
static int d154on4 = -1; /* D302-class cache: inner GDL walk, fires per bullet hit */
if (d154on4 < 0) d154on4 = getenv("GE_D154") != NULL;
if (d154on4)
osSyncPrintf("D154 TRI4 s2=%d w0=%08x w1=%08x idx2=%d,%d,%d\n",
(int)s2, (unsigned)(u32)gdl->words.w0, (unsigned)(u32)gdl->words.w1,
(int)idx2[0], (int)idx2[1], (int)idx2[2]);