diff --git a/hw/xbox/nv2a/pgraph/vk/constants.h b/hw/xbox/nv2a/pgraph/vk/constants.h index 2607b17379..de36664707 100644 --- a/hw/xbox/nv2a/pgraph/vk/constants.h +++ b/hw/xbox/nv2a/pgraph/vk/constants.h @@ -331,8 +331,12 @@ typedef struct SurfaceFormatInfo { static const BasicSurfaceFormatInfo kelvin_surface_color_format_map[] = { [NV097_SET_SURFACE_FORMAT_COLOR_LE_X1R5G5B5_Z1R5G5B5] = { 2 }, + [NV097_SET_SURFACE_FORMAT_COLOR_LE_X1R5G5B5_O1R5G5B5] = { 2 }, [NV097_SET_SURFACE_FORMAT_COLOR_LE_R5G6B5] = { 2 }, [NV097_SET_SURFACE_FORMAT_COLOR_LE_X8R8G8B8_Z8R8G8B8] = { 4 }, + [NV097_SET_SURFACE_FORMAT_COLOR_LE_X8R8G8B8_O8R8G8B8] = { 4 }, + [NV097_SET_SURFACE_FORMAT_COLOR_LE_X1A7R8G8B8_Z1A7R8G8B8] = { 4 }, + [NV097_SET_SURFACE_FORMAT_COLOR_LE_X1A7R8G8B8_O1A7R8G8B8] = { 4 }, [NV097_SET_SURFACE_FORMAT_COLOR_LE_A8R8G8B8] = { 4 }, [NV097_SET_SURFACE_FORMAT_COLOR_LE_B8] = { 1 }, [NV097_SET_SURFACE_FORMAT_COLOR_LE_G8B8] = { 2 }, @@ -347,6 +351,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 +374,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 +451,25 @@ static const SurfaceFormatInfo zeta_d24_unorm_s8_uint = { VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT, }; +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 573222c0b7..4eed80ced5 100644 --- a/hw/xbox/nv2a/pgraph/vk/renderer.h +++ b/hw/xbox/nv2a/pgraph/vk/renderer.h @@ -462,6 +462,8 @@ typedef struct PGRAPHVkState { QSIMPLEQ_HEAD(, QueryReport) report_queue; // FIXME: Statically allocate SurfaceFormatInfo kelvin_surface_zeta_vk_map[3]; + SurfaceFormatInfo + kelvin_surface_color_vk_map[ARRAY_SIZE(kelvin_surface_color_format_vk_map)]; uint32_t clear_parameter; diff --git a/hw/xbox/nv2a/pgraph/vk/surface.c b/hw/xbox/nv2a/pgraph/vk/surface.c index 393f4d512d..730e5153c9 100644 --- a/hw/xbox/nv2a/pgraph/vk/surface.c +++ b/hw/xbox/nv2a/pgraph/vk/surface.c @@ -1465,7 +1465,7 @@ 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); @@ -1843,15 +1843,62 @@ 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"); + 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 *fallback1; + const SurfaceFormatInfo *fallback2; + } color_fallbacks[] = { + { 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 }, + { 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++) { + SurfaceFormatInfo *slot = + &r->kelvin_surface_color_vk_map[color_fallbacks[i].idx]; + const SurfaceFormatInfo *fallback = NULL; + + if (slot->host_bytes_per_pixel == 0) { + continue; + } + + if (check_format_and_usage_supported(r, slot->vk_format, slot->usage)) { + continue; + } + + if (color_fallbacks[i].fallback1 && + check_format_and_usage_supported(r, + color_fallbacks[i].fallback1->vk_format, + color_fallbacks[i].fallback1->usage)) { + fallback = color_fallbacks[i].fallback1; + } else if (color_fallbacks[i].fallback2 && + check_format_and_usage_supported( + r, color_fallbacks[i].fallback2->vk_format, + color_fallbacks[i].fallback2->usage)) { + fallback = color_fallbacks[i].fallback2; + } + + if (fallback) { + *slot = *fallback; + } else { + fprintf(stderr, + "Warning: Vulkan color surface format 0x%x has no supported fallback\n", + color_fallbacks[i].idx); + } } // Check if the device supports preferred VK_FORMAT_D24_UNORM_S8_UINT