diff --git a/emulator/vpx-device/vpxlog.cpp b/emulator/vpx-device/vpxlog.cpp index 99403f4..12064dd 100644 --- a/emulator/vpx-device/vpxlog.cpp +++ b/emulator/vpx-device/vpxlog.cpp @@ -410,7 +410,6 @@ static void vpx_write(Bitu port, Bitu val, Bitu iolen) { * 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 @@ -650,34 +649,30 @@ 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); +} + +/* ---- VDB palette windows: one 640x480 window per display palette --------- + * Each shows a 16x16 grid of the 256 colors, updated live. */ +static void pal_draw(HDC dc, int g, int cw, int ch) { + glViewport(0, 0, cw, ch); + glClearColor(0.09f, 0.09f, 0.11f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT); + glDisable(GL_DEPTH_TEST); + glDisable(GL_TEXTURE_2D); + glDisable(GL_LIGHTING); + glMatrixMode(GL_PROJECTION); glLoadIdentity(); + glOrtho(0, 16, 16, 0, -1, 1); /* 16x16 grid, y-down */ + glMatrixMode(GL_MODELVIEW); glLoadIdentity(); + glBegin(GL_QUADS); + for (int i = 0; i < 256; i++) { + float x = (float)(i % 16), y = (float)(i / 16); + const unsigned char *e = &vdb_pal[g].ram[i * 3]; + glColor3ub(e[0], e[1], e[2]); + glVertex2f(x, y); glVertex2f(x + 1, y); + glVertex2f(x + 1, y + 1); glVertex2f(x, y + 1); } + glEnd(); SwapBuffers(dc); } @@ -686,6 +681,28 @@ static LRESULT CALLBACK rt_wndproc(HWND w, UINT msg, WPARAM wp, LPARAM lp) { return DefWindowProcA(w, msg, wp, lp); } +/* Create a visible window with its own GL context (each window gets its own + * so we can render several from one thread by wglMakeCurrent-ing each). */ +static bool make_gl_window(const char *title, int w, int h, int x, int y, + HWND *out_wnd, HDC *out_dc, HGLRC *out_gl) { + RECT r = { 0, 0, w, h }; + AdjustWindowRect(&r, WS_OVERLAPPEDWINDOW, FALSE); + HWND wnd = CreateWindowA("VPXGL", title, WS_OVERLAPPEDWINDOW | WS_VISIBLE, + x, y, r.right - r.left, r.bottom - r.top, NULL, NULL, + GetModuleHandleA(NULL), NULL); + if (!wnd) return false; + HDC dc = GetDC(wnd); + PIXELFORMATDESCRIPTOR pfd; memset(&pfd, 0, sizeof pfd); + pfd.nSize = sizeof pfd; pfd.nVersion = 1; + pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER; + pfd.iPixelType = PFD_TYPE_RGBA; pfd.cColorBits = 24; pfd.cDepthBits = 24; + SetPixelFormat(dc, ChoosePixelFormat(dc, &pfd), &pfd); + HGLRC gl = wglCreateContext(dc); + if (!gl) return false; + *out_wnd = wnd; *out_dc = dc; *out_gl = gl; + return true; +} + static DWORD WINAPI rt_main(LPVOID) { WNDCLASSA wc; memset(&wc, 0, sizeof wc); wc.style = CS_OWNDC; @@ -694,41 +711,50 @@ static DWORD WINAPI rt_main(LPVOID) { wc.hCursor = LoadCursor(NULL, IDC_ARROW); wc.lpszClassName = "VPXGL"; RegisterClassA(&wc); - RECT r = { 0, 0, 832, 512 }; - AdjustWindowRect(&r, WS_OVERLAPPEDWINDOW, FALSE); - HWND wnd = CreateWindowA("VPXGL", "VPX VelociRender (emulated)", - WS_OVERLAPPEDWINDOW | WS_VISIBLE, 40, 40, - r.right - r.left, r.bottom - r.top, NULL, NULL, wc.hInstance, NULL); - if (!wnd) return 1; - HDC dc = GetDC(wnd); - PIXELFORMATDESCRIPTOR pfd; memset(&pfd, 0, sizeof pfd); - pfd.nSize = sizeof pfd; pfd.nVersion = 1; - pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER; - pfd.iPixelType = PFD_TYPE_RGBA; pfd.cColorBits = 24; pfd.cDepthBits = 24; - int pf = ChoosePixelFormat(dc, &pfd); - SetPixelFormat(dc, pf, &pfd); - HGLRC gl = wglCreateContext(dc); - if (!gl) return 1; - wglMakeCurrent(dc, gl); + + HWND wnd; HDC dc; HGLRC gl; + if (!make_gl_window("VPX VelociRender (emulated)", 832, 512, 40, 40, + &wnd, &dc, &gl)) return 1; + + /* three 640x480 windows, one per VDB display palette */ + static const char *pal_titles[3] = { + "VDB palette 0 - secondary", "VDB palette 1 - aux1", + "VDB palette 2 - aux2" }; + HWND pwnd[3]; HDC pdc[3]; HGLRC pgl[3]; + bool phave[3] = { false, false, false }; + for (int g = 0; g < 3; g++) + phave[g] = make_gl_window(pal_titles[g], 640, 480, + 920, 40 + g * 500, &pwnd[g], &pdc[g], &pgl[g]); + VFrame cur; for (;;) { - DWORD w = MsgWaitForMultipleObjects(1, &rt_event, FALSE, INFINITE, + /* 50ms timeout so the palette windows animate even between VPX + * frames (pal0 is rewritten continuously by the game). */ + DWORD w = MsgWaitForMultipleObjects(1, &rt_event, FALSE, 50, QS_ALLINPUT); MSG msg; while (PeekMessageA(&msg, NULL, 0, 0, PM_REMOVE)) { TranslateMessage(&msg); DispatchMessageA(&msg); } - bool redraw = false; if (w == WAIT_OBJECT_0) { + bool redraw = false; EnterCriticalSection(&rt_lock); if (rt_new) { cur = rt_pending; rt_new = false; redraw = true; } LeaveCriticalSection(&rt_lock); + if (redraw && cur.valid) { + wglMakeCurrent(dc, gl); + RECT cr; GetClientRect(wnd, &cr); + rt_draw(dc, cur, cr.right, cr.bottom); + rt_frames++; + } } - if (redraw && cur.valid) { - RECT cr; GetClientRect(wnd, &cr); - rt_draw(dc, cur, cr.right, cr.bottom); - rt_frames++; + /* redraw the palette windows every tick (live) */ + for (int g = 0; g < 3; g++) { + if (!phave[g]) continue; + wglMakeCurrent(pdc[g], pgl[g]); + RECT cr; GetClientRect(pwnd[g], &cr); + pal_draw(pdc[g], g, cr.right, cr.bottom); } } } @@ -1237,9 +1263,6 @@ 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();