gauges: all 6 cockpit MFD surfaces in a SEPARATE dev window (Milestone C)

Generalizes the single-'sec'-inset (Milestone B) into a full 6-surface
compositor presented in its own top-level window -- the default under
BT_DEV_GAUGES (BT_DEV_GAUGES_DOCK=1 docks it into the main window instead).
A 960x384 window tiles all six pod instrument screens: Heat (COOLANT/BALANCE/
RES + condenser gauges), Comm (KILLS/DEATHS/SELECT TARGET), Mfd1/2/3 (DISPLAY/
PROGRAM/NEAREST frames), and the color radar (SCALE grid + dials + ARMOR
DAMAGE). Main 800x600 3D view is un-occluded; default DEV un-regressed
(TARGET DESTROYED after 8 hits, 0 crashes).

Design (mapped by the mfd-multisurface-map workflow):
- All 6 surfaces are bit-plane MASKS over the ONE shared SVGA16/pixelBuffer
  (sec=palette low byte; Heat=0x4000 UL, Mfd2=0x0400 UC, Comm=0x8000 UR,
  Mfd1=0x0100 LL, Mfd3=0x1000 LR), so the compositor reaches the SVGA16 once
  and extracts each by mask. No port-name reconcile needed on the dev path --
  fetch the BT names directly; the RP aux* names only matter to the pod's own
  SVGA16::Update demux (a deferred pod-only fallback).
- SVGA16::DrawDevSurface: two kernels -- palette-LUT (sec) + mono bit-plane->
  tint ((word&mask)?tint:0). BTDrawGaugeSurfaces iterates a 6-entry table.
- Separate window = one CreateAdditionalSwapChain on the existing device (no
  2nd D3D device). BTGaugeWindowRenderAndPresent (after the main EndScene):
  SetRenderTarget -> SetDepthStencilSurface(NULL) -> Clear -> BeginScene ->
  6 tiles -> EndScene -> restore -> swap->Present.
- KEY BUG fixed: the main 800x600 depth surface stays bound when rendering to
  the 960x384 gauge backbuffer; a bound depth smaller than the RT is invalid
  -> all draws silently fail (clear color, no geometry). Unbind depth (Z is
  off), restore after.

