diff --git a/hw/xbox/mcpx/apu/apu.c b/hw/xbox/mcpx/apu/apu.c index f61ec7d96b..7860d68aed 100644 --- a/hw/xbox/mcpx/apu/apu.c +++ b/hw/xbox/mcpx/apu/apu.c @@ -302,8 +302,8 @@ static void monitor_sink_cb(void *opaque, uint8_t *stream, int free_b) if (avail >= free_b) { break; } - sleep_ns(500000); qemu_cond_broadcast(&s->cond); + sleep_ns(500000); if (!runstate_is_running()) { memset(stream, 0, free_b); return; diff --git a/hw/xbox/nv2a/pgraph/gl/display.c b/hw/xbox/nv2a/pgraph/gl/display.c index 1546755d7c..ec4aed9763 100644 --- a/hw/xbox/nv2a/pgraph/gl/display.c +++ b/hw/xbox/nv2a/pgraph/gl/display.c @@ -28,6 +28,13 @@ #include #ifdef __ANDROID__ #include +#include +/* EGL_EGLEXT_PROTOTYPES enables KHR function declarations in eglext.h. + * We use KHR variants because eglCreateSync (EGL 1.5) is only in + * libEGL.so stubs from API 29+; the KHR equivalents exist from API 21. */ +#define EGL_EGLEXT_PROTOTYPES +#include +#undef EGL_EGLEXT_PROTOTYPES #endif #ifdef __ANDROID__ @@ -537,8 +544,23 @@ static void render_display(NV2AState *d, SurfaceBinding *surface) static void gl_fence(void) { #ifdef __ANDROID__ - /* Shared-context sync objects are still producing invalid-operation - * failures on Adreno. Favor correctness over throughput here. */ + /* GL sync objects fail with GL_INVALID_OPERATION on Adreno when shared + * across EGL contexts. EGL_KHR_fence_sync operates at the EGL level and + * is not context-specific, so it is immune to this bug. + * We use the KHR variants because they are present in libEGL.so from + * API 21+; the EGL 1.5 core equivalents only appear from API 29. */ + EGLDisplay dpy = eglGetCurrentDisplay(); + if (dpy != EGL_NO_DISPLAY) { + EGLSyncKHR sync = eglCreateSyncKHR(dpy, EGL_SYNC_FENCE_KHR, NULL); + if (sync != EGL_NO_SYNC_KHR) { + eglClientWaitSyncKHR(dpy, sync, + EGL_SYNC_FLUSH_COMMANDS_BIT_KHR, + EGL_FOREVER_KHR); + eglDestroySyncKHR(dpy, sync); + return; + } + } + /* Fallback: EGL sync unavailable */ glFinish(); #else GLsync fence = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); diff --git a/hw/xbox/nv2a/pgraph/gl/renderer.h b/hw/xbox/nv2a/pgraph/gl/renderer.h index 872098b813..a361c04122 100644 --- a/hw/xbox/nv2a/pgraph/gl/renderer.h +++ b/hw/xbox/nv2a/pgraph/gl/renderer.h @@ -196,6 +196,13 @@ typedef struct PGRAPHGLState { #ifdef __ANDROID__ GLuint gl_download_pbo; size_t gl_download_pbo_size; + + /* Persistent scratch buffers for RGBA8 format conversion. + * Avoids per-frame g_malloc/g_free in surface upload/download hot paths. */ + uint8_t *android_conv_buf; /* upload conversion (pgraph_gl_upload_surface_data) */ + size_t android_conv_buf_size; + uint8_t *android_s2t_conv_buf; /* render-to-texture conversion */ + size_t android_s2t_conv_buf_size; #endif TextureBinding *texture_binding[NV2A_MAX_TEXTURES]; diff --git a/hw/xbox/nv2a/pgraph/gl/surface.c b/hw/xbox/nv2a/pgraph/gl/surface.c index e2e027e04b..440b55fc5d 100644 --- a/hw/xbox/nv2a/pgraph/gl/surface.c +++ b/hw/xbox/nv2a/pgraph/gl/surface.c @@ -28,6 +28,9 @@ #ifdef __ANDROID__ #include +#ifdef __aarch64__ +#include +#endif #endif #ifdef __ANDROID__ @@ -211,22 +214,50 @@ static void android_surface_guest_to_rgba8(const SurfaceBinding *surface, break; case NV097_SET_SURFACE_FORMAT_COLOR_LE_X8R8G8B8_Z8R8G8B8: case NV097_SET_SURFACE_FORMAT_COLOR_LE_A8R8G8B8: + { + bool preserve_alpha = (surface->shape.color_format == + NV097_SET_SURFACE_FORMAT_COLOR_LE_A8R8G8B8); for (y = 0; y < height; y++) { const uint8_t *src_row = src + y * src_stride; uint8_t *dst_row = dst + y * width * 4; +#ifdef __aarch64__ + /* vqtbl1q_u8: 16-byte shuffle, processes 4 pixels per instruction. + * Permutation swaps R (byte 2) and B (byte 0) within each pixel. */ + static const uint8_t perm_arr[16] = + {2,1,0,3, 6,5,4,7, 10,9,8,11, 14,13,12,15}; + uint8x16_t vperm = vld1q_u8(perm_arr); + /* For X8R8G8B8, force alpha=0xFF by ORing a pre-built mask. */ + static const uint8_t alpha_mask_arr[16] = + {0,0,0,0xFF, 0,0,0,0xFF, 0,0,0,0xFF, 0,0,0,0xFF}; + uint8x16_t valpha_mask = preserve_alpha + ? vdupq_n_u8(0) + : vld1q_u8(alpha_mask_arr); + unsigned int px = width; + while (px >= 4) { + uint8x16_t v = vqtbl1q_u8(vld1q_u8(src_row), vperm); + vst1q_u8(dst_row, vorrq_u8(v, valpha_mask)); + src_row += 16; dst_row += 16; px -= 4; + } + /* scalar tail for widths not divisible by 4 */ + while (px-- > 0) { + dst_row[0] = src_row[2]; + dst_row[1] = src_row[1]; + dst_row[2] = src_row[0]; + dst_row[3] = preserve_alpha ? src_row[3] : 0xFF; + src_row += 4; dst_row += 4; + } +#else for (x = 0; x < width; x++) { const uint8_t *pixel = src_row + x * 4; dst_row[x * 4 + 0] = pixel[2]; dst_row[x * 4 + 1] = pixel[1]; dst_row[x * 4 + 2] = pixel[0]; - dst_row[x * 4 + 3] = - (surface->shape.color_format == - NV097_SET_SURFACE_FORMAT_COLOR_LE_A8R8G8B8) - ? pixel[3] - : 0xFF; + dst_row[x * 4 + 3] = preserve_alpha ? pixel[3] : 0xFF; } +#endif } break; + } default: g_assert_not_reached(); } @@ -867,20 +898,24 @@ static void render_surface_to_texture_slow(NV2AState *d, #ifdef __ANDROID__ if (android_surface_to_texture_rgba8_compatible(surface, texture_shape)) { - uint8_t *upload_tmp = g_malloc(width * height * 4); + PGRAPHGLState *r = pg->gl_renderer_state; + size_t needed = (size_t)width * height * 4; + if (needed > r->android_s2t_conv_buf_size) { + r->android_s2t_conv_buf = g_realloc(r->android_s2t_conv_buf, needed); + r->android_s2t_conv_buf_size = needed; + } if (android_surface_to_texture_needs_guest_reinterpretation( surface, texture_shape)) { android_surface_guest_to_texture_rgba8( texture_shape, buf, width, height, - width * surface->fmt.bytes_per_pixel, upload_tmp); + width * surface->fmt.bytes_per_pixel, r->android_s2t_conv_buf); } else { android_surface_guest_to_rgba8(surface, buf, width, height, width * surface->fmt.bytes_per_pixel, - upload_tmp); + r->android_s2t_conv_buf); } glTexImage2D(texture->gl_target, 0, GL_RGBA8, width, height, 0, - GL_RGBA, GL_UNSIGNED_BYTE, upload_tmp); - g_free(upload_tmp); + GL_RGBA, GL_UNSIGNED_BYTE, r->android_s2t_conv_buf); } else #endif glTexImage2D(texture->gl_target, 0, f->gl_internal_format, width, height, 0, @@ -2068,8 +2103,8 @@ void pgraph_gl_upload_surface_data(NV2AState *d, SurfaceBinding *surface, glBindTexture(GL_TEXTURE_2D, surface->gl_buffer); #ifdef __ANDROID__ { + PGRAPHGLState *r = pg->gl_renderer_state; const uint8_t *upload_buf = gl_read_buf; - uint8_t *upload_tmp = NULL; GLint upload_ifmt; GLenum upload_fmt; GLenum upload_type; @@ -2077,15 +2112,18 @@ void pgraph_gl_upload_surface_data(NV2AState *d, SurfaceBinding *surface, android_surface_get_storage_format(surface, &upload_ifmt, &upload_fmt, &upload_type); if (android_surface_uses_rgba8_transfer(surface)) { - upload_tmp = g_malloc(width * height * 4); + size_t needed = (size_t)width * height * 4; + if (needed > r->android_conv_buf_size) { + r->android_conv_buf = g_realloc(r->android_conv_buf, needed); + r->android_conv_buf_size = needed; + } android_surface_guest_to_rgba8(surface, gl_read_buf, width, height, width * surface->fmt.bytes_per_pixel, - upload_tmp); - upload_buf = upload_tmp; + r->android_conv_buf); + upload_buf = r->android_conv_buf; } glTexImage2D(GL_TEXTURE_2D, 0, upload_ifmt, width, height, 0, upload_fmt, upload_type, upload_buf); - g_free(upload_tmp); } #else glTexImage2D(GL_TEXTURE_2D, 0, surface->fmt.gl_internal_format, width, @@ -2577,6 +2615,10 @@ void pgraph_gl_init_surfaces(PGRAPHState *pg) #ifdef __ANDROID__ glGenBuffers(1, &r->gl_download_pbo); r->gl_download_pbo_size = 0; + r->android_conv_buf = NULL; + r->android_conv_buf_size = 0; + r->android_s2t_conv_buf = NULL; + r->android_s2t_conv_buf_size = 0; #endif QTAILQ_INIT(&r->surfaces); r->downloads_pending = false; @@ -2622,6 +2664,12 @@ void pgraph_gl_finalize_surfaces(PGRAPHState *pg) r->gl_download_pbo = 0; r->gl_download_pbo_size = 0; } + g_free(r->android_conv_buf); + r->android_conv_buf = NULL; + r->android_conv_buf_size = 0; + g_free(r->android_s2t_conv_buf); + r->android_s2t_conv_buf = NULL; + r->android_s2t_conv_buf_size = 0; #endif finalize_render_to_texture(pg); diff --git a/ui/xemu.c b/ui/xemu.c index 2e66bf451b..f68d74e7e1 100644 --- a/ui/xemu.c +++ b/ui/xemu.c @@ -62,6 +62,9 @@ #ifdef __ANDROID__ #include +#ifdef __aarch64__ +#include +#endif #endif #ifdef _WIN32 #include "nvapi.h" @@ -1455,28 +1458,49 @@ void xb_surface_gl_create_texture(DisplaySurface *surface) uint8_t *converted = NULL; bool use_row_length = true; if (upload_format == GL_BGRA_EXT) { - const int width = surface_width(surface); - const int height = surface_height(surface); - const int stride = surface_stride(surface); - const uint8_t *src = (const uint8_t *)surface_data(surface); - converted = g_malloc((size_t)width * height * 4); - for (int y = 0; y < height; ++y) { - const uint8_t *row = src + (size_t)y * stride; - uint8_t *dst = converted + (size_t)y * width * 4; - for (int x = 0; x < width; ++x) { - const uint8_t b = row[x * 4 + 0]; - const uint8_t g = row[x * 4 + 1]; - const uint8_t r = row[x * 4 + 2]; - const uint8_t a = row[x * 4 + 3]; - dst[x * 4 + 0] = r; - dst[x * 4 + 1] = g; - dst[x * 4 + 2] = b; - dst[x * 4 + 3] = a; + if (g_android_gl_bgra_supported) { + /* GL_EXT_texture_format_BGRA8888: upload native BGRA with no CPU + * work. The spec requires internalformat == GL_BGRA_EXT too. */ + internal_format = GL_BGRA_EXT; + /* upload_format stays GL_BGRA_EXT, use_row_length stays true */ + } else { + /* Fallback: swizzle BGRA→RGBA on CPU before upload */ + const int width = surface_width(surface); + const int height = surface_height(surface); + const int stride = surface_stride(surface); + const uint8_t *src = (const uint8_t *)surface_data(surface); + converted = g_malloc((size_t)width * height * 4); + for (int y = 0; y < height; ++y) { + const uint8_t *row = src + (size_t)y * stride; + uint8_t *dst = converted + (size_t)y * width * 4; +#ifdef __aarch64__ + /* vqtbl1q_u8: 16-byte table lookup, processes 4 pixels/cycle */ + static const uint8_t perm_arr[16] = + {2,1,0,3, 6,5,4,7, 10,9,8,11, 14,13,12,15}; + uint8x16_t vperm = vld1q_u8(perm_arr); + int px = width; + while (px >= 4) { + vst1q_u8(dst, vqtbl1q_u8(vld1q_u8(row), vperm)); + row += 16; dst += 16; px -= 4; + } + while (px-- > 0) { + dst[0] = row[2]; dst[1] = row[1]; + dst[2] = row[0]; dst[3] = row[3]; + row += 4; dst += 4; + } +#else + for (int x = 0; x < width; ++x) { + dst[x * 4 + 0] = row[x * 4 + 2]; + dst[x * 4 + 1] = row[x * 4 + 1]; + dst[x * 4 + 2] = row[x * 4 + 0]; + dst[x * 4 + 3] = row[x * 4 + 3]; + } +#endif } + upload_format = GL_RGBA; + pixels = converted; + use_row_length = false; } - upload_format = GL_RGBA; - pixels = converted; - use_row_length = false; } if (use_row_length) { #endif