From 04d5ec3f6a1caa4052f870f1aea6d4a51646c17c Mon Sep 17 00:00:00 2001 From: izzy2lost Date: Fri, 27 Feb 2026 18:41:08 -0500 Subject: [PATCH] might fix some missing textures --- hw/xbox/nv2a/pgraph/vk/constants.h | 59 ++++++++++++++++++++ hw/xbox/nv2a/pgraph/vk/renderer.h | 1 + hw/xbox/nv2a/pgraph/vk/surface.c | 87 ++++++++++++++++++++++++++---- 3 files changed, 137 insertions(+), 10 deletions(-) diff --git a/hw/xbox/nv2a/pgraph/vk/constants.h b/hw/xbox/nv2a/pgraph/vk/constants.h index 2607b17379..3cd926423e 100644 --- a/hw/xbox/nv2a/pgraph/vk/constants.h +++ b/hw/xbox/nv2a/pgraph/vk/constants.h @@ -347,6 +347,14 @@ static const SurfaceFormatInfo kelvin_surface_color_format_vk_map[] = { VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, VK_IMAGE_ASPECT_COLOR_BIT, }, + [NV097_SET_SURFACE_FORMAT_COLOR_LE_X1R5G5B5_O1R5G5B5] = + { + // FIXME: Force alpha to one + 2, + VK_FORMAT_A1R5G5B5_UNORM_PACK16, + VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, + VK_IMAGE_ASPECT_COLOR_BIT, + }, [NV097_SET_SURFACE_FORMAT_COLOR_LE_R5G6B5] = { 2, @@ -362,6 +370,30 @@ static const SurfaceFormatInfo kelvin_surface_color_format_vk_map[] = { VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, VK_IMAGE_ASPECT_COLOR_BIT, }, + [NV097_SET_SURFACE_FORMAT_COLOR_LE_X8R8G8B8_O8R8G8B8] = + { + // FIXME: Force alpha to one + 4, + VK_FORMAT_B8G8R8A8_UNORM, + VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, + VK_IMAGE_ASPECT_COLOR_BIT, + }, + [NV097_SET_SURFACE_FORMAT_COLOR_LE_X1A7R8G8B8_Z1A7R8G8B8] = + { + // FIXME: Precise X1A7 alpha encoding not emulated; stored as 8-bit alpha + 4, + VK_FORMAT_B8G8R8A8_UNORM, + VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, + VK_IMAGE_ASPECT_COLOR_BIT, + }, + [NV097_SET_SURFACE_FORMAT_COLOR_LE_X1A7R8G8B8_O1A7R8G8B8] = + { + // FIXME: Precise X1A7 alpha encoding not emulated; stored as 8-bit alpha + 4, + VK_FORMAT_B8G8R8A8_UNORM, + VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, + VK_IMAGE_ASPECT_COLOR_BIT, + }, [NV097_SET_SURFACE_FORMAT_COLOR_LE_A8R8G8B8] = { 4, @@ -415,4 +447,31 @@ static const SurfaceFormatInfo zeta_d24_unorm_s8_uint = { VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT, }; +// Fallback color surface formats used when the primary VkFormat is not +// supported as VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT on a given device. +// VK_FORMAT_A1R5G5B5_UNORM_PACK16 has known issues on Adreno: not supported +// as COLOR_ATTACHMENT on Adreno 740 (crashes), and renders with broken alpha +// on Adreno 710 (invisible geometry). R5G6B5 is universally supported and +// correct for Xbox's X1R5G5B5 surfaces where the X bit is always opaque. +static const SurfaceFormatInfo color_r5g6b5_fallback = { + 2, + VK_FORMAT_R5G6B5_UNORM_PACK16, + VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, + VK_IMAGE_ASPECT_COLOR_BIT, +}; + +static const SurfaceFormatInfo color_b8g8r8a8_fallback = { + 4, + VK_FORMAT_B8G8R8A8_UNORM, + VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, + VK_IMAGE_ASPECT_COLOR_BIT, +}; + +static const SurfaceFormatInfo color_r8g8b8a8_fallback = { + 4, + VK_FORMAT_R8G8B8A8_UNORM, + VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, + VK_IMAGE_ASPECT_COLOR_BIT, +}; + #endif diff --git a/hw/xbox/nv2a/pgraph/vk/renderer.h b/hw/xbox/nv2a/pgraph/vk/renderer.h index 66cb6bb084..a53d47bae9 100644 --- a/hw/xbox/nv2a/pgraph/vk/renderer.h +++ b/hw/xbox/nv2a/pgraph/vk/renderer.h @@ -425,6 +425,7 @@ typedef struct PGRAPHVkState { QSIMPLEQ_HEAD(, QueryReport) report_queue; // FIXME: Statically allocate SurfaceFormatInfo kelvin_surface_zeta_vk_map[3]; + SurfaceFormatInfo kelvin_surface_color_vk_map[11]; // indices 0x00–0x0A, per-device substitutions uint32_t clear_parameter; diff --git a/hw/xbox/nv2a/pgraph/vk/surface.c b/hw/xbox/nv2a/pgraph/vk/surface.c index 915e7d0cbd..315be0363a 100644 --- a/hw/xbox/nv2a/pgraph/vk/surface.c +++ b/hw/xbox/nv2a/pgraph/vk/surface.c @@ -28,6 +28,12 @@ #include "qemu/compiler.h" #include "ui/xemu-settings.h" #include "renderer.h" +#ifdef __ANDROID__ +#include +#define NV2A_LOG(...) __android_log_print(ANDROID_LOG_INFO, "xemu-android", __VA_ARGS__) +#else +#define NV2A_LOG(...) fprintf(stderr, __VA_ARGS__) +#endif const int num_invalid_surfaces_to_keep = 10; // FIXME: Make automatic const int max_surface_frame_time_delta = 5; @@ -1328,12 +1334,14 @@ static void populate_surface_binding_target_sized(NV2AState *d, bool color, assert(pg->surface_shape.color_format < ARRAY_SIZE(kelvin_surface_color_format_vk_map)); fmt = kelvin_surface_color_format_map[pg->surface_shape.color_format]; - host_fmt = kelvin_surface_color_format_vk_map[pg->surface_shape.color_format]; + host_fmt = r->kelvin_surface_color_vk_map[pg->surface_shape.color_format]; if (host_fmt.host_bytes_per_pixel == 0) { fprintf(stderr, "nv2a: unimplemented color surface format 0x%x\n", pg->surface_shape.color_format); abort(); } + NV2A_LOG("nv2a: [surface] color fmt=0x%x vk_format=%d", + pg->surface_shape.color_format, (int)host_fmt.vk_format); } else { surface = &pg->surface_zeta; dma_address = pg->dma_zeta; @@ -1704,15 +1712,74 @@ void pgraph_vk_init_surfaces(PGRAPHState *pg) { PGRAPHVkState *r = pg->vk_renderer_state; - // Make sure all surface format types are supported. We don't expect issue - // with these, and therefore have no fallback mechanism. - bool color_formats_supported = check_surface_internal_formats_supported( - r, kelvin_surface_color_format_vk_map, - ARRAY_SIZE(kelvin_surface_color_format_vk_map)); - if (!color_formats_supported) { - fprintf(stderr, - "Warning: Some Vulkan surface color formats are unsupported; " - "continuing with best-effort mapping.\n"); + // Build the per-device mutable color format map. We start from the const + // baseline and then check each format that has known portability issues. + // If the primary VkFormat is not supported as COLOR_ATTACHMENT on this + // device, we substitute a compatible fallback (same or wider bpp). + // This mirrors the existing per-device substitution done for zeta formats. + assert(ARRAY_SIZE(kelvin_surface_color_format_vk_map) <= + ARRAY_SIZE(r->kelvin_surface_color_vk_map)); + memcpy(r->kelvin_surface_color_vk_map, kelvin_surface_color_format_vk_map, + sizeof(kelvin_surface_color_format_vk_map)); + + static const struct { + int idx; + const SurfaceFormatInfo *f1; + const SurfaceFormatInfo *f2; + } color_fallbacks[] = { + // A1R5G5B5_UNORM_PACK16 has known issues on Adreno GPUs: + // Adreno 740: not supported as COLOR_ATTACHMENT → hard crash + // Adreno 710: renders with broken alpha → invisible geometry + // R5G6B5 is the correct substitute for Xbox's X1R5G5B5 format because + // the "X" bit is always opaque; no alpha data is lost. + { NV097_SET_SURFACE_FORMAT_COLOR_LE_X1R5G5B5_Z1R5G5B5, + &color_r5g6b5_fallback, &color_b8g8r8a8_fallback }, + { NV097_SET_SURFACE_FORMAT_COLOR_LE_X1R5G5B5_O1R5G5B5, + &color_r5g6b5_fallback, &color_b8g8r8a8_fallback }, + // B8G8R8A8 is widely supported but try R8G8B8A8 as a fallback. + { NV097_SET_SURFACE_FORMAT_COLOR_LE_X8R8G8B8_Z8R8G8B8, + &color_r8g8b8a8_fallback, NULL }, + { NV097_SET_SURFACE_FORMAT_COLOR_LE_X8R8G8B8_O8R8G8B8, + &color_r8g8b8a8_fallback, NULL }, + { NV097_SET_SURFACE_FORMAT_COLOR_LE_X1A7R8G8B8_Z1A7R8G8B8, + &color_r8g8b8a8_fallback, NULL }, + { NV097_SET_SURFACE_FORMAT_COLOR_LE_X1A7R8G8B8_O1A7R8G8B8, + &color_r8g8b8a8_fallback, NULL }, + { NV097_SET_SURFACE_FORMAT_COLOR_LE_A8R8G8B8, + &color_r8g8b8a8_fallback, NULL }, + }; + + for (int i = 0; i < ARRAY_SIZE(color_fallbacks); i++) { + int idx = color_fallbacks[i].idx; + SurfaceFormatInfo *slot = &r->kelvin_surface_color_vk_map[idx]; + if (slot->host_bytes_per_pixel == 0) { + continue; // absent entry, skip + } + if (check_format_and_usage_supported(r, slot->vk_format, slot->usage)) { + NV2A_LOG("nv2a: color surface fmt 0x%x vk_format=%d OK on this device", + idx, (int)slot->vk_format); + continue; // primary format fine on this device + } + const SurfaceFormatInfo *sub = NULL; + if (color_fallbacks[i].f1 && + check_format_and_usage_supported(r, color_fallbacks[i].f1->vk_format, + color_fallbacks[i].f1->usage)) { + sub = color_fallbacks[i].f1; + } else if (color_fallbacks[i].f2 && + check_format_and_usage_supported(r, color_fallbacks[i].f2->vk_format, + color_fallbacks[i].f2->usage)) { + sub = color_fallbacks[i].f2; + } + if (sub) { + NV2A_LOG("nv2a: color surface fmt 0x%x vk_format=%d not supported " + "as COLOR_ATTACHMENT, substituting vk_format=%d", + idx, (int)slot->vk_format, (int)sub->vk_format); + *slot = *sub; + } else { + NV2A_LOG("nv2a: color surface fmt 0x%x vk_format=%d not supported " + "as COLOR_ATTACHMENT, no fallback available", + idx, (int)slot->vk_format); + } } // Check if the device supports preferred VK_FORMAT_D24_UNORM_S8_UINT