Pod SVGA16::Update/BuildWindows path byte-unchanged; all gated BT_DEV_GAUGES.
Content is still the authored cockpit frames + base-table gauges -- the live
MFD widgets need the BTL4MethodDescription reconstruction (this window is now
the live viewer for that work). Details: docs/GAUGE_COMPOSITE.md.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
arcattack
2026-07-06 11:43:31 -05:00
co-authored by Claude Opus 4.8
parent a256813395
commit b526e92cad
5 changed files with 333 additions and 49 deletions
+21
View File
@@ -675,6 +675,27 @@ method table (BT gauge classes: `PlayerStatus` + ~16 recovered `methodDescriptio
game-state DATA BINDINGS (why some gauges resolve NULL) — that is what the dev skip/guards stand in for, and
what the POD needs. Then Step 2 (RP↔BT MFD port-name reconcile → the 5 MFDs) + Step 3 (2-window layout).
**✅✅ GAUGE MILESTONE C — ALL SIX instrument surfaces in a SEPARATE dev window (default under
`BT_DEV_GAUGES`; `BT_DEV_GAUGES_DOCK=1` = docked-in-main-window instead; full map: `docs/GAUGE_COMPOSITE.md`).**
Steps 2+3 done for the dev path. A 960×384 top-level window tiles all 6 pod screens with authentic content:
**Heat** (COOLANT/BALANCE/RES + condenser/temp-leak gauges), **Comm** (KILLS/DEATHS/SELECT TARGET), **Mfd1/2/3**
(DISPLAY/PROGRAM/NEAREST/TEMP-STATUS frames), and the color **radar** (SCALE grid + dials + ARMOR DAMAGE). Main
800×600 3D view is un-occluded; default DEV un-regressed (TARGET DESTROYED, 0 crashes). **Architecture (from the
`mfd-multisurface-map` workflow):** all 6 surfaces are bit-plane MASKS over the ONE shared `SVGA16`/`pixelBuffer`
(`sec`=palette low byte; `Heat`=0x4000 UL, `Mfd2`=0x0400 UC, `Comm`=0x8000 UR, `Mfd1`=0x0100 LL, `Mfd3`=0x1000 LR;
`Eng1-3` = engineering-mode alt planes, not extra monitors), so the compositor reaches the SVGA16 ONCE and
extracts each by mask. **No port-name reconcile needed on the dev path** — we fetch the BT port names directly via
`GetGraphicsPort` (the RP `auxUL2/…` names only matter to the POD's own `SVGA16::Update` demux — a deferred
pod-only fallback). `SVGA16::DrawDevSurface` has two kernels: palette-LUT (sec, == Update case 0) + mono bit-plane→
tint (`(word&mask)?tint:0`, the reduced core of Update cases 1/2); `BTDrawGaugeSurfaces` iterates a 6-entry table.
The **separate window = one `CreateAdditionalSwapChain` on the EXISTING device** (no 2nd D3D device); rendered
AFTER the main `EndScene` (`BTGaugeWindowRenderAndPresent`, L4VIDEO): SetRenderTarget→**SetDepthStencilSurface(NULL)**
→Clear→BeginScene→6 tiles→EndScene→restore→`swap->Present`. **⚠ KEY BUG (cost a cycle): depth-stencil size
mismatch** — the main 800×600 depth stays bound when you SetRenderTarget to the 960×384 gauge backbuffer; a bound
depth SMALLER than the RT is INVALID → all draws silently fail (window shows the Clear color, no geometry). Fix:
unbind depth (Z is off anyway), restore after. Pod `BuildWindows`/`Update` path byte-unchanged. REMAINING = the
widget recon (above) + the pod MFD port-name fallback.
**Alternate route considered:** run original `btrel410.exe` under DOSBox — **blocked**: it needs the
VPX/IG board DOSBox can't emulate; bypassing it = the shim work anyway.
+47 -6
View File
@@ -172,10 +172,51 @@ accommodations (skip/guard) stand in for. Files touched: `L4VB16.{h,cpp}` (Draw
`L4VIDEO.cpp` (the EndScene hook), `btl4app.{cpp,hpp}` (the override fix), `GAUGREND.cpp` (tolerant skip),
`gauge.h` + `GAUGE.{h,cpp}` (NULL-source + SEH guards).
## Follow-ups (after Milestone B)
## Milestone C DONE (2026-07) — ALL SIX instrument surfaces in a SEPARATE dev window
- **Widget recon (the real fix):** reconstruct `BTL4MethodDescription` (BT gauge classes) + the gauge→mech
data bindings so the skipped/guarded gauges show real data and the POD path (strict `Fail`, no guards) works.
- **Step 2** — reconcile the MFD port names (`auxUL2/auxC/...` in `SVGA16::Update` vs `Heat/Comm/Mfd1-3` in
`L4GAUGE.CFG`) → the 5 MFD surfaces render (only `sec`/radar works today).
- **Step 3** — the 2-window layout (3D-view window + an instrument-panel window tiling radar + the 5 MFDs).
The beachhead's single-`sec`-inset is now a full **6-surface compositor in its own top-level window** (the
default under `BT_DEV_GAUGES`; `BT_DEV_GAUGES_DOCK=1` docks the panel into the main window instead). The
960×384 gauge window tiles all six pod instrument screens with authentic content: **Heat** (COOLANT/BALANCE/
RES + condenser & temp-leak gauges), **Comm** (KILLS/DEATHS/SELECT TARGET scoreboard), **Mfd1/Mfd2/Mfd3**
(DISPLAY/PROGRAM/NEAREST/TEMP-STATUS frames), and the color **radar** (SCALE grid + speed/heading dials +
ARMOR DAMAGE schematic). Verified live: gauge window renders all 6; main 800×600 3D view is un-occluded;
default DEV (no gauges) un-regressed (TARGET DESTROYED after 8 hits, 0 crashes).
**Architecture (mapped by the `mfd-multisurface-map` workflow, then implemented):**
- **One buffer, six bit-plane views.** All gauge ports share ONE `SVGA16`/`pixelBuffer`; a "surface" is a
MASK over that one 640×480×16 buffer. The compositor reaches the `SVGA16` once (via any port) and extracts
each surface by its own plane mask. BT plane map (`L4GAUGE.CFG` MechInit @4395): `sec`=radar (palette,
low byte) · `Heat`=UL (0x4000) · `Mfd2`=UC (0x0400) · `Comm`=UR (0x8000) · `Mfd1`=LL (0x0100) ·
`Mfd3`=LR (0x1000). `Eng1/2/3` are the engineering-mode alt planes of UC/LL/LR, not extra monitors.
- **No port-name reconcile needed on the dev path.** The RP names `auxUL2/auxC/…` hardcoded in
`SVGA16::Update` only matter to the POD demux (a deferred pod-only follow-up); our compositor fetches the
BT names (`Heat/Comm/Mfd1/Mfd2/Mfd3`) directly via `GetGraphicsPort` (`stricmp`).
- **Two extract kernels** in `SVGA16::DrawDevSurface`: palette-LUT (sec/radar, == `Update` case 0) and mono
bit-plane → tint (`(word & mask) ? tint : 0`, the reduced core of `Update` cases 1/2; MFD masks are single
bits so no DWORD SIMD/pack). `BTDrawGaugeSurfaces` iterates a `{name,tint,cellRect}` table over all 6.
- **Separate window = one ADDITIONAL SWAP CHAIN on the existing device** (no 2nd D3D device — textures/verts
are shared). `BTGaugeWindowRenderAndPresent` (called AFTER the main `EndScene`, before the main Present):
`GetBackBuffer``SetRenderTarget`**`SetDepthStencilSurface(NULL)`** → Clear → BeginScene → 6 tiles →
EndScene → restore RT+DS → `swap->Present`. The pod's own per-surface `BuildWindows`/`Update` path is
byte-unchanged (dev mode already NULLs those objects).
- **⚠ THE KEY BUG (cost a cycle): depth-stencil size mismatch.** The main 800×600 depth surface stays bound
when you `SetRenderTarget` to the 960×384 gauge backbuffer; a bound depth surface SMALLER than the render
target is INVALID → every draw silently fails (window showed the Clear color but no tiles). Fix:
`SetDepthStencilSurface(NULL)` before the gauge draws (Z is disabled anyway), restore after. Symptom to
remember: render-to-a-second-swap-chain shows the clear color but no geometry ⇒ check the bound depth size.
**Content reality (unchanged from Milestone B):** the surfaces show the authored cockpit FRAME art + a few
base-table gauges (heat numerics, radar) — the live animated MFD widgets are BT-specific gauge classes not
yet reconstructed (parse-skipped). The separate window is now the **live viewer** for that widget-recon work.
## Follow-ups (after Milestone C)
- **Widget recon (the real fix, the big remaining workstream):** populate `BTL4MethodDescription` (give each
BT gauge class a `methodDescription`) + wire the gauge→game-state `Execute()` data bindings, so the
skipped/guarded widgets (`map`/`GeneratorCluster`/`vehicleSubSystems`/`pilotList`/`cmArmor`/heat clusters)
render live data. The Milestone-C window shows them coming online as they're reconstructed.
- **Pod MFD port-name reconcile (pod-only):** a ~5-line positional dual-name fallback in `SVGA16::Update`
(`UL = GetGraphicsPort("auxUL2"); if(!UL) UL = GetGraphicsPort("Heat"); …`) so BT MFDs reach the pod's real
monitors. NOT a CFG rename (the CFG uses BT names pervasively + `MUNGA_L4` is shared with RP's aux names).
- **Polish:** overlay-plane compositing over `sec`; a pod-accurate RGB-packing toggle; texture atlas
(one lock/upload); device-reset recreate hardening for the additional swap chain.
+248 -36
View File
@@ -109,6 +109,15 @@ static bool DevGaugeComposite()
return v != 0;
}
// DEV-COMPOSITE: BT_DEV_GAUGES_DOCK=1 docks the 6-surface panel INTO the main 800x600
// window (occludes the 3D view); default (unset) presents them in a SEPARATE window.
static bool DevGaugeDocked()
{
static int v = -1;
if (v < 0) v = (getenv("BT_DEV_GAUGES_DOCK") != NULL) ? 1 : 0;
return v != 0;
}
//===========================================================================//
// DEV-COMPOSITE (option B) -- draw the woken gauge renderer's SECONDARY/radar
// surface as an inset in the MAIN window. The gauge widgets have already
@@ -117,50 +126,70 @@ static bool DevGaugeComposite()
// a quad. Called by the main renderer as the LAST draw before EndScene, so no
// render-state save/restore is needed (the next frame resets device state).
//===========================================================================//
void SVGA16::DrawDevInset(LPDIRECT3DDEVICE9 device, int secMask, int paletteID)
void SVGA16::DrawDevSurface(LPDIRECT3DDEVICE9 device, int slot, int mask, int paletteID,
int monoTint, float dstX, float dstY, float dstW, float dstH)
{
int w = pixelBuffer.Data.Size.x; // 640
int h = pixelBuffer.Data.Size.y; // 480
if (device == NULL || w <= 0 || h <= 0) return;
if (device == NULL || slot < 0 || slot >= 8 || w <= 0 || h <= 0) return;
// Lazily create the inset texture on the MAIN device (MANAGED -> lockable +
// Lazily create this surface's texture on the MAIN device (MANAGED -> lockable +
// survives device reset).
if (mDevInsetTex == NULL)
device->CreateTexture(w, h, 1, 0, D3DFMT_R5G6B5, D3DPOOL_MANAGED, &mDevInsetTex, NULL);
if (mDevInsetTex == NULL) return;
if (mDevSurfaceTex[slot] == NULL)
device->CreateTexture(w, h, 1, 0, D3DFMT_R5G6B5, D3DPOOL_MANAGED, &mDevSurfaceTex[slot], NULL);
LPDIRECT3DTEXTURE9 tex = mDevSurfaceTex[slot];
if (tex == NULL) return;
// Palette-expand the secondary plane into the texture (== SVGA16::Update case 0).
SVGA16Palette *pal = &palette[paletteID];
// Extract this surface's bit-plane from the ONE shared pixelBuffer into the texture.
D3DLOCKED_RECT rect;
if (SUCCEEDED(mDevInsetTex->LockRect(0, &rect, NULL, 0)))
if (SUCCEEDED(tex->LockRect(0, &rect, NULL, 0)))
{
Word *source = pixelBuffer.Data.MapPointer;
Word *dest = (Word*)rect.pBits;
int postRowIncrement = (rect.Pitch / 2) - w;
for (int y = 0; y < h; y++)
if (monoTint < 0)
{
for (int x = 0; x < w; x++)
// PALETTE surface (sec/radar) -- palette-LUT expand (== SVGA16::Update case 0).
SVGA16Palette *pal = &palette[paletteID];
for (int y = 0; y < h; y++)
{
PaletteTriplet *pe = &(pal->paletteData.Color[*source & secMask]);
*dest = ((pe->Red >> 3) << 11) | ((pe->Green >> 2) << 5) | (pe->Blue >> 3);
dest++; source++;
for (int x = 0; x < w; x++)
{
PaletteTriplet *pe = &(pal->paletteData.Color[*source & mask]);
*dest = ((pe->Red >> 3) << 11) | ((pe->Green >> 2) << 5) | (pe->Blue >> 3);
dest++; source++;
}
dest += postRowIncrement;
}
dest += postRowIncrement;
}
mDevInsetTex->UnlockRect(0);
else
{
// MONO MFD surface -- single-bit-plane expand to a tint (the reduced core of
// SVGA16::Update cases 1/2; MFD masks are single bits, so no DWORD SIMD/pack).
Word tint = (Word) monoTint;
for (int y = 0; y < h; y++)
{
for (int x = 0; x < w; x++)
{
*dest = (*source & mask) ? tint : 0;
dest++; source++;
}
dest += postRowIncrement;
}
}
tex->UnlockRect(0);
}
// Draw the inset: a 320x240 panel in the bottom-left of the (800x600) backbuffer.
const float iw = 320.0f, ih = 240.0f, ix = 8.0f, iy = 600.0f - ih - 8.0f;
// Draw the surface quad at (dstX,dstY,dstW,dstH) in the current render target.
struct InsetVert { float x, y, z, rhw, u, v; };
InsetVert quad[4] =
{
{ ix, iy, 0.0f, 1.0f, 0.0f, 0.0f },
{ ix + iw, iy, 0.0f, 1.0f, 1.0f, 0.0f },
{ ix + iw, iy + ih, 0.0f, 1.0f, 1.0f, 1.0f },
{ ix, iy + ih, 0.0f, 1.0f, 0.0f, 1.0f },
{ dstX, dstY, 0.0f, 1.0f, 0.0f, 0.0f },
{ dstX + dstW, dstY, 0.0f, 1.0f, 1.0f, 0.0f },
{ dstX + dstW, dstY + dstH, 0.0f, 1.0f, 1.0f, 1.0f },
{ dstX, dstY + dstH, 0.0f, 1.0f, 0.0f, 1.0f },
};
device->SetTexture(0, mDevInsetTex);
device->SetTexture(0, tex);
device->SetFVF(D3DFVF_XYZRHW | D3DFVF_TEX1);
device->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_SELECTARG1);
device->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
@@ -173,23 +202,205 @@ void SVGA16::DrawDevInset(LPDIRECT3DDEVICE9 device, int secMask, int paletteID)
device->DrawPrimitiveUP(D3DPT_TRIANGLEFAN, 2, quad, sizeof(InsetVert));
}
//
// The pod's SIX instrument surfaces, all bit-plane views of the ONE shared 640x480
// gauge buffer (masks/positions from content/GAUGE/L4GAUGE.CFG MechInit @4395):
// sec = radar/secondary (palette) Heat = UL Mfd2 = UC Comm = UR
// Mfd1 = LL Mfd3 = LR
// monoTint < 0 -> palette-expand; else the mono tint for that MFD plane. The MFD
// content today is mostly the authored cockpit frame art (btquad/btcomm/btheat) --
// the live widgets are BT-specific gauge classes not yet reconstructed (parse-skipped).
//
struct BTGaugeSurfaceDesc
{
const char *portName;
int monoTint; // -1 = palette (sec); else R5G6B5 tint
float cellX, cellY; // normalized cell origin within the panel (y down)
float cellW, cellH; // normalized cell size
};
static const BTGaugeSurfaceDesc kBTGaugeSurfaces[6] =
{
// top row: Heat (UL) | Mfd2 (UC) | Comm (UR)
{ "Heat", 0xFFFF, 0.0f / 3, 0.0f, 1.0f / 3, 0.5f },
{ "Mfd2", 0xFFFF, 1.0f / 3, 0.0f, 1.0f / 3, 0.5f },
{ "Comm", 0xFFFF, 2.0f / 3, 0.0f, 1.0f / 3, 0.5f },
// bottom row: Mfd1 (LL) | sec/radar (center) | Mfd3 (LR)
{ "Mfd1", 0xFFFF, 0.0f / 3, 0.5f, 1.0f / 3, 0.5f },
{ "sec", -1, 1.0f / 3, 0.5f, 1.0f / 3, 0.5f },
{ "Mfd3", 0xFFFF, 2.0f / 3, 0.5f, 1.0f / 3, 0.5f },
};
//
// Composite ALL SIX gauge surfaces into the CURRENT render target, tiled within the
// panel rect (px,py,pw,ph). Reaches the ONE shared SVGA16 via any resolvable port and
// extracts each surface by its own bit-plane mask. Ports that don't resolve are skipped.
//
void BTDrawGaugeSurfaces(LPDIRECT3DDEVICE9 device, float px, float py, float pw, float ph)
{
if (device == NULL) return;
GaugeRenderer *gr = application ? application->GetGaugeRenderer() : NULL;
if (gr == NULL) return;
// Reach the shared display once (any present port shares the same SVGA16/pixelBuffer).
SVGA16 *svga = NULL;
for (int i = 0; i < 6 && svga == NULL; i++)
{
L4GraphicsPort *p = static_cast<L4GraphicsPort*>(gr->GetGraphicsPort(kBTGaugeSurfaces[i].portName));
if (p != NULL) svga = (SVGA16*) p->graphicsDisplay;
}
if (svga == NULL) return;
for (int i = 0; i < 6; i++)
{
const BTGaugeSurfaceDesc &d = kBTGaugeSurfaces[i];
L4GraphicsPort *port = static_cast<L4GraphicsPort*>(gr->GetGraphicsPort(d.portName));
if (port == NULL) continue; // this surface's port isn't configured -> skip
svga->DrawDevSurface(
device, i, port->GetBitMask(), port->paletteID, d.monoTint,
px + d.cellX * pw, py + d.cellY * ph, d.cellW * pw, d.cellH * ph);
}
}
//
// Free entry point the MAIN renderer calls (L4VIDEO DPLRenderer::ExecuteImplementation,
// just before EndScene). No-op unless BT_DEV_GAUGES is set + the gauge renderer + its
// 'sec' port exist. Reaches the gauge renderer's SVGA16 and blits the secondary inset.
// just before EndScene). No-op unless BT_DEV_GAUGES is set. Docks the 6-surface panel
// in the bottom-left of the 800x600 main window (a second dedicated window is the goal;
// see BTGaugeWindow* below).
//
void BTDrawGaugeInset(LPDIRECT3DDEVICE9 device)
{
if (!DevGaugeComposite() || device == NULL) return;
GaugeRenderer *gr = application ? application->GetGaugeRenderer() : NULL;
if (gr == NULL) return;
// The secondary/radar surface ('sec') is the one port that resolves without
// the RP<->BT MFD port-name reconciliation (Step 2), so it is the beachhead.
L4GraphicsPort *secPort = static_cast<L4GraphicsPort*>(gr->GetGraphicsPort("sec"));
if (secPort == NULL) return;
SVGA16 *svga = (SVGA16*)secPort->graphicsDisplay;
if (svga == NULL) return;
svga->DrawDevInset(device, secPort->GetBitMask(), secPort->paletteID);
if (!DevGaugeComposite() || !DevGaugeDocked() || device == NULL) return;
// DOCKED mode only: draw into the main backbuffer (this runs before EndScene).
BTDrawGaugeSurfaces(device, 2.0f, 300.0f, 540.0f, 296.0f);
}
//===========================================================================//
// DEV-COMPOSITE -- the SEPARATE cockpit-MFD window (the default under BT_DEV_GAUGES).
// A second top-level window fed by an ADDITIONAL SWAP CHAIN on the existing main
// device (no second D3D device needed -- textures/vertex data are shared). The pod's
// own per-surface fullscreen-device path (SVGA16::BuildWindows) is untouched; this is
// the dev-box analog of the pod's separate instrument monitors, collapsed into one
// tiled window. BTGaugeWindowRenderAndPresent is called AFTER the main EndScene.
//===========================================================================//
static HWND s_gaugeHwnd = NULL;
static IDirect3DSwapChain9 *s_gaugeSwap = NULL;
static const int s_gaugeW = 960;
static const int s_gaugeH = 384;
static LRESULT CALLBACK BTGaugeWndProc(HWND h, UINT m, WPARAM w, LPARAM l)
{
if (m == WM_CLOSE) { ShowWindow(h, SW_HIDE); return 0; } // hide, never quit the app
return DefWindowProc(h, m, w, l);
}
// Lazily create the window + its additional swap chain (on the MAIN device).
static bool BTGaugeWindowEnsure(LPDIRECT3DDEVICE9 device)
{
if (s_gaugeSwap != NULL) return true;
if (device == NULL) return false;
HINSTANCE hInst = GetModuleHandle(NULL);
static bool s_classReg = false;
if (!s_classReg)
{
WNDCLASSA wc;
memset(&wc, 0, sizeof(wc));
wc.lpfnWndProc = BTGaugeWndProc;
wc.hInstance = hInst;
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH) GetStockObject(BLACK_BRUSH);
wc.lpszClassName = "BTGaugeWnd";
RegisterClassA(&wc);
s_classReg = true;
}
if (s_gaugeHwnd == NULL)
{
RECT r = { 0, 0, s_gaugeW, s_gaugeH };
AdjustWindowRect(&r, WS_OVERLAPPEDWINDOW, FALSE);
s_gaugeHwnd = CreateWindowExA(
0, "BTGaugeWnd", "BattleTech - Cockpit MFDs", WS_OVERLAPPEDWINDOW,
40, 40, r.right - r.left, r.bottom - r.top, NULL, NULL, hInst, NULL);
if (s_gaugeHwnd == NULL) return false;
ShowWindow(s_gaugeHwnd, SW_SHOWNOACTIVATE);
}
// Match the main swap chain's backbuffer format so the additional chain is compatible.
D3DFORMAT bbFormat = D3DFMT_X8R8G8B8;
IDirect3DSwapChain9 *mainSwap = NULL;
if (SUCCEEDED(device->GetSwapChain(0, &mainSwap)) && mainSwap != NULL)
{
D3DPRESENT_PARAMETERS mpp;
if (SUCCEEDED(mainSwap->GetPresentParameters(&mpp)))
bbFormat = mpp.BackBufferFormat;
mainSwap->Release();
}
D3DPRESENT_PARAMETERS pp;
memset(&pp, 0, sizeof(pp));
pp.Windowed = TRUE;
pp.SwapEffect = D3DSWAPEFFECT_DISCARD;
pp.hDeviceWindow = s_gaugeHwnd;
pp.BackBufferFormat = bbFormat;
pp.BackBufferWidth = s_gaugeW;
pp.BackBufferHeight = s_gaugeH;
pp.BackBufferCount = 1;
if (FAILED(device->CreateAdditionalSwapChain(&pp, &s_gaugeSwap)))
{
s_gaugeSwap = NULL;
return false;
}
return true;
}
// Render the 6 surfaces into the gauge window's swap chain and present it. Called AFTER
// the main EndScene, BEFORE the main Present (so it can BeginScene/EndScene on its own).
void BTGaugeWindowRenderAndPresent(LPDIRECT3DDEVICE9 device)
{
if (!DevGaugeComposite() || DevGaugeDocked() || device == NULL) return;
if (!BTGaugeWindowEnsure(device)) return;
IDirect3DSurface9 *gaugeBB = NULL;
if (FAILED(s_gaugeSwap->GetBackBuffer(0, D3DBACKBUFFER_TYPE_MONO, &gaugeBB)) || gaugeBB == NULL)
{
// device likely lost -> drop the chain so it recreates next frame
s_gaugeSwap->Release();
s_gaugeSwap = NULL;
return;
}
IDirect3DSurface9 *mainRT = NULL;
device->GetRenderTarget(0, &mainRT);
IDirect3DSurface9 *mainDS = NULL;
device->GetDepthStencilSurface(&mainDS); // may be NULL
device->SetRenderTarget(0, gaugeBB);
// Unbind the main depth-stencil: it is 800x600 but the gauge backbuffer is 960x384
// (wider) -- a bound depth surface smaller than the render target is INVALID and makes
// every draw silently fail. We don't need depth here (Z is disabled per surface quad).
device->SetDepthStencilSurface(NULL);
device->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(12, 12, 16), 1.0f, 0);
if (SUCCEEDED(device->BeginScene()))
{
BTDrawGaugeSurfaces(device, 0.0f, 0.0f, (float) s_gaugeW, (float) s_gaugeH);
device->EndScene();
}
if (mainRT != NULL) // restore the main render target + depth for the next frame
{
device->SetRenderTarget(0, mainRT);
mainRT->Release();
}
device->SetDepthStencilSurface(mainDS); // NULL is valid (restores "no depth")
if (mainDS != NULL)
mainDS->Release();
gaugeBB->Release();
if (s_gaugeSwap->Present(NULL, NULL, s_gaugeHwnd, NULL, 0) == D3DERR_DEVICELOST)
{
s_gaugeSwap->Release();
s_gaugeSwap = NULL;
}
}
void SVGA16::BuildWindows(unsigned int width, unsigned int height, bool windowed, int *secondaryIndex, int *aux1Index, int *aux2Index)
@@ -3927,7 +4138,8 @@ SVGA16::SVGA16(
):Video16BitBuffered(init_width, init_height)
{
BuildWindows(init_width,init_height,windowed, secondaryIndex, aux1Index, aux2Index);
mDevInsetTex = NULL; // DEV-COMPOSITE (option B): lazily created on first inset draw
for (int _i = 0; _i < 8; _i++) // DEV-COMPOSITE: lazily created on first surface draw
mDevSurfaceTex[_i] = NULL;
//STUBBED: VIDEO RB 1/15/07
# if defined(DEBUG)
Tell("SVGA16::SVGA16()\n");
+9 -5
View File
@@ -292,12 +292,16 @@ private:
int mDisplayToUpdate;
LPDIRECT3DTEXTURE9 mDevInsetTex; // DEV-COMPOSITE (option B): the secondary/radar
// plane uploaded to a texture on the MAIN device.
LPDIRECT3DTEXTURE9 mDevSurfaceTex[8]; // DEV-COMPOSITE: one texture per gauge surface
// (sec/radar + the 5 MFD bit-planes), on the MAIN device.
public:
// DEV-COMPOSITE (option B): palette-expand the secondary plane into mDevInsetTex on
// the given (main) device + draw it as an inset quad. See BTDrawGaugeInset (L4VB16.cpp).
void DrawDevInset(LPDIRECT3DDEVICE9 device, int secMask, int paletteID);
// DEV-COMPOSITE: extract ONE gauge surface from the shared pixelBuffer into
// mDevSurfaceTex[slot] on the given (main) device + draw it as a quad at (dstX,dstY,
// dstW,dstH). monoTint < 0 -> palette-expand (sec/radar, case-0 LUT); monoTint >= 0
// -> mono bit-plane expand ((word & mask) ? tint : 0) for an MFD plane. See
// BTDrawGaugeSurfaces (L4VB16.cpp).
void DrawDevSurface(LPDIRECT3DDEVICE9 device, int slot, int mask, int paletteID,
int monoTint, float dstX, float dstY, float dstW, float dstH);
};
//########################################################################
+8 -2
View File
@@ -6963,13 +6963,19 @@ void DPLRenderer::ExecuteImplementation(RendererComplexity, RendererOrigin::Inte
if (mCamShipHUD)
mCamShipHUD->Render(0, &viewTransform);
// DEV-COMPOSITE (option B): blit the woken gauge renderer's secondary/radar surface
// as an inset in this window (LAST draw before EndScene; no-op unless BT_DEV_GAUGES).
// DEV-COMPOSITE: in DOCKED mode (BT_DEV_GAUGES_DOCK) blit the 6-surface gauge panel
// into this window as the LAST draw before EndScene (no-op otherwise / off pod).
extern void BTDrawGaugeInset(LPDIRECT3DDEVICE9 device);
BTDrawGaugeInset(mDevice);
hr = mDevice->EndScene();
// DEV-COMPOSITE: default mode -- render the 6 cockpit surfaces into a SEPARATE window
// (its own additional swap chain on this device) + present it, between the main
// EndScene and the main Present. No-op unless BT_DEV_GAUGES (and not docked mode).
extern void BTGaugeWindowRenderAndPresent(LPDIRECT3DDEVICE9 device);
BTGaugeWindowRenderAndPresent(mDevice);
// DIAG (turn-hitch hunt): draw CPU is _rt0..here; Present blocks on the GPU.
LARGE_INTEGER _rt1; QueryPerformanceCounter(&_rt1);
hr = mDevice->Present(NULL, NULL, NULL, NULL);