diff --git a/emulator/vpx-device/vpxlog.cpp b/emulator/vpx-device/vpxlog.cpp index 897a316..99403f4 100644 --- a/emulator/vpx-device/vpxlog.cpp +++ b/emulator/vpx-device/vpxlog.cpp @@ -405,6 +405,13 @@ static void vpx_write(Bitu port, Bitu val, Bitu iolen) { } } +/* VDB display-palette storage (shared: written by the VDB I/O handler on the + * emulator thread, read by the GL thread for the live palette strips). + * Declared here so rt_draw() below can see it. */ +struct VDBPalette { unsigned char waddr, raddr, sub, mask; unsigned char ram[768]; }; +static VDBPalette vdb_pal[3]; +static bool vpx_show_vdb_strips = true; /* draw live palette strips (VPX_VDBSTRIP=0 off) */ + /* ================= Phase 3b: live render backend (VPX_RENDER=1) ========== * Reconstructs the DPL scene graph from the FIFO message stream (protocol * established in PHASE3-PROGRESS.md / render_capture.py) and draws each @@ -643,6 +650,34 @@ static void rt_draw(HDC dc, const VFrame &f, int cw, int ch) { glDisable(GL_TEXTURE_2D); glDisable(GL_LIGHTING); } + /* Live VDB palette strips: the three display color maps (secondary/aux1/ + * aux2) as 256-wide bars along the bottom, updating in real time as the + * game rewrites them (VPX_VDBSTRIP=0 to hide). Read straight from the VDB + * storage -- a torn read is invisible at this cadence. */ + if (vpx_show_vdb_strips) { + glDisable(GL_DEPTH_TEST); + glDisable(GL_TEXTURE_2D); + glDisable(GL_LIGHTING); + glMatrixMode(GL_PROJECTION); glLoadIdentity(); + glOrtho(0, cw, ch, 0, -1, 1); /* screen pixels, y-down */ + glMatrixMode(GL_MODELVIEW); glLoadIdentity(); + int barh = 16, gap = 2; + int total = 3 * barh + 2 * gap; + int y0 = ch - total - 4; + for (int g = 0; g < 3; g++) { + float y = (float)(y0 + g * (barh + gap)); + glBegin(GL_QUADS); + for (int i = 0; i < 256; i++) { + float x0 = (float)i / 256.0f * cw; + float x1 = (float)(i + 1) / 256.0f * cw; + const unsigned char *e = &vdb_pal[g].ram[i * 3]; + glColor3ub(e[0], e[1], e[2]); + glVertex2f(x0, y); glVertex2f(x1, y); + glVertex2f(x1, y + barh); glVertex2f(x0, y + barh); + } + glEnd(); + } + } SwapBuffers(dc); } @@ -1083,8 +1118,6 @@ static void vpx_render_start(void) {} * later; reads return 0xFF. Active when VPXLOG is set; logs to the same file. */ static const io_port_t VDB_BASE = 0x300; -struct VDBPalette { unsigned char waddr, raddr, sub, mask; unsigned char ram[768]; }; -static VDBPalette vdb_pal[3]; static bool vdb_splitter_on = false; /* lazy coalescing of palette data-byte writes so a 768-byte load is one line */ static int vdb_data_group = -1; @@ -1115,10 +1148,20 @@ static void vdb_flush_data(void) { fflush(vpx_fp); } if (vdb_paldump && vdb_data_count >= 768) { - char path[600]; - snprintf(path, sizeof path, "%s%d.rgb", vdb_paldump, vdb_data_group); - FILE *pf = fopen(path, "wb"); - if (pf) { fwrite(vdb_pal[vdb_data_group].ram, 1, 768, pf); fclose(pf); } + /* keep the MOST-LIT snapshot per group (max non-black bytes), so + * the real content is captured no matter when it lands or which + * phase an animated palette is sampled in. */ + static unsigned pal_max_nz[3] = { 0, 0, 0 }; + int g = vdb_data_group; + unsigned nz = 0; + for (int i = 0; i < 768; i++) if (vdb_pal[g].ram[i]) nz++; + if (nz >= pal_max_nz[g]) { + pal_max_nz[g] = nz; + char path[600]; + snprintf(path, sizeof path, "%s%d.rgb", vdb_paldump, g); + FILE *pf = fopen(path, "wb"); + if (pf) { fwrite(vdb_pal[g].ram, 1, 768, pf); fclose(pf); } + } } } vdb_data_group = -1; vdb_data_count = 0; @@ -1194,6 +1237,9 @@ void VPXLOG_Init(void) { if (fifo_dump_fp == NULL) LOG_MSG("VPXLOG: cannot open fifodump '%s'", fd); } + const char *vs = getenv("VPX_VDBSTRIP"); + if (vs && vs[0] == '0') vpx_show_vdb_strips = false; + const char *rn = getenv("VPX_RENDER"); if (rn && rn[0] && rn[0] != '0') { vpx_render_start();