mirror of
https://github.com/izzy2lost/xemu.git
synced 2026-07-06 00:20:22 -07:00
a little more stable and a little better performance.
This commit is contained in:
@@ -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()
|
||||
|
||||
@@ -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")) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -19,7 +19,26 @@
|
||||
|
||||
#include "renderer.h"
|
||||
|
||||
static void create_buffer(PGRAPHState *pg, StorageBuffer *buffer)
|
||||
#ifdef __ANDROID__
|
||||
#include <android/log.h>
|
||||
#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)
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user