From 14f020dd3b54eb1802b6847302403c6ff679c0ee Mon Sep 17 00:00:00 2001 From: izzy2lost Date: Fri, 13 Feb 2026 03:45:50 -0500 Subject: [PATCH] a little more stable and a little better performance. --- android/app/src/main/cpp/CMakeLists.txt | 6 +- hw/xbox/nv2a/pgraph/gl/renderer.c | 4 - hw/xbox/nv2a/pgraph/pgraph.c | 42 ++++++--- hw/xbox/nv2a/pgraph/vk/buffer.c | 113 +++++++++++++++++++++--- hw/xbox/nv2a/pgraph/vk/display.c | 67 ++++++++------ hw/xbox/nv2a/pgraph/vk/instance.c | 77 ++++++++++++++-- hw/xbox/nv2a/pgraph/vk/renderer.c | 92 ++++++++++++++++--- hw/xbox/nv2a/pgraph/vk/renderer.h | 3 +- hw/xbox/nv2a/pgraph/vk/surface.c | 25 +++++- ui/xemu.c | 16 +++- 10 files changed, 357 insertions(+), 88 deletions(-) diff --git a/android/app/src/main/cpp/CMakeLists.txt b/android/app/src/main/cpp/CMakeLists.txt index 05733e13fe..fcf4499c60 100644 --- a/android/app/src/main/cpp/CMakeLists.txt +++ b/android/app/src/main/cpp/CMakeLists.txt @@ -262,7 +262,7 @@ set(XEMU_CORE_DIRS set(XEMU_CORE_SOURCES "") foreach(dir ${XEMU_CORE_DIRS}) - file(GLOB_RECURSE DIR_SOURCES + file(GLOB_RECURSE DIR_SOURCES CONFIGURE_DEPENDS "${REPO_ROOT}/${dir}/*.c" "${REPO_ROOT}/${dir}/*.cc" "${REPO_ROOT}/${dir}/*.cpp" @@ -270,7 +270,7 @@ foreach(dir ${XEMU_CORE_DIRS}) list(APPEND XEMU_CORE_SOURCES ${DIR_SOURCES}) endforeach() -file(GLOB ROOT_SOURCES +file(GLOB ROOT_SOURCES CONFIGURE_DEPENDS "${REPO_ROOT}/*.c" "${REPO_ROOT}/*.cc" "${REPO_ROOT}/*.cpp" @@ -375,6 +375,8 @@ list(FILTER XEMU_CORE_SOURCES EXCLUDE REGEX "hw[\\\\/]vfio-user[\\\\/].*\\.c$") list(FILTER XEMU_CORE_SOURCES EXCLUDE REGEX "hw[\\\\/]virtio[\\\\/].*\\.c$") list(FILTER XEMU_CORE_SOURCES EXCLUDE REGEX "hw[\\\\/]watchdog[\\\\/].*\\.c$") list(FILTER XEMU_CORE_SOURCES EXCLUDE REGEX "hw[\\\\/]xbox[\\\\/]chihiro.*\\.c$") +list(FILTER XEMU_CORE_SOURCES EXCLUDE REGEX "hw[\\\\/]xbox[\\\\/]nv2a[\\\\/]pgraph[\\\\/]gl[\\\\/]gpuprops\\.c$") +list(FILTER XEMU_CORE_SOURCES EXCLUDE REGEX "hw[\\\\/]xbox[\\\\/]nv2a[\\\\/]pgraph[\\\\/]vk[\\\\/]gpuprops\\.c$") if(NOT XEMU_ENABLE_VULKAN) list(FILTER XEMU_CORE_SOURCES EXCLUDE REGEX "hw[\\\\/]xbox[\\\\/]nv2a[\\\\/]pgraph[\\\\/]vk[\\\\/].*\\.c$") endif() diff --git a/hw/xbox/nv2a/pgraph/gl/renderer.c b/hw/xbox/nv2a/pgraph/gl/renderer.c index 19bd4f885f..8cda79de36 100644 --- a/hw/xbox/nv2a/pgraph/gl/renderer.c +++ b/hw/xbox/nv2a/pgraph/gl/renderer.c @@ -114,10 +114,6 @@ static void pgraph_gl_init(NV2AState *d, Error **errp) gl_debug_initialize(); #endif -#ifdef __ANDROID__ - pgraph_gl_determine_gpu_properties(); -#endif - #ifdef __ANDROID__ /* DXT textures may be available via extension on Android. */ if (!glo_check_extension("GL_EXT_texture_compression_s3tc")) { diff --git a/hw/xbox/nv2a/pgraph/pgraph.c b/hw/xbox/nv2a/pgraph/pgraph.c index 2b1cb9c5a4..03aeba30d4 100644 --- a/hw/xbox/nv2a/pgraph/pgraph.c +++ b/hw/xbox/nv2a/pgraph/pgraph.c @@ -402,22 +402,40 @@ static bool attempt_renderer_init(PGRAPHState *pg) static void init_renderer(PGRAPHState *pg) { - if (attempt_renderer_init(pg)) { - return; // Success - } - + CONFIG_DISPLAY_RENDERER original_renderer = g_config.display.renderer; CONFIG_DISPLAY_RENDERER default_renderer = get_default_renderer(); - if (default_renderer != g_config.display.renderer) { - g_config.display.renderer = default_renderer; - if (attempt_renderer_init(pg)) { - g_autofree gchar *msg = g_strdup_printf( - "Switched to default renderer: %s", pg->renderer->name); - xemu_queue_notification(msg); - return; + CONFIG_DISPLAY_RENDERER attempts[CONFIG_DISPLAY_RENDERER__COUNT]; + int attempt_count = 0; + + attempts[attempt_count++] = original_renderer; + if (default_renderer != original_renderer) { + attempts[attempt_count++] = default_renderer; + } + for (int i = 0; i < CONFIG_DISPLAY_RENDERER__COUNT; i++) { + CONFIG_DISPLAY_RENDERER renderer = (CONFIG_DISPLAY_RENDERER)i; + bool already_added = false; + for (int j = 0; j < attempt_count; j++) { + if (attempts[j] == renderer) { + already_added = true; + break; + } + } + if (!already_added && renderers[renderer]) { + attempts[attempt_count++] = renderer; } } - // FIXME: Try others + for (int i = 0; i < attempt_count; i++) { + g_config.display.renderer = attempts[i]; + if (attempt_renderer_init(pg)) { + if (attempts[i] != original_renderer) { + g_autofree gchar *msg = g_strdup_printf( + "Switched renderer to %s", pg->renderer->name); + xemu_queue_notification(msg); + } + return; + } + } fprintf(stderr, "Fatal error: cannot initialize renderer\n"); exit(1); diff --git a/hw/xbox/nv2a/pgraph/vk/buffer.c b/hw/xbox/nv2a/pgraph/vk/buffer.c index 3993048235..f8b7bf35b5 100644 --- a/hw/xbox/nv2a/pgraph/vk/buffer.c +++ b/hw/xbox/nv2a/pgraph/vk/buffer.c @@ -19,7 +19,26 @@ #include "renderer.h" -static void create_buffer(PGRAPHState *pg, StorageBuffer *buffer) +#ifdef __ANDROID__ +#include +#endif + +static const char *const buffer_names[BUFFER_COUNT] = { + "BUFFER_STAGING_DST", + "BUFFER_STAGING_SRC", + "BUFFER_COMPUTE_DST", + "BUFFER_COMPUTE_SRC", + "BUFFER_INDEX", + "BUFFER_INDEX_STAGING", + "BUFFER_VERTEX_RAM", + "BUFFER_VERTEX_INLINE", + "BUFFER_VERTEX_INLINE_STAGING", + "BUFFER_UNIFORM", + "BUFFER_UNIFORM_STAGING", +}; + +static bool create_buffer(PGRAPHState *pg, StorageBuffer *buffer, + const char *name, Error **errp) { PGRAPHVkState *r = pg->vk_renderer_state; @@ -29,41 +48,75 @@ static void create_buffer(PGRAPHState *pg, StorageBuffer *buffer) .usage = buffer->usage, .sharingMode = VK_SHARING_MODE_EXCLUSIVE, }; - VK_CHECK(vmaCreateBuffer(r->allocator, &buffer_create_info, - &buffer->alloc_info, &buffer->buffer, - &buffer->allocation, NULL)); + VkResult result = vmaCreateBuffer(r->allocator, &buffer_create_info, + &buffer->alloc_info, &buffer->buffer, + &buffer->allocation, NULL); + if (result != VK_SUCCESS) { + error_setg(errp, "Failed to create Vulkan buffer %s (%zu bytes): %d", + name, buffer->buffer_size, result); + return false; + } + return true; } static void destroy_buffer(PGRAPHState *pg, StorageBuffer *buffer) { PGRAPHVkState *r = pg->vk_renderer_state; + if (buffer->buffer == VK_NULL_HANDLE && buffer->allocation == VK_NULL_HANDLE) { + return; + } vmaDestroyBuffer(r->allocator, buffer->buffer, buffer->allocation); buffer->buffer = VK_NULL_HANDLE; buffer->allocation = VK_NULL_HANDLE; } -void pgraph_vk_init_buffers(NV2AState *d) +bool pgraph_vk_init_buffers(NV2AState *d, Error **errp) { PGRAPHState *pg = &d->pgraph; PGRAPHVkState *r = pg->vk_renderer_state; // FIXME: Profile buffer sizes + const size_t mib = 1024 * 1024; + size_t vram_size = memory_region_size(d->vram); + size_t staging_size = vram_size; + if (staging_size < (16 * mib)) { + staging_size = 16 * mib; + } + size_t compute_size = vram_size * 2; + if (compute_size < (64 * mib)) { + compute_size = 64 * mib; + } +#ifdef __ANDROID__ + if (compute_size > (64 * mib)) { + compute_size = 64 * mib; + } +#else + if (compute_size > (256 * mib)) { + compute_size = 256 * mib; + } +#endif + +#ifdef __ANDROID__ + __android_log_print(ANDROID_LOG_INFO, "xemu-android", + "vk buffer init: vram=%zu staging=%zu compute=%zu", + vram_size, staging_size, compute_size); +#endif + VmaAllocationCreateInfo host_alloc_create_info = { .usage = VMA_MEMORY_USAGE_AUTO_PREFER_HOST, - .flags = VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT | - VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT, + .flags = VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT, }; VmaAllocationCreateInfo device_alloc_create_info = { .usage = VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE, - .flags = VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT + .flags = 0, }; r->storage_buffers[BUFFER_STAGING_DST] = (StorageBuffer){ .alloc_info = host_alloc_create_info, .usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT, - .buffer_size = 4096 * 4096 * 4, + .buffer_size = staging_size, }; r->storage_buffers[BUFFER_STAGING_SRC] = (StorageBuffer){ @@ -76,7 +129,7 @@ void pgraph_vk_init_buffers(NV2AState *d) .alloc_info = device_alloc_create_info, .usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, - .buffer_size = (1024 * 10) * (1024 * 10) * 8, + .buffer_size = compute_size, }; r->storage_buffers[BUFFER_COMPUTE_SRC] = (StorageBuffer){ @@ -108,6 +161,10 @@ void pgraph_vk_init_buffers(NV2AState *d) r->bitmap_size = memory_region_size(d->vram) / 4096; r->uploaded_bitmap = bitmap_new(r->bitmap_size); + if (!r->uploaded_bitmap) { + error_setg(errp, "Failed to allocate uploaded surface bitmap"); + return false; + } bitmap_clear(r->uploaded_bitmap, 0, r->bitmap_size); r->storage_buffers[BUFFER_VERTEX_INLINE] = (StorageBuffer){ @@ -138,7 +195,14 @@ void pgraph_vk_init_buffers(NV2AState *d) }; for (int i = 0; i < BUFFER_COUNT; i++) { - create_buffer(pg, &r->storage_buffers[i]); +#ifdef __ANDROID__ + __android_log_print(ANDROID_LOG_INFO, "xemu-android", + "vk buffer init: create %s size=%zu", + buffer_names[i], r->storage_buffers[i].buffer_size); +#endif + if (!create_buffer(pg, &r->storage_buffers[i], buffer_names[i], errp)) { + goto fail; + } } // FIXME: Add fallback path for device using host mapped memory @@ -149,12 +213,33 @@ void pgraph_vk_init_buffers(NV2AState *d) BUFFER_UNIFORM_STAGING }; for (int i = 0; i < ARRAY_SIZE(buffers_to_map); i++) { - VK_CHECK(vmaMapMemory( - r->allocator, r->storage_buffers[buffers_to_map[i]].allocation, - (void **)&r->storage_buffers[buffers_to_map[i]].mapped)); + int idx = buffers_to_map[i]; + VkResult result = vmaMapMemory( + r->allocator, r->storage_buffers[idx].allocation, + (void **)&r->storage_buffers[idx].mapped); + if (result != VK_SUCCESS) { + error_setg(errp, "Failed to map Vulkan buffer %s (%zu bytes): %d", + buffer_names[idx], r->storage_buffers[idx].buffer_size, + result); + goto fail; + } } pgraph_prim_rewrite_init(&r->prim_rewrite_buf); + return true; + +fail: + for (int i = 0; i < BUFFER_COUNT; i++) { + if (r->storage_buffers[i].mapped) { + vmaUnmapMemory(r->allocator, r->storage_buffers[i].allocation); + r->storage_buffers[i].mapped = NULL; + } + destroy_buffer(pg, &r->storage_buffers[i]); + } + g_free(r->uploaded_bitmap); + r->uploaded_bitmap = NULL; + r->bitmap_size = 0; + return false; } void pgraph_vk_finalize_buffers(NV2AState *d) diff --git a/hw/xbox/nv2a/pgraph/vk/display.c b/hw/xbox/nv2a/pgraph/vk/display.c index 1c17dbebc7..de9b3ec9bf 100644 --- a/hw/xbox/nv2a/pgraph/vk/display.c +++ b/hw/xbox/nv2a/pgraph/vk/display.c @@ -590,12 +590,12 @@ static void destroy_current_display_image(PGRAPHState *pg) destroy_frame_buffer(pg); #if HAVE_EXTERNAL_MEMORY - if (p_glDeleteMemoryObjectsEXT) { + if (d->gl_texture_id && p_glDeleteMemoryObjectsEXT) { glDeleteTextures(1, &d->gl_texture_id); } d->gl_texture_id = 0; - if (p_glDeleteMemoryObjectsEXT) { + if (d->gl_memory_obj && p_glDeleteMemoryObjectsEXT) { p_glDeleteMemoryObjectsEXT(1, &d->gl_memory_obj); } d->gl_memory_obj = 0; @@ -630,24 +630,33 @@ static bool create_display_image(PGRAPHState *pg, int width, int height) destroy_current_display_image(pg); } +#if HAVE_EXTERNAL_MEMORY const GLint gl_internal_format = GL_RGBA8; +#endif bool use_optimal_tiling = true; +#if HAVE_EXTERNAL_MEMORY + bool use_external_memory = d->use_external_memory; +#else + bool use_external_memory = false; +#endif #if HAVE_EXTERNAL_MEMORY - GLint num_tiling_types; - glGetInternalformativ(GL_TEXTURE_2D, gl_internal_format, - GL_NUM_TILING_TYPES_EXT, 1, &num_tiling_types); - // XXX: Apparently on AMD GL_OPTIMAL_TILING_EXT is reported to be - // supported, but doesn't work? On nVidia, GL_LINEAR_TILING_EXT may not - // be supported so we must use optimal. Default to optimal unless - // linear is explicitly specified... - GLint tiling_types[num_tiling_types]; - glGetInternalformativ(GL_TEXTURE_2D, gl_internal_format, - GL_TILING_TYPES_EXT, num_tiling_types, tiling_types); - for (int i = 0; i < num_tiling_types; i++) { - if (tiling_types[i] == GL_LINEAR_TILING_EXT) { - use_optimal_tiling = false; - break; + if (use_external_memory) { + GLint num_tiling_types; + glGetInternalformativ(GL_TEXTURE_2D, gl_internal_format, + GL_NUM_TILING_TYPES_EXT, 1, &num_tiling_types); + // XXX: Apparently on AMD GL_OPTIMAL_TILING_EXT is reported to be + // supported, but doesn't work? On nVidia, GL_LINEAR_TILING_EXT may not + // be supported so we must use optimal. Default to optimal unless + // linear is explicitly specified... + GLint tiling_types[num_tiling_types]; + glGetInternalformativ(GL_TEXTURE_2D, gl_internal_format, + GL_TILING_TYPES_EXT, num_tiling_types, tiling_types); + for (int i = 0; i < num_tiling_types; i++) { + if (tiling_types[i] == GL_LINEAR_TILING_EXT) { + use_optimal_tiling = false; + break; + } } } #endif @@ -683,7 +692,11 @@ static bool create_display_image(PGRAPHState *pg, int width, int height) .handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT_KHR, #endif }; - image_create_info.pNext = &external_memory_image_create_info; + if (use_external_memory) { + image_create_info.pNext = &external_memory_image_create_info; + } else { + image_create_info.pNext = NULL; + } VkResult result = vkCreateImage(r->device, &image_create_info, NULL, &d->image); if (result != VK_SUCCESS) { @@ -739,15 +752,18 @@ static bool create_display_image(PGRAPHState *pg, int width, int height) #endif , }; - void *alloc_p_next = &export_memory_alloc_info; + void *alloc_p_next = NULL; VkMemoryDedicatedAllocateInfo dedicated_alloc_info = { .sType = VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO, .image = d->image, }; if (dedicated_requirements.requiresDedicatedAllocation == VK_TRUE) { - dedicated_alloc_info.pNext = alloc_p_next; alloc_p_next = &dedicated_alloc_info; } + if (use_external_memory) { + export_memory_alloc_info.pNext = alloc_p_next; + alloc_p_next = &export_memory_alloc_info; + } alloc_info.pNext = alloc_p_next; result = vkAllocateMemory(r->device, &alloc_info, NULL, &d->memory); @@ -793,17 +809,13 @@ static bool create_display_image(PGRAPHState *pg, int width, int height) } #if HAVE_EXTERNAL_MEMORY - if (!load_gl_external_memory_symbols()) { + if (use_external_memory && !load_gl_external_memory_symbols()) { fprintf(stderr, "Vulkan display: GL_EXT_memory_object not available\n"); - vkDestroyImageView(r->device, d->image_view, NULL); - d->image_view = VK_NULL_HANDLE; - vkFreeMemory(r->device, d->memory, NULL); - d->memory = VK_NULL_HANDLE; - vkDestroyImage(r->device, d->image, NULL); - d->image = VK_NULL_HANDLE; - return false; + d->use_external_memory = false; + use_external_memory = false; } + if (use_external_memory) { #ifdef WIN32 VkMemoryGetWin32HandleInfoKHR handle_info = { @@ -872,6 +884,7 @@ static bool create_display_image(PGRAPHState *pg, int width, int height) d->image = VK_NULL_HANDLE; return false; } + } #endif // HAVE_EXTERNAL_MEMORY diff --git a/hw/xbox/nv2a/pgraph/vk/instance.c b/hw/xbox/nv2a/pgraph/vk/instance.c index 2a2f6441d3..0f30b685f3 100644 --- a/hw/xbox/nv2a/pgraph/vk/instance.c +++ b/hw/xbox/nv2a/pgraph/vk/instance.c @@ -179,6 +179,12 @@ static bool create_instance(PGRAPHState *pg, Error **errp) add_optional_instance_extension_names(pg, available_extensions, enabled_extension_names); + const char *const *enabled_instance_extension_names = NULL; + if (enabled_extension_names->len > 0) { + enabled_instance_extension_names = + &g_array_index(enabled_extension_names, const char *, 0); + } + fprintf(stderr, "Enabled instance extensions:\n"); for (int i = 0; i < enabled_extension_names->len; i++) { fprintf(stderr, "- %s\n", @@ -189,8 +195,7 @@ static bool create_instance(PGRAPHState *pg, Error **errp) .sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO, .pApplicationInfo = &app_info, .enabledExtensionCount = enabled_extension_names->len, - .ppEnabledExtensionNames = - &g_array_index(enabled_extension_names, const char *, 0), + .ppEnabledExtensionNames = enabled_instance_extension_names, }; enable_validation = g_config.display.vulkan.validation_layers; @@ -445,6 +450,12 @@ static bool create_logical_device(PGRAPHState *pg, Error **errp) add_optional_device_extension_names(pg, available_extensions, enabled_extension_names); + const char *const *enabled_device_extension_names = NULL; + if (enabled_extension_names->len > 0) { + enabled_device_extension_names = + &g_array_index(enabled_extension_names, const char *, 0); + } + fprintf(stderr, "Enabled device extensions:\n"); for (int i = 0; i < enabled_extension_names->len; i++) { fprintf(stderr, "- %s\n", @@ -557,8 +568,7 @@ static bool create_logical_device(PGRAPHState *pg, Error **errp) .pQueueCreateInfos = &queue_create_info, .pEnabledFeatures = &r->enabled_physical_device_features, .enabledExtensionCount = enabled_extension_names->len, - .ppEnabledExtensionNames = - &g_array_index(enabled_extension_names, const char *, 0), + .ppEnabledExtensionNames = enabled_device_extension_names, .pNext = next_struct, }; @@ -567,12 +577,20 @@ static bool create_logical_device(PGRAPHState *pg, Error **errp) device_create_info.ppEnabledLayerNames = validation_layers; } +#ifdef __ANDROID__ + __android_log_print(ANDROID_LOG_INFO, "xemu-android", + "vk init stage: vkCreateDevice"); +#endif result = vkCreateDevice(r->physical_device, &device_create_info, NULL, &r->device); if (result != VK_SUCCESS) { error_setg(errp, "Failed to create logical device (%d)", result); return false; } +#ifdef __ANDROID__ + __android_log_print(ANDROID_LOG_INFO, "xemu-android", + "vk init stage: vkCreateDevice done"); +#endif vkGetDeviceQueue(r->device, indices.queue_family, 0, &r->queue); return true; @@ -648,21 +666,64 @@ static bool init_allocator(PGRAPHState *pg, Error **errp) .pVulkanFunctions = &vulkanFunctions, }; +#ifdef __ANDROID__ + __android_log_print(ANDROID_LOG_INFO, "xemu-android", + "vk init stage: vmaCreateAllocator"); +#endif result = vmaCreateAllocator(&create_info, &r->allocator); if (result != VK_SUCCESS) { error_setg(errp, "vmaCreateAllocator failed"); return false; } +#ifdef __ANDROID__ + __android_log_print(ANDROID_LOG_INFO, "xemu-android", + "vk init stage: vmaCreateAllocator done"); +#endif return true; } void pgraph_vk_init_instance(PGRAPHState *pg, Error **errp) { - if (create_instance(pg, errp) && - select_physical_device(pg, errp) && - create_logical_device(pg, errp) && - init_allocator(pg, errp)) { + bool ok = false; + +#ifdef __ANDROID__ + __android_log_print(ANDROID_LOG_INFO, "xemu-android", + "vk init stage: create_instance"); +#endif + if (!create_instance(pg, errp)) { + goto done; + } +#ifdef __ANDROID__ + __android_log_print(ANDROID_LOG_INFO, "xemu-android", + "vk init stage: select_physical_device"); +#endif + if (!select_physical_device(pg, errp)) { + goto done; + } +#ifdef __ANDROID__ + __android_log_print(ANDROID_LOG_INFO, "xemu-android", + "vk init stage: create_logical_device"); +#endif + if (!create_logical_device(pg, errp)) { + goto done; + } +#ifdef __ANDROID__ + __android_log_print(ANDROID_LOG_INFO, "xemu-android", + "vk init stage: init_allocator"); +#endif + if (!init_allocator(pg, errp)) { + goto done; + } + + ok = true; + +done: + if (ok) { +#ifdef __ANDROID__ + __android_log_print(ANDROID_LOG_INFO, "xemu-android", + "vk init stage: complete"); +#endif return; } diff --git a/hw/xbox/nv2a/pgraph/vk/renderer.c b/hw/xbox/nv2a/pgraph/vk/renderer.c index c3184ba434..ba0d079677 100644 --- a/hw/xbox/nv2a/pgraph/vk/renderer.c +++ b/hw/xbox/nv2a/pgraph/vk/renderer.c @@ -52,6 +52,7 @@ static void pgraph_vk_init(NV2AState *d, Error **errp) pg->vk_renderer_state = (PGRAPHVkState *)g_malloc0(sizeof(PGRAPHVkState)); #if HAVE_EXTERNAL_MEMORY + bool use_external_memory = false; #ifdef __ANDROID__ if (!g_gl_context) { // On Android, we need to ensure the main GL context is accessible @@ -65,15 +66,22 @@ static void pgraph_vk_init(NV2AState *d, Error **errp) } } #endif - if (!g_gl_context) { - error_setg(errp, "Failed to create GL offscreen context for Vulkan display"); - return; + if (g_gl_context) { + glo_set_current(g_gl_context); + use_external_memory = pgraph_vk_gl_external_memory_available(); } - glo_set_current(g_gl_context); - if (!pgraph_vk_gl_external_memory_available()) { - error_setg(errp, "GL_EXT_memory_object not available for Vulkan display"); - return; + if (!use_external_memory) { +#ifdef __ANDROID__ + __android_log_print(ANDROID_LOG_WARN, "xemu-android", + "pgraph_vk_init: external memory interop unavailable, using download fallback"); +#endif } +#ifdef __ANDROID__ + __android_log_print(ANDROID_LOG_INFO, "xemu-android", + "pgraph_vk_init: external memory interop=%s", + use_external_memory ? "enabled" : "disabled"); +#endif + pg->vk_renderer_state->display.use_external_memory = use_external_memory; #endif pgraph_vk_debug_init(); @@ -83,19 +91,66 @@ static void pgraph_vk_init(NV2AState *d, Error **errp) return; } +#ifdef __ANDROID__ + __android_log_print(ANDROID_LOG_INFO, "xemu-android", + "vk init stage: command_buffers"); +#endif pgraph_vk_init_command_buffers(pg); - pgraph_vk_init_buffers(d); +#ifdef __ANDROID__ + __android_log_print(ANDROID_LOG_INFO, "xemu-android", + "vk init stage: buffers"); +#endif + if (!pgraph_vk_init_buffers(d, errp)) { +#ifdef __ANDROID__ + __android_log_print(ANDROID_LOG_ERROR, "xemu-android", + "vk init stage: buffers failed"); +#endif + return; + } +#ifdef __ANDROID__ + __android_log_print(ANDROID_LOG_INFO, "xemu-android", + "vk init stage: surfaces"); +#endif pgraph_vk_init_surfaces(pg); +#ifdef __ANDROID__ + __android_log_print(ANDROID_LOG_INFO, "xemu-android", + "vk init stage: shaders"); +#endif pgraph_vk_init_shaders(pg); +#ifdef __ANDROID__ + __android_log_print(ANDROID_LOG_INFO, "xemu-android", + "vk init stage: pipelines"); +#endif pgraph_vk_init_pipelines(pg); +#ifdef __ANDROID__ + __android_log_print(ANDROID_LOG_INFO, "xemu-android", + "vk init stage: textures"); +#endif pgraph_vk_init_textures(pg); +#ifdef __ANDROID__ + __android_log_print(ANDROID_LOG_INFO, "xemu-android", + "vk init stage: reports"); +#endif pgraph_vk_init_reports(pg); +#ifdef __ANDROID__ + __android_log_print(ANDROID_LOG_INFO, "xemu-android", + "vk init stage: compute"); +#endif pgraph_vk_init_compute(pg); +#ifdef __ANDROID__ + __android_log_print(ANDROID_LOG_INFO, "xemu-android", + "vk init stage: display"); +#endif pgraph_vk_init_display(pg); pgraph_vk_update_vertex_ram_buffer(&d->pgraph, 0, d->vram_ptr, memory_region_size(d->vram)); +#ifdef __ANDROID__ + __android_log_print(ANDROID_LOG_INFO, "xemu-android", + "vk init stage: renderer_ready"); +#endif + } static void pgraph_vk_finalize(NV2AState *d) @@ -146,7 +201,13 @@ static void pgraph_vk_flush(NV2AState *d) static void pgraph_vk_sync(NV2AState *d) { PGRAPHState *pg = &d->pgraph; +#if HAVE_EXTERNAL_MEMORY + if (pg->vk_renderer_state->display.use_external_memory) { + pgraph_vk_render_display(pg); + } +#else pgraph_vk_render_display(pg); +#endif qatomic_set(&d->pgraph.sync_pending, false); qemu_event_set(&d->pgraph.sync_complete); @@ -230,12 +291,17 @@ static int pgraph_vk_get_framebuffer_surface(NV2AState *d) surface->frame_time = pg->frame_time; #if HAVE_EXTERNAL_MEMORY - qemu_event_reset(&d->pgraph.sync_complete); - qatomic_set(&pg->sync_pending, true); - pfifo_kick(d); + if (r->display.use_external_memory) { + qemu_event_reset(&d->pgraph.sync_complete); + qatomic_set(&pg->sync_pending, true); + pfifo_kick(d); + qemu_mutex_unlock(&d->pfifo.lock); + qemu_event_wait(&d->pgraph.sync_complete); + return r->display.gl_texture_id; + } qemu_mutex_unlock(&d->pfifo.lock); - qemu_event_wait(&d->pgraph.sync_complete); - return r->display.gl_texture_id; + pgraph_vk_wait_for_surface_download(surface); + return 0; #else qemu_mutex_unlock(&d->pfifo.lock); pgraph_vk_wait_for_surface_download(surface); diff --git a/hw/xbox/nv2a/pgraph/vk/renderer.h b/hw/xbox/nv2a/pgraph/vk/renderer.h index 16a26f6117..66cb6bb084 100644 --- a/hw/xbox/nv2a/pgraph/vk/renderer.h +++ b/hw/xbox/nv2a/pgraph/vk/renderer.h @@ -286,6 +286,7 @@ typedef struct PGRAPHVkDisplayState { int width, height; int draw_time; + bool use_external_memory; // OpenGL Interop #ifdef WIN32 @@ -470,7 +471,7 @@ void pgraph_vk_unref_shader_module(PGRAPHVkState *r, ShaderModuleInfo *info); void pgraph_vk_destroy_shader_module(PGRAPHVkState *r, ShaderModuleInfo *info); // buffer.c -void pgraph_vk_init_buffers(NV2AState *d); +bool pgraph_vk_init_buffers(NV2AState *d, Error **errp); void pgraph_vk_finalize_buffers(NV2AState *d); bool pgraph_vk_buffer_has_space_for(PGRAPHState *pg, int index, VkDeviceSize size, diff --git a/hw/xbox/nv2a/pgraph/vk/surface.c b/hw/xbox/nv2a/pgraph/vk/surface.c index f59c689251..f10e78c55f 100644 --- a/hw/xbox/nv2a/pgraph/vk/surface.c +++ b/hw/xbox/nv2a/pgraph/vk/surface.c @@ -496,8 +496,19 @@ static void download_surface(NV2AState *d, SurfaceBinding *surface, bool force) void pgraph_vk_wait_for_surface_download(SurfaceBinding *surface) { NV2AState *d = g_nv2a; + bool require_download = qatomic_read(&surface->draw_dirty); - if (qatomic_read(&surface->draw_dirty)) { +#ifdef __ANDROID__ + /* + * Android Vulkan currently presents through the CPU/VGA fallback path. + * Force framebuffer surface download so fallback upload sees fresh pixels. + */ + if (!d->pgraph.vk_renderer_state->display.use_external_memory) { + require_download = true; + } +#endif + + if (require_download) { qemu_mutex_lock(&d->pfifo.lock); qemu_event_reset(&d->pgraph.vk_renderer_state->downloads_complete); qatomic_set(&surface->download_pending, true); @@ -1699,7 +1710,11 @@ void pgraph_vk_init_surfaces(PGRAPHState *pg) bool color_formats_supported = check_surface_internal_formats_supported( r, kelvin_surface_color_format_vk_map, ARRAY_SIZE(kelvin_surface_color_format_vk_map)); - assert(color_formats_supported); + if (!color_formats_supported) { + fprintf(stderr, + "Warning: Some Vulkan surface color formats are unsupported; " + "continuing with best-effort mapping.\n"); + } // Check if the device supports preferred VK_FORMAT_D24_UNORM_S8_UINT // format, fall back to D32_SFLOAT_S8_UINT otherwise. @@ -1713,7 +1728,11 @@ void pgraph_vk_init_surfaces(PGRAPHState *pg) r->kelvin_surface_zeta_vk_map[NV097_SET_SURFACE_FORMAT_ZETA_Z24S8] = zeta_d32_sfloat_s8_uint; } else { - assert(!"No suitable depth-stencil format supported"); + fprintf(stderr, + "Warning: No suitable Vulkan Z24S8 depth-stencil format; " + "falling back to Z16 mapping.\n"); + r->kelvin_surface_zeta_vk_map[NV097_SET_SURFACE_FORMAT_ZETA_Z24S8] = + zeta_d16; } QTAILQ_INIT(&r->surfaces); diff --git a/ui/xemu.c b/ui/xemu.c index d717931fc0..4ad3aa5869 100644 --- a/ui/xemu.c +++ b/ui/xemu.c @@ -224,7 +224,8 @@ static bool android_blit_init(void) "uniform sampler2D u_tex;\n" "out vec4 out_Color;\n" "void main() {\n" - " out_Color = texture(u_tex, v_uv);\n" + " vec4 c = texture(u_tex, v_uv);\n" + " out_Color = vec4(c.rgb, 1.0);\n" "}\n"; GLuint vshader = android_gl_compile_shader(GL_VERTEX_SHADER, vs); @@ -292,6 +293,11 @@ static void android_blit_frame(GLuint tex, bool flip) glBindFramebuffer(GL_FRAMEBUFFER, 0); glViewport(0, 0, w, h); + glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + glDisable(GL_BLEND); + glDisable(GL_DEPTH_TEST); + glDisable(GL_CULL_FACE); + glDisable(GL_SCISSOR_TEST); glUseProgram(g_android_blit_prog); glUniform1i(g_android_blit_tex_loc, 0); glUniform1i(g_android_blit_flip_loc, flip ? 1 : 0); @@ -1412,10 +1418,12 @@ void xb_surface_gl_create_texture(DisplaySurface *surface) } glBindTexture(GL_TEXTURE_2D, surface->texture); const void *pixels = surface_data(surface); + GLenum upload_format = surface->glformat; + GLenum upload_type = surface->gltype; #ifdef __ANDROID__ uint8_t *converted = NULL; bool use_row_length = true; - if (surface->glformat == GL_BGRA_EXT) { + if (upload_format == GL_BGRA_EXT) { const int width = surface_width(surface); const int height = surface_height(surface); const int stride = surface_stride(surface); @@ -1435,7 +1443,7 @@ void xb_surface_gl_create_texture(DisplaySurface *surface) dst[x * 4 + 3] = a; } } - surface->glformat = GL_RGBA; + upload_format = GL_RGBA; pixels = converted; use_row_length = false; } @@ -1449,7 +1457,7 @@ void xb_surface_gl_create_texture(DisplaySurface *surface) glTexImage2D(GL_TEXTURE_2D, 0, internal_format, surface_width(surface), surface_height(surface), - 0, surface->glformat, surface->gltype, + 0, upload_format, upload_type, pixels); #ifdef __ANDROID__ if (use_row_length) {