Skip to content

Commit 71aede9

Browse files
committed
gfx/d3d8: Upload RGUI menu framebuffer directly as ARGB4444
The D3D8 driver previously expanded RGUI's 16bpp menu framebuffer to 32bpp on the CPU every frame via a per-pixel loop, then uploaded the result into a D3DFMT_A8R8G8B8 texture. RGUI already assembles its framebuffer in 16bpp; D3D8 has supported D3DFMT_A4R4G4B4 as a baseline texture format since launch, on both PC and Original Xbox. Add D3D8_ARGB4444_FORMAT to the existing D3D8 format macros (with D3DFMT_LIN_A4R4G4B4 for the _XBOX build path), and a "d3d8" case to the RGUI pixel format dispatcher selecting argb32_to_argb4444 (already in use by the rsx/PS3 and d3d9_hlsl/d3d9_cg drivers, which target the same ARGB4444 bit layout). In d3d8_set_menu_texture_frame, allocate the menu texture as D3DFMT_A4R4G4B4 when rgb32 is false (the only case in current practice; RGUI is the sole caller), and upload row-by-row via memcpy. The rgb32 = true API branch is preserved for forward compatibility and continues to use D3DFMT_A8R8G8B8. Track the bpp of the currently-allocated menu texture in a new d3d8_video_t::menu_tex_rgb32 field so the texture is recreated when the rgb32 flag flips between calls — same approach the D3D9 patch uses for d3d9_video_t. Endian-safe by construction: argb32_to_argb4444 produces a host-endian uint16_t with A in bits 15..12 down to B in 3..0; D3DFMT_A4R4G4B4 is read by D3D as host-endian 16-bit units with the same bit assignments. D3D8 only targets LE hosts (Pentium III on Original Xbox, x86/x64 on legacy Windows), so there is no byte-swap concern in either direction. Xbox-specific tex_coords[2]/[3] assignment after texture (re)creation is preserved.
1 parent 67e2a9e commit 71aede9

2 files changed

Lines changed: 40 additions & 21 deletions

File tree

gfx/drivers/d3d8.c

Lines changed: 39 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -79,10 +79,12 @@
7979
#define D3D8_RGB565_FORMAT D3DFMT_LIN_R5G6B5
8080
#define D3D8_XRGB8888_FORMAT D3DFMT_LIN_X8R8G8B8
8181
#define D3D8_ARGB8888_FORMAT D3DFMT_LIN_A8R8G8B8
82+
#define D3D8_ARGB4444_FORMAT D3DFMT_LIN_A4R4G4B4
8283
#else
8384
#define D3D8_RGB565_FORMAT D3DFMT_R5G6B5
8485
#define D3D8_XRGB8888_FORMAT D3DFMT_X8R8G8B8
8586
#define D3D8_ARGB8888_FORMAT D3DFMT_A8R8G8B8
87+
#define D3D8_ARGB4444_FORMAT D3DFMT_A4R4G4B4
8688
#endif
8789

8890
typedef struct d3d8_video
@@ -128,6 +130,14 @@ typedef struct d3d8_video
128130

129131
/* Only used for Xbox */
130132
bool widescreen_mode;
133+
134+
/* Bit-depth of the data most recently uploaded to `menu->tex`.
135+
* The menu texture is created with a fixed pixel format (16bpp
136+
* ARGB4444 for the RGUI fast path, 32bpp ARGB8888 otherwise),
137+
* so we must recreate it when set_menu_texture_frame is called
138+
* with a different `rgb32` value. Defaults to false; the first
139+
* call will see a NULL tex and create one regardless. */
140+
bool menu_tex_rgb32;
131141
} d3d8_video_t;
132142

