From a50498f3ad564766927d9daf4615bdfad17b028a Mon Sep 17 00:00:00 2001 From: bunnei Date: Thu, 12 Sep 2019 22:28:12 -0400 Subject: [PATCH] android: jni: Migrate EmuWindow_Android class to EGL. - This enables us to use shared contexts more easily. --- .../src/main/jni/emu_window/emu_window.cpp | 193 +++++++++++++----- .../app/src/main/jni/emu_window/emu_window.h | 58 +++--- src/android/app/src/main/jni/native.cpp | 10 +- 3 files changed, 178 insertions(+), 83 deletions(-) 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 ff5203f96..ecd43fb4d 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 @@ -1,25 +1,38 @@ -// Copyright 2016 Citra Emulator Project +// Copyright 2019 Citra Emulator Project // Licensed under GPLv2 or any later version // Refer to the license.txt file included. #include +#include #include #include + +#include #include + #include "common/logging/log.h" -#include "common/scm_rev.h" -#include "common/string_util.h" -#include "core/3ds.h" #include "core/settings.h" -#include "input_common/keyboard.h" #include "input_common/main.h" -#include "input_common/motion_emu.h" #include "jni/button_manager.h" -#include "jni/id_cache.h" #include "jni/emu_window/emu_window.h" -#include "jni/ndk_helper/GLContext.h" +#include "jni/id_cache.h" #include "network/network.h" +static void* EGL_GetFuncAddress(const char* name) { + return (void*)eglGetProcAddress(name); +} + +static bool IsPortraitMode() { + return JNI_FALSE != IDCache::GetEnvForThread()->CallStaticBooleanMethod( + IDCache::GetNativeLibraryClass(), IDCache::GetIsPortraitMode()); +} + +static void UpdateLandscapeScreenLayout() { + Settings::values.layout_option = + static_cast(IDCache::GetEnvForThread()->CallStaticIntMethod( + IDCache::GetNativeLibraryClass(), IDCache::GetLandscapeScreenLayout())); +} + void EmuWindow_Android::OnSurfaceChanged(ANativeWindow* surface) { render_window = surface; } @@ -36,74 +49,156 @@ void EmuWindow_Android::OnTouchMoved(int x, int y) { TouchMoved((unsigned)std::max(x, 0), (unsigned)std::max(y, 0)); } -static bool IsPortraitMode() -{ - JNIEnv* env = IDCache::GetEnvForThread(); - - // Execute the Java method. - jboolean result = env->CallStaticBooleanMethod( - IDCache::GetNativeLibraryClass(), IDCache::GetIsPortraitMode()); - - return result != JNI_FALSE; -} - -static void UpdateLandscapeScreenLayout() -{ - JNIEnv* env = IDCache::GetEnvForThread(); - - // Execute the Java method. - Settings::values.layout_option = static_cast(env->CallStaticIntMethod( - IDCache::GetNativeLibraryClass(), IDCache::GetLandscapeScreenLayout())); -} - void EmuWindow_Android::OnFramebufferSizeChanged() { - int width, height; - width = gl_context->GetScreenWidth(); - height = gl_context->GetScreenHeight(); UpdateLandscapeScreenLayout(); - UpdateCurrentFramebufferLayout(width, height, IsPortraitMode()); + UpdateCurrentFramebufferLayout(window_width, window_height, IsPortraitMode()); } EmuWindow_Android::EmuWindow_Android(ANativeWindow* surface) { LOG_DEBUG(Frontend, "Initializing Emuwindow"); Network::Init(); - gl_context = ndk_helper::GLContext::GetInstance(); - render_window = surface; - LOG_INFO(Frontend, "InitDisplay"); - gl_context->Init(render_window); + host_window = surface; + egl_display = eglGetDisplay(EGL_DEFAULT_DISPLAY); - if (!gladLoadGLES2Loader(reinterpret_cast(eglGetProcAddress))) { - LOG_CRITICAL(Frontend, "Failed to initialize GL functions: %d", eglGetError()); + EGLint num_configs{}, egl_major{}, egl_minor{}; + + if (!egl_display) { + LOG_CRITICAL(Frontend, "eglGetDisplay() failed"); + return; } + if (!eglInitialize(egl_display, &egl_major, &egl_minor)) { + 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)) { + LOG_CRITICAL(Frontend, "eglChooseConfig() failed"); + return; + } + + eglBindAPI(EGL_OPENGL_ES_API); + + 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 (!CreateWindowSurface()) { + LOG_CRITICAL(Frontend, "CreateWindowSurface() failed"); + return; + } + + MakeCurrent(); + OnFramebufferSizeChanged(); - DoneCurrent(); + + gladLoadGLES2Loader(EGL_GetFuncAddress); +} + +bool EmuWindow_Android::CreateWindowSurface() { + if (!host_window) { + return true; + } + + EGLint format{}; + eglGetConfigAttrib(egl_display, 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); + + return !!egl_surface; +} + +void EmuWindow_Android::DestroyWindowSurface() { + if (egl_surface == EGL_NO_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; +} + +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)) { + LOG_CRITICAL(Frontend, "eglTerminate() failed"); + } + + egl_context = EGL_NO_CONTEXT; + egl_display = EGL_NO_DISPLAY; } EmuWindow_Android::~EmuWindow_Android() { - gl_context->Invalidate(); - Network::Shutdown(); + DestroyWindowSurface(); + DestroyContext(); } void EmuWindow_Android::SwapBuffers() { - - if (EGL_SUCCESS != gl_context->Swap()) - LOG_ERROR(Frontend, "Swap failed"); + eglSwapBuffers(egl_display, egl_surface); } void EmuWindow_Android::PollEvents() { - if (render_window != gl_context->GetANativeWindow()) { - MakeCurrent(); - OnFramebufferSizeChanged(); + if (!render_window) { + return; } + + host_window = render_window; + render_window = nullptr; + DoneCurrent(); + DestroyWindowSurface(); + CreateWindowSurface(); + MakeCurrent(); + OnFramebufferSizeChanged(); } void EmuWindow_Android::MakeCurrent() { - gl_context->Resume(render_window); + eglMakeCurrent(egl_display, egl_surface, egl_surface, egl_context); } void EmuWindow_Android::DoneCurrent() { - gl_context->Suspend(); + eglMakeCurrent(egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); } 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 317a0bda8..2b85253f6 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 @@ -1,31 +1,23 @@ -// Copyright 2016 Citra Emulator Project +// Copyright 2019 Citra Emulator Project // Licensed under GPLv2 or any later version // Refer to the license.txt file included. #pragma once -#include -#include +#include + +#include +#include + #include "core/frontend/emu_window.h" -#include "jni/ndk_helper/GLContext.h" + +struct ANativeWindow; class EmuWindow_Android : public Frontend::EmuWindow { public: EmuWindow_Android(ANativeWindow* surface); ~EmuWindow_Android(); - /// Swap buffers to display the next frame - void SwapBuffers() override; - - /// Polls window events - void PollEvents() override; - - /// Makes the graphics context current for the caller thread - void MakeCurrent() override; - - /// Releases the GL context from the caller thread - void DoneCurrent() override; - /// Called by the onSurfaceChanges() method to change the surface void OnSurfaceChanged(ANativeWindow* surface); @@ -35,25 +27,27 @@ public: /// Handles movement of touch pointer void OnTouchMoved(int x, int y); + void SwapBuffers() override; + void PollEvents() override; + void MakeCurrent() override; + void DoneCurrent() override; + private: - /// Called by PollEvents when a key is pressed or released. - void OnKeyEvent(int key, u8 state); - - /// Called by PollEvents when the mouse moves. - void OnMouseMotion(s32 x, s32 y); - - /// Called by PollEvents when a mouse button is pressed or released - void OnMouseButton(u32 button, u8 state, s32 x, s32 y); - - /// Called when any event that may change the surface(Rotation) void OnFramebufferSizeChanged(); + bool CreateWindowSurface(); + void DestroyWindowSurface(); + void DestroyContext(); - /// Internal render window - ANativeWindow* render_window; + ANativeWindow* render_window{}; + ANativeWindow* host_window{}; - /// Initialise the EGL context associated with the window - bool InitDisplay(); + bool is_shared{}; + int window_width{}; + int window_height{}; + std::vector shared_attribs; - /// A helper class for handling the EGL context - ndk_helper::GLContext* gl_context; + EGLConfig config; + EGLSurface egl_surface{}; + EGLContext egl_context{}; + EGLDisplay egl_display{}; }; diff --git a/src/android/app/src/main/jni/native.cpp b/src/android/app/src/main/jni/native.cpp index 18b568d25..080557a28 100644 --- a/src/android/app/src/main/jni/native.cpp +++ b/src/android/app/src/main/jni/native.cpp @@ -174,14 +174,20 @@ void Java_org_citra_citra_1android_NativeLibrary_SurfaceChanged(JNIEnv* env, job jobject surf) { s_surf = ANativeWindow_fromSurface(env, surf); - if (is_running) { + if (window) { window->OnSurfaceChanged(s_surf); } LOG_INFO(Frontend, "Surface changed"); } -void Java_org_citra_citra_1android_NativeLibrary_SurfaceDestroyed(JNIEnv* env, jobject obj) {} +void Java_org_citra_citra_1android_NativeLibrary_SurfaceDestroyed(JNIEnv* env, jobject obj) { + ANativeWindow_release(s_surf); + s_surf = nullptr; + if (window) { + window->OnSurfaceChanged(s_surf); + } +} void Java_org_citra_citra_1android_NativeLibrary_NotifyOrientationChange( JNIEnv* env, jobject obj, jint layout_option, jboolean is_portrait_mode) {