mirror of
https://github.com/libretro/RetroArch.git
synced 2026-11-08 08:10:30 -10:00
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.
This commit is contained in:
1 parent
67e2a9e363
commit
71aede9632
2 files changed
+40
-21
No files matched your search
+39
-21
@@ -79,10 +79,12 @@
|
||||
#define D3D8_RGB565_FORMAT D3DFMT_LIN_R5G6B5
|
||||
#define D3D8_XRGB8888_FORMAT D3DFMT_LIN_X8R8G8B8
|
||||
#define D3D8_ARGB8888_FORMAT D3DFMT_LIN_A8R8G8B8
|
||||
#define D3D8_ARGB4444_FORMAT D3DFMT_LIN_A4R4G4B4
|
||||
#else
|
||||
#define D3D8_RGB565_FORMAT D3DFMT_R5G6B5
|
||||
#define D3D8_XRGB8888_FORMAT D3DFMT_X8R8G8B8
|
||||
#define D3D8_ARGB8888_FORMAT D3DFMT_A8R8G8B8
|
||||
#define D3D8_ARGB4444_FORMAT D3DFMT_A4R4G4B4
|
||||
#endif
|
||||
|
||||
typedef struct d3d8_video
|
||||
@@ -128,6 +130,14 @@ typedef struct d3d8_video
|
||||
|
||||
/* Only used for Xbox */
|
||||
bool widescreen_mode;
|
||||
|
||||
/* Bit-depth of the data most recently uploaded to `menu->tex`.
|
||||
* The menu texture is created with a fixed pixel format (16bpp
|
||||
* ARGB4444 for the RGUI fast path, 32bpp ARGB8888 otherwise),
|
||||
* so we must recreate it when set_menu_texture_frame is called
|
||||
* with a different `rgb32` value. Defaults to false; the first
|
||||
* call will see a NULL tex and create one regardless. */
|
||||
bool menu_tex_rgb32;
|
||||
} d3d8_video_t;
|
||||
|
||||
typedef struct d3d8_renderchain
|
||||
@@ -2000,17 +2010,25 @@ static void d3d8_set_menu_texture_frame(void *data,
|
||||
if (!d3d || !d3d->menu)
|
||||
return;
|
||||
|
||||
if ( !d3d->menu->tex ||
|
||||
d3d->menu->tex_w != width ||
|
||||
d3d->menu->tex_h != height)
|
||||
if ( !d3d->menu->tex ||
|
||||
d3d->menu->tex_w != width ||
|
||||
d3d->menu->tex_h != height ||
|
||||
d3d->menu_tex_rgb32 != rgb32)
|
||||
{
|
||||
LPDIRECT3DTEXTURE8 tex = d3d->menu->tex;
|
||||
if (tex)
|
||||
IDirect3DTexture8_Release(tex);
|
||||
|
||||
/* RGUI sends 16bpp ARGB4444 (the d3d8 case in RGUI's pixel
|
||||
* format dispatcher selects argb32_to_argb4444), so we can
|
||||
* upload it byte-for-byte into a D3DFMT_A4R4G4B4 texture and
|
||||
* skip the per-pixel CPU expansion to ARGB8888 the previous
|
||||
* implementation did every frame. The rgb32 path is preserved
|
||||
* for callers that hand us 32bpp data; in current practice no
|
||||
* such caller exists, but the API contract supports it. */
|
||||
d3d->menu->tex = d3d8_texture_new(d3d->dev,
|
||||
width, height, 1,
|
||||
0, D3D8_ARGB8888_FORMAT,
|
||||
0, rgb32 ? D3D8_ARGB8888_FORMAT : D3D8_ARGB4444_FORMAT,
|
||||
D3DPOOL_MANAGED, 0, 0, 0, NULL, NULL, false);
|
||||
|
||||
if (!d3d->menu->tex)
|
||||
@@ -2018,6 +2036,7 @@ static void d3d8_set_menu_texture_frame(void *data,
|
||||
|
||||
d3d->menu->tex_w = width;
|
||||
d3d->menu->tex_h = height;
|
||||
d3d->menu_tex_rgb32 = rgb32;
|
||||
#ifdef _XBOX
|
||||
d3d->menu->tex_coords [2] = width;
|
||||
d3d->menu->tex_coords[3] = height;
|
||||
@@ -2031,7 +2050,7 @@ static void d3d8_set_menu_texture_frame(void *data,
|
||||
if (IDirect3DTexture8_LockRect(tex,
|
||||
0, &d3dlr, NULL, D3DLOCK_NOSYSLOCK) == D3D_OK)
|
||||
{
|
||||
unsigned h, w;
|
||||
unsigned h;
|
||||
|
||||
if (rgb32)
|
||||
{
|
||||
@@ -2047,24 +2066,23 @@ static void d3d8_set_menu_texture_frame(void *data,
|
||||
}
|
||||
else
|
||||
{
|
||||
uint32_t *dst = (uint32_t*)d3dlr.pBits;
|
||||
const uint16_t *src = (const uint16_t*)frame;
|
||||
/* Direct ARGB4444 upload. The bit layout produced by
|
||||
* argb32_to_argb4444 (host-endian uint16_t with A in bits
|
||||
* 15..12, R 11..8, G 7..4, B 3..0) matches D3DFMT_A4R4G4B4
|
||||
* exactly: D3D reads the locked memory as host-endian
|
||||
* 16-bit units with the same bit assignments, so the same
|
||||
* source bytes work on LE PC and LE Original Xbox (NV2A
|
||||
* via D3DFMT_LIN_*) without a byte swap. */
|
||||
uint8_t *dst = (uint8_t*)d3dlr.pBits;
|
||||
const uint8_t *src = (const uint8_t*)frame;
|
||||
unsigned src_pitch = width * sizeof(uint16_t);
|
||||
unsigned row_bytes = width * sizeof(uint16_t);
|
||||
|
||||
for (h = 0; h < height; h++, dst += d3dlr.Pitch >> 2, src += width)
|
||||
for (h = 0; h < height; h++, dst += d3dlr.Pitch, src += src_pitch)
|
||||
{
|
||||
for (w = 0; w < width; w++)
|
||||
{
|
||||
uint16_t c = src[w];
|
||||
uint32_t r = (c >> 12) & 0xf;
|
||||
uint32_t g = (c >> 8) & 0xf;
|
||||
uint32_t b = (c >> 4) & 0xf;
|
||||
uint32_t a = (c >> 0) & 0xf;
|
||||
r = ((r << 4) | r) << 16;
|
||||
g = ((g << 4) | g) << 8;
|
||||
b = ((b << 4) | b) << 0;
|
||||
a = ((a << 4) | a) << 24;
|
||||
dst[w] = r | g | b | a;
|
||||
}
|
||||
memcpy(dst, src, row_bytes);
|
||||
if (d3dlr.Pitch > (int)row_bytes)
|
||||
memset(dst + row_bytes, 0, d3dlr.Pitch - row_bytes);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1397,6 +1397,7 @@ static bool rgui_set_pixel_format_function(void)
|
||||
else if (string_is_equal(driver_ident, "psp1")) /* PSP */
|
||||
argb32_to_pixel_platform_format = argb32_to_abgr4444;
|
||||
else if ( string_is_equal(driver_ident, "rsx") /* PS3 */
|
||||
|| string_is_equal(driver_ident, "d3d8") /* D3D8 (Original Xbox + legacy Windows) */
|
||||
|| string_is_equal(driver_ident, "d3d9_hlsl") /* D3D9 (PC + Xbox 360) */
|
||||
|| string_is_equal(driver_ident, "d3d9_cg"))
|
||||
argb32_to_pixel_platform_format = argb32_to_argb4444;
|
||||
|
||||
Reference in new issue
Block a user