133143
typedef struct d3d8_renderchain
@@ -2000,24 +2010,33 @@ static void d3d8_set_menu_texture_frame(void *data,
20002010
if (!d3d || !d3d->menu)
20012011
return;
20022012

2003-
if ( !d3d->menu->tex ||
2004-
d3d->menu->tex_w != width ||
2005-
d3d->menu->tex_h != height)
2013+
if ( !d3d->menu->tex ||
2014+
d3d->menu->tex_w != width ||
2015+
d3d->menu->tex_h != height ||
2016+
d3d->menu_tex_rgb32 != rgb32)
20062017
{
20072018
LPDIRECT3DTEXTURE8 tex = d3d->menu->tex;
20082019
if (tex)
20092020
IDirect3DTexture8_Release(tex);
20102021

2022+
/* RGUI sends 16bpp ARGB4444 (the d3d8 case in RGUI's pixel
2023+
* format dispatcher selects argb32_to_argb4444), so we can
2024+
* upload it byte-for-byte into a D3DFMT_A4R4G4B4 texture and
2025+
* skip the per-pixel CPU expansion to ARGB8888 the previous
2026+
* implementation did every frame. The rgb32 path is preserved
2027+
* for callers that hand us 32bpp data; in current practice no
2028+
* such caller exists, but the API contract supports it. */
20112029
d3d->menu->tex = d3d8_texture_new(d3d->dev,
20122030
width, height, 1,
2013-
0, D3D8_ARGB8888_FORMAT,
2031+
0, rgb32 ? D3D8_ARGB8888_FORMAT : D3D8_ARGB4444_FORMAT,
20142032
D3DPOOL_MANAGED, 0, 0, 0, NULL, NULL, false);
20152033

20162034
if (!d3d->menu->tex)
20172035
return;
20182036

20192037
d3d->menu->tex_w = width;
20202038
d3d->menu->tex_h = height;
2039+
d3d->menu_tex_rgb32 = rgb32;
20212040
#ifdef _XBOX
20222041
d3d->menu->tex_coords [2] = width;
20232042
d3d->menu->tex_coords[3] = height;
@@ -2031,7 +2050,7 @@ static void d3d8_set_menu_texture_frame(void *data,
20312050
if (IDirect3DTexture8_LockRect(tex,
20322051
0, &d3dlr, NULL, D3DLOCK_NOSYSLOCK) == D3D_OK)
20332052
{
2034-
unsigned h, w;
2053+
unsigned h;
20352054

20362055
if (rgb32)
20372056
{
@@ -2047,24 +2066,23 @@ static void d3d8_set_menu_texture_frame(void *data,
20472066
}
20482067
else
20492068
{
2050-
uint32_t *dst = (uint32_t*)d3dlr.pBits;
2051-
const uint16_t *src = (const uint16_t*)frame;
2069+
/* Direct ARGB4444 upload. The bit layout produced by
2070+
* argb32_to_argb4444 (host-endian uint16_t with A in bits
2071+
* 15..12, R 11..8, G 7..4, B 3..0) matches D3DFMT_A4R4G4B4
2072+
* exactly: D3D reads the locked memory as host-endian
2073+
* 16-bit units with the same bit assignments, so the same
2074+
* source bytes work on LE PC and LE Original Xbox (NV2A
2075+
* via D3DFMT_LIN_*) without a byte swap. */
2076+
uint8_t *dst = (uint8_t*)d3dlr.pBits;
2077+
const uint8_t *src = (const uint8_t*)frame;
2078+
unsigned src_pitch = width * sizeof(uint16_t);
2079+
unsigned row_bytes = width * sizeof(uint16_t);
20522080

2053-
for (h = 0; h < height; h++, dst += d3dlr.Pitch >> 2, src += width)
2081+
for (h = 0; h < height; h++, dst += d3dlr.Pitch, src += src_pitch)
20542082
{
2055-
for (w = 0; w < width; w++)
2056-
{
2057-
uint16_t c = src[w];
2058-
uint32_t r = (c >> 12) & 0xf;
2059-
uint32_t g = (c >> 8) & 0xf;
2060-
uint32_t b = (c >> 4) & 0xf;
2061-
uint32_t a = (c >> 0) & 0xf;
2062-
r = ((r << 4) | r) << 16;
2063-
g = ((g << 4) | g) << 8;
2064-
b = ((b << 4) | b) << 0;
2065-
a = ((a << 4) | a) << 24;
2066-
dst[w] = r | g | b | a;
2067-
}
2083+
memcpy(dst, src, row_bytes);
2084+
if (d3dlr.Pitch > (int)row_bytes)
2085+
memset(dst + row_bytes, 0, d3dlr.Pitch - row_bytes);
20682086
}
20692087
}
20702088

menu/drivers/rgui.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1397,6 +1397,7 @@ static bool rgui_set_pixel_format_function(void)
13971397
else if (string_is_equal(driver_ident, "psp1")) /* PSP */
13981398
argb32_to_pixel_platform_format = argb32_to_abgr4444;
13991399
else if ( string_is_equal(driver_ident, "rsx") /* PS3 */
1400+
|| string_is_equal(driver_ident, "d3d8") /* D3D8 (Original Xbox + legacy Windows) */
14001401
|| string_is_equal(driver_ident, "d3d9_hlsl") /* D3D9 (PC + Xbox 360) */
14011402
|| string_is_equal(driver_ident, "d3d9_cg"))
14021403
argb32_to_pixel_platform_format = argb32_to_argb4444;

0 commit comments

Comments
 (0)