From b021926d11e1f57259cb80f83d66d18d926e4897 Mon Sep 17 00:00:00 2001 From: bunnei Date: Sat, 7 Mar 2020 21:07:46 -0500 Subject: [PATCH] android: emu_window: Adapt for use with split presenter thread. --- src/android/app/src/main/jni/config.cpp | 2 +- .../src/main/jni/emu_window/emu_window.cpp | 187 ++++++++++++------ .../app/src/main/jni/emu_window/emu_window.h | 37 +++- src/android/app/src/main/jni/native.cpp | 4 + 4 files changed, 166 insertions(+), 64 deletions(-) diff --git a/src/android/app/src/main/jni/config.cpp b/src/android/app/src/main/jni/config.cpp index 170443cdb..6229cc39a 100644 --- a/src/android/app/src/main/jni/config.cpp +++ b/src/android/app/src/main/jni/config.cpp @@ -110,7 +110,7 @@ void Config::ReadValues() { Settings::values.use_shader_jit = sdl2_config->GetBoolean("Renderer", "use_shader_jit", true); Settings::values.resolution_factor = static_cast(sdl2_config->GetInteger("Renderer", "resolution_factor", 1)); - Settings::values.vsync_enabled = sdl2_config->GetBoolean("Renderer", "vsync_enabled", false); + Settings::values.use_vsync_new = sdl2_config->GetBoolean("Renderer", "use_vsync_new", true); Settings::values.use_frame_limit = sdl2_config->GetBoolean("Renderer", "use_frame_limit", true); Settings::values.frame_limit = static_cast(sdl2_config->GetReal("Renderer", "frame_limit", 1.0) * 100.0); diff --git a/src/android/app/src/main/jni/emu_window/emu_window.cpp b/src/android/app/src/main/jni/emu_window/emu_window.cpp index ecd43fb4d..e91947471 100644 --- a/src/android/app/src/main/jni/emu_window/emu_window.cpp +++ b/src/android/app/src/main/jni/emu_window/emu_window.cpp @@ -17,9 +17,53 @@ #include "jni/emu_window/emu_window.h" #include "jni/id_cache.h" #include "network/network.h" +#include "video_core/renderer_base.h" +#include "video_core/video_core.h" -static void* EGL_GetFuncAddress(const char* name) { - return (void*)eglGetProcAddress(name); +static constexpr std::array egl_attribs{EGL_SURFACE_TYPE, + EGL_WINDOW_BIT, + EGL_RENDERABLE_TYPE, + EGL_OPENGL_ES3_BIT_KHR, + EGL_BLUE_SIZE, + 8, + EGL_GREEN_SIZE, + 8, + EGL_RED_SIZE, + 8, + EGL_DEPTH_SIZE, + 0, + EGL_STENCIL_SIZE, + 0, + EGL_NONE}; +static constexpr std::array egl_empty_attribs{EGL_WIDTH, 1, EGL_HEIGHT, 1, EGL_NONE}; +static constexpr std::array egl_context_attribs{EGL_CONTEXT_CLIENT_VERSION, 3, EGL_NONE}; + +SharedContext_Android::SharedContext_Android(EGLDisplay egl_display, EGLConfig egl_config, + EGLContext egl_share_context) + : egl_display{egl_display}, egl_surface{eglCreatePbufferSurface(egl_display, egl_config, + egl_empty_attribs.data())}, + egl_context{eglCreateContext(egl_display, egl_config, egl_share_context, + egl_context_attribs.data())} { + ASSERT_MSG(egl_surface, "eglCreatePbufferSurface() failed!"); + ASSERT_MSG(egl_context, "eglCreateContext() failed!"); +} + +SharedContext_Android::~SharedContext_Android() { + if (!eglDestroySurface(egl_display, egl_surface)) { + LOG_CRITICAL(Frontend, "eglDestroySurface() failed"); + } + + if (!eglDestroyContext(egl_display, egl_context)) { + LOG_CRITICAL(Frontend, "eglDestroySurface() failed"); + } +} + +void SharedContext_Android::MakeCurrent() { + eglMakeCurrent(egl_display, egl_surface, egl_surface, egl_context); +} + +void SharedContext_Android::DoneCurrent() { + eglMakeCurrent(egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); } static bool IsPortraitMode() { @@ -55,65 +99,61 @@ void EmuWindow_Android::OnFramebufferSizeChanged() { } EmuWindow_Android::EmuWindow_Android(ANativeWindow* surface) { - LOG_DEBUG(Frontend, "Initializing Emuwindow"); + LOG_DEBUG(Frontend, "Initializing EmuWindow_Android"); + + if (!surface) { + LOG_CRITICAL(Frontend, "surface is nullptr"); + return; + } Network::Init(); host_window = surface; - egl_display = eglGetDisplay(EGL_DEFAULT_DISPLAY); - EGLint num_configs{}, egl_major{}, egl_minor{}; - - if (!egl_display) { + if (egl_display = eglGetDisplay(EGL_DEFAULT_DISPLAY); egl_display == EGL_NO_DISPLAY) { LOG_CRITICAL(Frontend, "eglGetDisplay() failed"); return; } - - if (!eglInitialize(egl_display, &egl_major, &egl_minor)) { + if (eglInitialize(egl_display, 0, 0) != EGL_TRUE) { LOG_CRITICAL(Frontend, "eglInitialize() failed"); return; } - - constexpr std::array attribs{EGL_RENDERABLE_TYPE, - EGL_OPENGL_ES3_BIT_KHR, - EGL_RED_SIZE, - 8, - EGL_GREEN_SIZE, - 8, - EGL_BLUE_SIZE, - 8, - EGL_SURFACE_TYPE, - EGL_WINDOW_BIT, - EGL_NONE}; - - if (!eglChooseConfig(egl_display, attribs.data(), &config, 1, &num_configs)) { + if (EGLint egl_num_configs{}; eglChooseConfig(egl_display, egl_attribs.data(), &egl_config, 1, + &egl_num_configs) != EGL_TRUE) { LOG_CRITICAL(Frontend, "eglChooseConfig() failed"); return; } - eglBindAPI(EGL_OPENGL_ES_API); + CreateWindowSurface(); - if (!egl_context) { - std::vector ctx_attribs{EGL_CONTEXT_CLIENT_VERSION, 3, EGL_NONE}; - egl_context = eglCreateContext(egl_display, config, EGL_NO_CONTEXT, &ctx_attribs[0]); - shared_attribs = std::move(ctx_attribs); - - if (!egl_context) { - LOG_CRITICAL(Frontend, "eglCreateContext() failed"); - return; - } + if (egl_context = eglCreateContext(egl_display, egl_config, 0, egl_context_attribs.data()); + egl_context == EGL_NO_CONTEXT) { + LOG_CRITICAL(Frontend, "eglCreateContext() failed"); + return; } - - if (!CreateWindowSurface()) { - LOG_CRITICAL(Frontend, "CreateWindowSurface() failed"); + if (eglSurfaceAttrib(egl_display, egl_surface, EGL_SWAP_BEHAVIOR, EGL_BUFFER_DESTROYED) != + EGL_TRUE) { + LOG_CRITICAL(Frontend, "eglSurfaceAttrib() failed"); + return; + } + if (core_context = CreateSharedContext(); !core_context) { + LOG_CRITICAL(Frontend, "CreateSharedContext() failed"); + return; + } + if (eglMakeCurrent(egl_display, egl_surface, egl_surface, egl_context) != EGL_TRUE) { + LOG_CRITICAL(Frontend, "eglMakeCurrent() failed"); + return; + } + if (!gladLoadGLES2Loader((GLADloadproc)eglGetProcAddress)) { + LOG_CRITICAL(Frontend, "gladLoadGLES2Loader() failed"); + return; + } + if (!eglSwapInterval(egl_display, Settings::values.use_vsync_new ? 1 : 0)) { + LOG_CRITICAL(Frontend, "eglSwapInterval() failed"); return; } - MakeCurrent(); - OnFramebufferSizeChanged(); - - gladLoadGLES2Loader(EGL_GetFuncAddress); } bool EmuWindow_Android::CreateWindowSurface() { @@ -122,32 +162,30 @@ bool EmuWindow_Android::CreateWindowSurface() { } EGLint format{}; - eglGetConfigAttrib(egl_display, config, EGL_NATIVE_VISUAL_ID, &format); + eglGetConfigAttrib(egl_display, egl_config, EGL_NATIVE_VISUAL_ID, &format); ANativeWindow_setBuffersGeometry(host_window, 0, 0, format); window_width = ANativeWindow_getWidth(host_window); window_height = ANativeWindow_getHeight(host_window); UpdateCurrentFramebufferLayout(window_width, window_height); - NotifyClientAreaSizeChanged(std::make_pair(window_width, window_height)); - egl_surface = eglCreateWindowSurface(egl_display, config, - static_cast(host_window), nullptr); + if (egl_surface = eglCreateWindowSurface(egl_display, egl_config, host_window, 0); + egl_surface == EGL_NO_SURFACE) { + return {}; + } return !!egl_surface; } void EmuWindow_Android::DestroyWindowSurface() { - if (egl_surface == EGL_NO_SURFACE) { + if (!egl_surface) { return; } - if (eglGetCurrentSurface(EGL_DRAW) == egl_surface) { eglMakeCurrent(egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); } - if (!eglDestroySurface(egl_display, egl_surface)) { LOG_CRITICAL(Frontend, "eglDestroySurface() failed"); } - egl_surface = EGL_NO_SURFACE; } @@ -155,30 +193,57 @@ void EmuWindow_Android::DestroyContext() { if (!egl_context) { return; } - if (eglGetCurrentContext() == egl_context) { eglMakeCurrent(egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); } - if (!eglDestroyContext(egl_display, egl_context)) { LOG_CRITICAL(Frontend, "eglDestroySurface() failed"); } - - if (!is_shared && !eglTerminate(egl_display)) { + if (!eglTerminate(egl_display)) { LOG_CRITICAL(Frontend, "eglTerminate() failed"); } - egl_context = EGL_NO_CONTEXT; egl_display = EGL_NO_DISPLAY; } EmuWindow_Android::~EmuWindow_Android() { + StopPresenting(); DestroyWindowSurface(); DestroyContext(); } -void EmuWindow_Android::SwapBuffers() { - eglSwapBuffers(egl_display, egl_surface); +std::unique_ptr EmuWindow_Android::CreateSharedContext() const { + return std::make_unique(egl_display, egl_config, egl_context); +} + +void EmuWindow_Android::StartPresenting() { + ASSERT(!presentation_thread); + is_presenting = true; + presentation_thread = + std::make_unique([emu_window{this}] { emu_window->Present(); }); +} + +void EmuWindow_Android::StopPresenting() { + is_presenting = false; + if (presentation_thread) { + presentation_thread->join(); + presentation_thread.reset(); + } +} + +bool EmuWindow_Android::IsPresenting() const { + return is_presenting; +} + +void EmuWindow_Android::Present() { + eglMakeCurrent(egl_display, egl_surface, egl_surface, egl_context); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); + eglSwapInterval(egl_display, Settings::values.use_vsync_new ? 1 : 0); + while (IsPresenting()) { + VideoCore::g_renderer->TryPresent(100); + eglSwapBuffers(egl_display, egl_surface); + } + eglMakeCurrent(egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); } void EmuWindow_Android::PollEvents() { @@ -188,17 +253,21 @@ void EmuWindow_Android::PollEvents() { host_window = render_window; render_window = nullptr; - DoneCurrent(); + + if (IsPresenting()) { + StopPresenting(); + } + DestroyWindowSurface(); CreateWindowSurface(); - MakeCurrent(); OnFramebufferSizeChanged(); + StartPresenting(); } void EmuWindow_Android::MakeCurrent() { - eglMakeCurrent(egl_display, egl_surface, egl_surface, egl_context); + core_context->MakeCurrent(); } void EmuWindow_Android::DoneCurrent() { - eglMakeCurrent(egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + core_context->DoneCurrent(); } diff --git a/src/android/app/src/main/jni/emu_window/emu_window.h b/src/android/app/src/main/jni/emu_window/emu_window.h index 2b85253f6..2fc01197d 100644 --- a/src/android/app/src/main/jni/emu_window/emu_window.h +++ b/src/android/app/src/main/jni/emu_window/emu_window.h @@ -4,6 +4,7 @@ #pragma once +#include #include #include @@ -13,11 +14,30 @@ struct ANativeWindow; +class SharedContext_Android : public Frontend::GraphicsContext { +public: + SharedContext_Android(EGLDisplay egl_display, EGLConfig egl_config, + EGLContext egl_share_context); + + ~SharedContext_Android() override; + + void MakeCurrent() override; + + void DoneCurrent() override; + +private: + EGLSurface egl_surface{}; + EGLContext egl_context{}; + EGLDisplay egl_display{}; +}; + class EmuWindow_Android : public Frontend::EmuWindow { public: EmuWindow_Android(ANativeWindow* surface); ~EmuWindow_Android(); + void Present(); + /// Called by the onSurfaceChanges() method to change the surface void OnSurfaceChanged(ANativeWindow* surface); @@ -27,11 +47,16 @@ public: /// Handles movement of touch pointer void OnTouchMoved(int x, int y); - void SwapBuffers() override; void PollEvents() override; void MakeCurrent() override; void DoneCurrent() override; + void StartPresenting(); + void StopPresenting(); + bool IsPresenting() const; + + std::unique_ptr CreateSharedContext() const override; + private: void OnFramebufferSizeChanged(); bool CreateWindowSurface(); @@ -41,13 +66,17 @@ private: ANativeWindow* render_window{}; ANativeWindow* host_window{}; - bool is_shared{}; int window_width{}; int window_height{}; - std::vector shared_attribs; - EGLConfig config; + EGLConfig egl_config; EGLSurface egl_surface{}; EGLContext egl_context{}; EGLDisplay egl_display{}; + + std::unique_ptr core_context; + + std::unique_ptr presentation_thread; + + bool is_presenting{}; }; diff --git a/src/android/app/src/main/jni/native.cpp b/src/android/app/src/main/jni/native.cpp index f8d8eacd7..605841ac9 100644 --- a/src/android/app/src/main/jni/native.cpp +++ b/src/android/app/src/main/jni/native.cpp @@ -24,6 +24,7 @@ #include "core/core.h" #include "core/file_sys/cia_container.h" #include "core/frontend/applets/default_applets.h" +#include "core/frontend/scope_acquire_context.h" #include "core/gdbstub/gdbstub.h" #include "core/hle/service/am/am.h" #include "core/loader/loader.h" @@ -142,6 +143,7 @@ static Core::System::ResultStatus RunCitra(const std::string& filepath) { InputManager::Init(); SCOPE_EXIT({ InputManager::Shutdown(); }); + Frontend::ScopeAcquireContext scope(*window); const Core::System::ResultStatus load_result{system.Load(*window, filepath)}; if (load_result != Core::System::ResultStatus::Success) { return load_result; @@ -153,6 +155,7 @@ static Core::System::ResultStatus RunCitra(const std::string& filepath) { is_running = true; pause_emulation = false; + window->StartPresenting(); while (is_running) { if (!pause_emulation) { system.RunLoop(); @@ -166,6 +169,7 @@ static Core::System::ResultStatus RunCitra(const std::string& filepath) { running_cv.wait(lock, [] { return !pause_emulation || !is_running; }); } } + window->StopPresenting(); return Core::System::ResultStatus::Success; }