android: jni: Migrate EmuWindow_Android class to EGL.
- This enables us to use shared contexts more easily.
This commit is contained in:
parent
db0cafa15f
commit
a50498f3ad
@ -1,25 +1,38 @@
|
|||||||
// Copyright 2016 Citra Emulator Project
|
// Copyright 2019 Citra Emulator Project
|
||||||
// Licensed under GPLv2 or any later version
|
// Licensed under GPLv2 or any later version
|
||||||
// Refer to the license.txt file included.
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <array>
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
|
#include <android/native_window_jni.h>
|
||||||
#include <glad/glad.h>
|
#include <glad/glad.h>
|
||||||
|
|
||||||
#include "common/logging/log.h"
|
#include "common/logging/log.h"
|
||||||
#include "common/scm_rev.h"
|
|
||||||
#include "common/string_util.h"
|
|
||||||
#include "core/3ds.h"
|
|
||||||
#include "core/settings.h"
|
#include "core/settings.h"
|
||||||
#include "input_common/keyboard.h"
|
|
||||||
#include "input_common/main.h"
|
#include "input_common/main.h"
|
||||||
#include "input_common/motion_emu.h"
|
|
||||||
#include "jni/button_manager.h"
|
#include "jni/button_manager.h"
|
||||||
#include "jni/id_cache.h"
|
|
||||||
#include "jni/emu_window/emu_window.h"
|
#include "jni/emu_window/emu_window.h"
|
||||||
#include "jni/ndk_helper/GLContext.h"
|
#include "jni/id_cache.h"
|
||||||
#include "network/network.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<Settings::LayoutOption>(IDCache::GetEnvForThread()->CallStaticIntMethod(
|
||||||
|
IDCache::GetNativeLibraryClass(), IDCache::GetLandscapeScreenLayout()));
|
||||||
|
}
|
||||||
|
|
||||||
void EmuWindow_Android::OnSurfaceChanged(ANativeWindow* surface) {
|
void EmuWindow_Android::OnSurfaceChanged(ANativeWindow* surface) {
|
||||||
render_window = 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));
|
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<Settings::LayoutOption>(env->CallStaticIntMethod(
|
|
||||||
IDCache::GetNativeLibraryClass(), IDCache::GetLandscapeScreenLayout()));
|
|
||||||
}
|
|
||||||
|
|
||||||
void EmuWindow_Android::OnFramebufferSizeChanged() {
|
void EmuWindow_Android::OnFramebufferSizeChanged() {
|
||||||
int width, height;
|
|
||||||
width = gl_context->GetScreenWidth();
|
|
||||||
height = gl_context->GetScreenHeight();
|
|
||||||
UpdateLandscapeScreenLayout();
|
UpdateLandscapeScreenLayout();
|
||||||
UpdateCurrentFramebufferLayout(width, height, IsPortraitMode());
|
UpdateCurrentFramebufferLayout(window_width, window_height, IsPortraitMode());
|
||||||
}
|
}
|
||||||
|
|
||||||
EmuWindow_Android::EmuWindow_Android(ANativeWindow* surface) {
|
EmuWindow_Android::EmuWindow_Android(ANativeWindow* surface) {
|
||||||
LOG_DEBUG(Frontend, "Initializing Emuwindow");
|
LOG_DEBUG(Frontend, "Initializing Emuwindow");
|
||||||
|
|
||||||
Network::Init();
|
Network::Init();
|
||||||
gl_context = ndk_helper::GLContext::GetInstance();
|
|
||||||
render_window = surface;
|
|
||||||
|
|
||||||
LOG_INFO(Frontend, "InitDisplay");
|
host_window = surface;
|
||||||
gl_context->Init(render_window);
|
egl_display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
|
||||||
|
|
||||||
if (!gladLoadGLES2Loader(reinterpret_cast<GLADloadproc>(eglGetProcAddress))) {
|
EGLint num_configs{}, egl_major{}, egl_minor{};
|
||||||
LOG_CRITICAL(Frontend, "Failed to initialize GL functions: %d", eglGetError());
|
|
||||||
|
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<int, 11> 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<EGLint> 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();
|
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<EGLNativeWindowType>(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() {
|
EmuWindow_Android::~EmuWindow_Android() {
|
||||||
gl_context->Invalidate();
|
DestroyWindowSurface();
|
||||||
Network::Shutdown();
|
DestroyContext();
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmuWindow_Android::SwapBuffers() {
|
void EmuWindow_Android::SwapBuffers() {
|
||||||
|
eglSwapBuffers(egl_display, egl_surface);
|
||||||
if (EGL_SUCCESS != gl_context->Swap())
|
|
||||||
LOG_ERROR(Frontend, "Swap failed");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmuWindow_Android::PollEvents() {
|
void EmuWindow_Android::PollEvents() {
|
||||||
if (render_window != gl_context->GetANativeWindow()) {
|
if (!render_window) {
|
||||||
MakeCurrent();
|
return;
|
||||||
OnFramebufferSizeChanged();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
host_window = render_window;
|
||||||
|
render_window = nullptr;
|
||||||
|
DoneCurrent();
|
||||||
|
DestroyWindowSurface();
|
||||||
|
CreateWindowSurface();
|
||||||
|
MakeCurrent();
|
||||||
|
OnFramebufferSizeChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmuWindow_Android::MakeCurrent() {
|
void EmuWindow_Android::MakeCurrent() {
|
||||||
gl_context->Resume(render_window);
|
eglMakeCurrent(egl_display, egl_surface, egl_surface, egl_context);
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmuWindow_Android::DoneCurrent() {
|
void EmuWindow_Android::DoneCurrent() {
|
||||||
gl_context->Suspend();
|
eglMakeCurrent(egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
|
||||||
}
|
}
|
||||||
|
@ -1,31 +1,23 @@
|
|||||||
// Copyright 2016 Citra Emulator Project
|
// Copyright 2019 Citra Emulator Project
|
||||||
// Licensed under GPLv2 or any later version
|
// Licensed under GPLv2 or any later version
|
||||||
// Refer to the license.txt file included.
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <memory>
|
#include <vector>
|
||||||
#include <utility>
|
|
||||||
|
#include <EGL/egl.h>
|
||||||
|
#include <EGL/eglext.h>
|
||||||
|
|
||||||
#include "core/frontend/emu_window.h"
|
#include "core/frontend/emu_window.h"
|
||||||
#include "jni/ndk_helper/GLContext.h"
|
|
||||||
|
struct ANativeWindow;
|
||||||
|
|
||||||
class EmuWindow_Android : public Frontend::EmuWindow {
|
class EmuWindow_Android : public Frontend::EmuWindow {
|
||||||
public:
|
public:
|
||||||
EmuWindow_Android(ANativeWindow* surface);
|
EmuWindow_Android(ANativeWindow* surface);
|
||||||
~EmuWindow_Android();
|
~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
|
/// Called by the onSurfaceChanges() method to change the surface
|
||||||
void OnSurfaceChanged(ANativeWindow* surface);
|
void OnSurfaceChanged(ANativeWindow* surface);
|
||||||
|
|
||||||
@ -35,25 +27,27 @@ public:
|
|||||||
/// Handles movement of touch pointer
|
/// Handles movement of touch pointer
|
||||||
void OnTouchMoved(int x, int y);
|
void OnTouchMoved(int x, int y);
|
||||||
|
|
||||||
|
void SwapBuffers() override;
|
||||||
|
void PollEvents() override;
|
||||||
|
void MakeCurrent() override;
|
||||||
|
void DoneCurrent() override;
|
||||||
|
|
||||||
private:
|
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();
|
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 is_shared{};
|
||||||
bool InitDisplay();
|
int window_width{};
|
||||||
|
int window_height{};
|
||||||
|
std::vector<int> shared_attribs;
|
||||||
|
|
||||||
/// A helper class for handling the EGL context
|
EGLConfig config;
|
||||||
ndk_helper::GLContext* gl_context;
|
EGLSurface egl_surface{};
|
||||||
|
EGLContext egl_context{};
|
||||||
|
EGLDisplay egl_display{};
|
||||||
};
|
};
|
||||||
|
@ -174,14 +174,20 @@ void Java_org_citra_citra_1android_NativeLibrary_SurfaceChanged(JNIEnv* env, job
|
|||||||
jobject surf) {
|
jobject surf) {
|
||||||
s_surf = ANativeWindow_fromSurface(env, surf);
|
s_surf = ANativeWindow_fromSurface(env, surf);
|
||||||
|
|
||||||
if (is_running) {
|
if (window) {
|
||||||
window->OnSurfaceChanged(s_surf);
|
window->OnSurfaceChanged(s_surf);
|
||||||
}
|
}
|
||||||
|
|
||||||
LOG_INFO(Frontend, "Surface changed");
|
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(
|
void Java_org_citra_citra_1android_NativeLibrary_NotifyOrientationChange(
|
||||||
JNIEnv* env, jobject obj, jint layout_option, jboolean is_portrait_mode) {
|
JNIEnv* env, jobject obj, jint layout_option, jboolean is_portrait_mode) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user