From c2eb6a2fcb3b9b5dc7c44603914ee49c881a62df Mon Sep 17 00:00:00 2001 From: Henrik Rydgard Date: Sun, 13 Dec 2015 18:38:26 +0100 Subject: [PATCH 1/9] Minimal port of Android EGL code from Dolphin. --- Common/GL/GLInterface/EGL.cpp | 228 ++++++++++++++++++++++++++ Common/GL/GLInterface/EGL.h | 32 ++++ Common/GL/GLInterface/EGLAndroid.cpp | 28 ++++ Common/GL/GLInterface/EGLAndroid.h | 15 ++ Common/GL/GLInterface/GLInterface.cpp | 40 +++++ Common/GL/GLInterfaceBase.h | 50 ++++++ 6 files changed, 393 insertions(+) create mode 100644 Common/GL/GLInterface/EGL.cpp create mode 100644 Common/GL/GLInterface/EGL.h create mode 100644 Common/GL/GLInterface/EGLAndroid.cpp create mode 100644 Common/GL/GLInterface/EGLAndroid.h create mode 100644 Common/GL/GLInterface/GLInterface.cpp create mode 100644 Common/GL/GLInterfaceBase.h diff --git a/Common/GL/GLInterface/EGL.cpp b/Common/GL/GLInterface/EGL.cpp new file mode 100644 index 0000000000..46d043aa56 --- /dev/null +++ b/Common/GL/GLInterface/EGL.cpp @@ -0,0 +1,228 @@ +// Copyright 2012 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#include +#include + +#include "Common/GL/GLInterface/EGL.h" +#include "Common/Log.h" + +// Show the current FPS +void cInterfaceEGL::Swap() +{ + eglSwapBuffers(egl_dpy, egl_surf); +} +void cInterfaceEGL::SwapInterval(int Interval) +{ + eglSwapInterval(egl_dpy, Interval); +} + +void* cInterfaceEGL::GetFuncAddress(const std::string& name) +{ + return (void*)eglGetProcAddress(name.c_str()); +} + +void cInterfaceEGL::DetectMode() +{ + if (s_opengl_mode != MODE_DETECT) + return; + + EGLint num_configs; + bool supportsGL = false, supportsGLES2 = false, supportsGLES3 = false; + std::array renderable_types = { + EGL_OPENGL_BIT, + (1 << 6), /* EGL_OPENGL_ES3_BIT_KHR */ + EGL_OPENGL_ES2_BIT, + }; + + for (auto renderable_type : renderable_types) + { + // attributes for a visual in RGBA format with at least + // 8 bits per color + int attribs[] = { + EGL_RED_SIZE, 8, + EGL_GREEN_SIZE, 8, + EGL_BLUE_SIZE, 8, + EGL_RENDERABLE_TYPE, renderable_type, + EGL_NONE + }; + + // Get how many configs there are + if (!eglChooseConfig( egl_dpy, attribs, nullptr, 0, &num_configs)) + { + INFO_LOG(G3D, "Error: couldn't get an EGL visual config\n"); + continue; + } + + EGLConfig* config = new EGLConfig[num_configs]; + + // Get all the configurations + if (!eglChooseConfig(egl_dpy, attribs, config, num_configs, &num_configs)) + { + INFO_LOG(G3D, "Error: couldn't get an EGL visual config\n"); + delete[] config; + continue; + } + + for (int i = 0; i < num_configs; ++i) + { + EGLint attribVal; + bool ret; + ret = eglGetConfigAttrib(egl_dpy, config[i], EGL_RENDERABLE_TYPE, &attribVal); + if (ret) + { + if (attribVal & EGL_OPENGL_BIT) + supportsGL = true; + if (attribVal & (1 << 6)) /* EGL_OPENGL_ES3_BIT_KHR */ + supportsGLES3 = true; + if (attribVal & EGL_OPENGL_ES2_BIT) + supportsGLES2 = true; + } + } + delete[] config; + } + + if (supportsGL) + s_opengl_mode = GLInterfaceMode::MODE_OPENGL; + else if (supportsGLES3) + s_opengl_mode = GLInterfaceMode::MODE_OPENGLES3; + else if (supportsGLES2) + s_opengl_mode = GLInterfaceMode::MODE_OPENGLES2; + + if (s_opengl_mode == GLInterfaceMode::MODE_DETECT) // Errored before we found a mode + s_opengl_mode = GLInterfaceMode::MODE_OPENGL; // Fall back to OpenGL +} + +// Create rendering window. +// Call browser: Core.cpp:EmuThread() > main.cpp:Video_Initialize() +bool cInterfaceEGL::Create(void *window_handle, bool core) +{ + const char *s; + EGLint egl_major, egl_minor; + + egl_dpy = OpenDisplay(); + + if (!egl_dpy) + { + INFO_LOG(G3D, "Error: eglGetDisplay() failed\n"); + return false; + } + + if (!eglInitialize(egl_dpy, &egl_major, &egl_minor)) + { + INFO_LOG(G3D, "Error: eglInitialize() failed\n"); + return false; + } + + /* Detection code */ + EGLConfig config; + EGLint num_configs; + + DetectMode(); + + // attributes for a visual in RGBA format with at least + // 8 bits per color + int attribs[] = { + EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, + EGL_RED_SIZE, 8, + EGL_GREEN_SIZE, 8, + EGL_BLUE_SIZE, 8, + EGL_NONE }; + + EGLint ctx_attribs[] = { + EGL_CONTEXT_CLIENT_VERSION, 2, + EGL_NONE + }; + switch (s_opengl_mode) + { + case MODE_OPENGL: + attribs[1] = EGL_OPENGL_BIT; + ctx_attribs[0] = EGL_NONE; + break; + case MODE_OPENGLES2: + attribs[1] = EGL_OPENGL_ES2_BIT; + ctx_attribs[1] = 2; + break; + case MODE_OPENGLES3: + attribs[1] = (1 << 6); /* EGL_OPENGL_ES3_BIT_KHR */ + ctx_attribs[1] = 3; + break; + default: + ERROR_LOG(G3D, "Unknown opengl mode set\n"); + return false; + break; + } + + if (!eglChooseConfig( egl_dpy, attribs, &config, 1, &num_configs)) + { + INFO_LOG(G3D, "Error: couldn't get an EGL visual config\n"); + exit(1); + } + + if (s_opengl_mode == MODE_OPENGL) + eglBindAPI(EGL_OPENGL_API); + else + eglBindAPI(EGL_OPENGL_ES_API); + + EGLNativeWindowType host_window = (EGLNativeWindowType) window_handle; + EGLNativeWindowType native_window = InitializePlatform(host_window, config); + + s = eglQueryString(egl_dpy, EGL_VERSION); + INFO_LOG(G3D, "EGL_VERSION = %s\n", s); + + s = eglQueryString(egl_dpy, EGL_VENDOR); + INFO_LOG(G3D, "EGL_VENDOR = %s\n", s); + + s = eglQueryString(egl_dpy, EGL_EXTENSIONS); + INFO_LOG(G3D, "EGL_EXTENSIONS = %s\n", s); + + s = eglQueryString(egl_dpy, EGL_CLIENT_APIS); + INFO_LOG(G3D, "EGL_CLIENT_APIS = %s\n", s); + + egl_ctx = eglCreateContext(egl_dpy, config, EGL_NO_CONTEXT, ctx_attribs ); + if (!egl_ctx) + { + INFO_LOG(G3D, "Error: eglCreateContext failed\n"); + exit(1); + } + + egl_surf = eglCreateWindowSurface(egl_dpy, config, native_window, nullptr); + if (!egl_surf) + { + INFO_LOG(G3D, "Error: eglCreateWindowSurface failed\n"); + exit(1); + } + + return true; +} + +bool cInterfaceEGL::MakeCurrent() +{ + return eglMakeCurrent(egl_dpy, egl_surf, egl_surf, egl_ctx); +} + +bool cInterfaceEGL::ClearCurrent() +{ + return eglMakeCurrent(egl_dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); +} + +// Close backend +void cInterfaceEGL::Shutdown() +{ + ShutdownPlatform(); + if (egl_ctx && !eglMakeCurrent(egl_dpy, egl_surf, egl_surf, egl_ctx)) + NOTICE_LOG(G3D, "Could not release drawing context."); + if (egl_ctx) + { + eglMakeCurrent(egl_dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + if (!eglDestroyContext(egl_dpy, egl_ctx)) + NOTICE_LOG(G3D, "Could not destroy drawing context."); + if (!eglDestroySurface(egl_dpy, egl_surf)) + NOTICE_LOG(G3D, "Could not destroy window surface."); + if (!eglTerminate(egl_dpy)) + NOTICE_LOG(G3D, "Could not destroy display connection."); + egl_ctx = nullptr; + egl_dpy = nullptr; + } +} diff --git a/Common/GL/GLInterface/EGL.h b/Common/GL/GLInterface/EGL.h new file mode 100644 index 0000000000..f25b129d90 --- /dev/null +++ b/Common/GL/GLInterface/EGL.h @@ -0,0 +1,32 @@ +// Copyright 2008 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#pragma once + +#include +#include + +#include "Common/GL/GLInterfaceBase.h" + +class cInterfaceEGL : public cInterfaceBase +{ +protected: + void DetectMode(); + EGLSurface egl_surf; + EGLContext egl_ctx; + EGLDisplay egl_dpy; + + virtual EGLDisplay OpenDisplay() = 0; + virtual EGLNativeWindowType InitializePlatform(EGLNativeWindowType host_window, EGLConfig config) = 0; + virtual void ShutdownPlatform() = 0; +public: + void SwapInterval(int Interval); + void Swap(); + void SetMode(u32 mode) { s_opengl_mode = mode; } + void* GetFuncAddress(const std::string& name); + bool Create(void *window_handle, bool core); + bool MakeCurrent(); + bool ClearCurrent(); + void Shutdown(); +}; diff --git a/Common/GL/GLInterface/EGLAndroid.cpp b/Common/GL/GLInterface/EGLAndroid.cpp new file mode 100644 index 0000000000..6cf8a956bf --- /dev/null +++ b/Common/GL/GLInterface/EGLAndroid.cpp @@ -0,0 +1,28 @@ +// Copyright 2014 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#include "Common/GL/GLInterface/EGLAndroid.h" + +EGLDisplay cInterfaceEGLAndroid::OpenDisplay() +{ + return eglGetDisplay(EGL_DEFAULT_DISPLAY); +} + +EGLNativeWindowType cInterfaceEGLAndroid::InitializePlatform(EGLNativeWindowType host_window, EGLConfig config) +{ + EGLint format; + eglGetConfigAttrib(egl_dpy, config, EGL_NATIVE_VISUAL_ID, &format); + ANativeWindow_setBuffersGeometry(host_window, 0, 0, format); + + const int width = ANativeWindow_getWidth(host_window); + const int height = ANativeWindow_getHeight(host_window); + SetBackBufferDimensions(width, height); + + return host_window; +} + +void cInterfaceEGLAndroid::ShutdownPlatform() +{ +} + diff --git a/Common/GL/GLInterface/EGLAndroid.h b/Common/GL/GLInterface/EGLAndroid.h new file mode 100644 index 0000000000..7f3ede889c --- /dev/null +++ b/Common/GL/GLInterface/EGLAndroid.h @@ -0,0 +1,15 @@ +// Copyright 2014 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#pragma once + +#include "Common/GL/GLInterface/EGL.h" + +class cInterfaceEGLAndroid : public cInterfaceEGL +{ +protected: + EGLDisplay OpenDisplay() override; + EGLNativeWindowType InitializePlatform(EGLNativeWindowType host_window, EGLConfig config) override; + void ShutdownPlatform() override; +}; diff --git a/Common/GL/GLInterface/GLInterface.cpp b/Common/GL/GLInterface/GLInterface.cpp new file mode 100644 index 0000000000..8abe67dc71 --- /dev/null +++ b/Common/GL/GLInterface/GLInterface.cpp @@ -0,0 +1,40 @@ +// Copyright 2014 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#include "Common/GL/GLInterfaceBase.h" + +#ifdef ANDROID +#include "Common/GL/GLInterface/EGLAndroid.h" +#elif defined(__APPLE__) +#include "Common/GL/GLInterface/AGL.h" +#elif defined(_WIN32) +#include "Common/GL/GLInterface/WGL.h" +#elif HAVE_X11 +#if defined(USE_EGL) && USE_EGL +#include "Common/GL/GLInterface/EGLX11.h" +#else +#include "Common/GL/GLInterface/GLX.h" +#endif +#else +#error Platform doesnt have a GLInterface +#endif + +cInterfaceBase* HostGL_CreateGLInterface() +{ + #ifdef ANDROID + return new cInterfaceEGLAndroid; + #elif defined(__APPLE__) + return new cInterfaceAGL; + #elif defined(_WIN32) + return new cInterfaceWGL; + #elif defined(HAVE_X11) && HAVE_X11 + #if defined(USE_EGL) && USE_EGL + return new cInterfaceEGLX11; + #else + return new cInterfaceGLX; + #endif + #else + return nullptr; + #endif +} diff --git a/Common/GL/GLInterfaceBase.h b/Common/GL/GLInterfaceBase.h new file mode 100644 index 0000000000..85bd412499 --- /dev/null +++ b/Common/GL/GLInterfaceBase.h @@ -0,0 +1,50 @@ +// Copyright 2008 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#pragma once + +#include + +#include "Common/CommonTypes.h" + +enum GLInterfaceMode { + MODE_DETECT = 0, + MODE_OPENGL, + MODE_OPENGLES2, + MODE_OPENGLES3, +}; + +class cInterfaceBase +{ +protected: + // Window dimensions. + u32 s_backbuffer_width; + u32 s_backbuffer_height; + + u32 s_opengl_mode; +public: + cInterfaceBase() : s_backbuffer_width(0), s_backbuffer_height(0), s_opengl_mode(MODE_DETECT) {} + virtual ~cInterfaceBase() {} + virtual void Swap() {} + virtual void SetMode(u32 mode) { s_opengl_mode = GLInterfaceMode::MODE_OPENGL; } + virtual u32 GetMode() { return s_opengl_mode; } + virtual void* GetFuncAddress(const std::string& name) { return nullptr; } + virtual bool Create(void *window_handle, bool core = true) { return true; } + virtual bool MakeCurrent() { return true; } + virtual bool ClearCurrent() { return true; } + virtual void Shutdown() {} + + virtual void SwapInterval(int Interval) { } + virtual u32 GetBackBufferWidth() { return s_backbuffer_width; } + virtual u32 GetBackBufferHeight() { return s_backbuffer_height; } + virtual void SetBackBufferDimensions(u32 W, u32 H) {s_backbuffer_width = W; s_backbuffer_height = H; } + virtual void Update() { } + virtual bool PeekMessages() { return false; } +}; + +extern cInterfaceBase *GLInterface; + +// This function has to be defined along the Host_ functions from Core/Host.h. +// Current canonical implementation: DolphinWX/GLInterface/GLInterface.cpp. +cInterfaceBase* HostGL_CreateGLInterface(); From 04f8bffa29e8d971624b6174bd7071e10c82dee1 Mon Sep 17 00:00:00 2001 From: Henrik Rydgard Date: Sun, 13 Dec 2015 22:25:58 +0100 Subject: [PATCH 2/9] Use EGL from native code to initialize and run OpenGL on Android. Should be more robust, allows initializing desktop GL if available, and lets us take control of the render loop. --- Common/GL/GLInterface/EGL.cpp | 17 +- Common/GL/GLInterface/EGL.h | 2 + Common/GL/GLInterface/EGLAndroid.cpp | 4 +- Common/GL/GLInterface/EGLAndroid.h | 13 +- Common/GL/GLInterfaceBase.h | 3 - UI/GameSettingsScreen.cpp | 1 + UI/NativeApp.cpp | 5 +- android/jni/Android.mk | 6 + android/jni/Locals.mk | 2 +- android/jni/app-android.cpp | 178 ++++++----- .../src/org/ppsspp/ppsspp/NativeActivity.java | 284 ++++++++++-------- android/src/org/ppsspp/ppsspp/NativeApp.java | 22 +- .../ppsspp/ppsspp/NativeEGLConfigChooser.java | 205 ------------- .../src/org/ppsspp/ppsspp/NativeRenderer.java | 94 ------ ...tiveGLView.java => NativeSurfaceView.java} | 108 ++++--- .../src/org/ppsspp/ppsspp/PpssppActivity.java | 11 +- 16 files changed, 379 insertions(+), 576 deletions(-) delete mode 100644 android/src/org/ppsspp/ppsspp/NativeEGLConfigChooser.java delete mode 100644 android/src/org/ppsspp/ppsspp/NativeRenderer.java rename android/src/org/ppsspp/ppsspp/{NativeGLView.java => NativeSurfaceView.java} (81%) diff --git a/Common/GL/GLInterface/EGL.cpp b/Common/GL/GLInterface/EGL.cpp index 46d043aa56..917e2392c0 100644 --- a/Common/GL/GLInterface/EGL.cpp +++ b/Common/GL/GLInterface/EGL.cpp @@ -44,6 +44,9 @@ void cInterfaceEGL::DetectMode() EGL_RED_SIZE, 8, EGL_GREEN_SIZE, 8, EGL_BLUE_SIZE, 8, + EGL_ALPHA_SIZE, 8, + EGL_DEPTH_SIZE, 16, + EGL_STENCIL_SIZE, 8, EGL_RENDERABLE_TYPE, renderable_type, EGL_NONE }; @@ -95,7 +98,6 @@ void cInterfaceEGL::DetectMode() } // Create rendering window. -// Call browser: Core.cpp:EmuThread() > main.cpp:Video_Initialize() bool cInterfaceEGL::Create(void *window_handle, bool core) { const char *s; @@ -128,6 +130,9 @@ bool cInterfaceEGL::Create(void *window_handle, bool core) EGL_RED_SIZE, 8, EGL_GREEN_SIZE, 8, EGL_BLUE_SIZE, 8, + EGL_ALPHA_SIZE, 8, + EGL_DEPTH_SIZE, 16, + EGL_STENCIL_SIZE, 8, EGL_NONE }; EGLint ctx_attribs[] = { @@ -154,8 +159,7 @@ bool cInterfaceEGL::Create(void *window_handle, bool core) break; } - if (!eglChooseConfig( egl_dpy, attribs, &config, 1, &num_configs)) - { + if (!eglChooseConfig( egl_dpy, attribs, &config, 1, &num_configs)) { INFO_LOG(G3D, "Error: couldn't get an EGL visual config\n"); exit(1); } @@ -181,15 +185,13 @@ bool cInterfaceEGL::Create(void *window_handle, bool core) INFO_LOG(G3D, "EGL_CLIENT_APIS = %s\n", s); egl_ctx = eglCreateContext(egl_dpy, config, EGL_NO_CONTEXT, ctx_attribs ); - if (!egl_ctx) - { + if (!egl_ctx) { INFO_LOG(G3D, "Error: eglCreateContext failed\n"); exit(1); } egl_surf = eglCreateWindowSurface(egl_dpy, config, native_window, nullptr); - if (!egl_surf) - { + if (!egl_surf) { INFO_LOG(G3D, "Error: eglCreateWindowSurface failed\n"); exit(1); } @@ -207,7 +209,6 @@ bool cInterfaceEGL::ClearCurrent() return eglMakeCurrent(egl_dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); } -// Close backend void cInterfaceEGL::Shutdown() { ShutdownPlatform(); diff --git a/Common/GL/GLInterface/EGL.h b/Common/GL/GLInterface/EGL.h index f25b129d90..527af54918 100644 --- a/Common/GL/GLInterface/EGL.h +++ b/Common/GL/GLInterface/EGL.h @@ -20,6 +20,8 @@ protected: virtual EGLDisplay OpenDisplay() = 0; virtual EGLNativeWindowType InitializePlatform(EGLNativeWindowType host_window, EGLConfig config) = 0; virtual void ShutdownPlatform() = 0; + virtual void SetInternalResolution(int internalWidth, int internalHeight) {} + public: void SwapInterval(int Interval); void Swap(); diff --git a/Common/GL/GLInterface/EGLAndroid.cpp b/Common/GL/GLInterface/EGLAndroid.cpp index 6cf8a956bf..f357982105 100644 --- a/Common/GL/GLInterface/EGLAndroid.cpp +++ b/Common/GL/GLInterface/EGLAndroid.cpp @@ -11,9 +11,9 @@ EGLDisplay cInterfaceEGLAndroid::OpenDisplay() EGLNativeWindowType cInterfaceEGLAndroid::InitializePlatform(EGLNativeWindowType host_window, EGLConfig config) { - EGLint format; + EGLint format = 0; eglGetConfigAttrib(egl_dpy, config, EGL_NATIVE_VISUAL_ID, &format); - ANativeWindow_setBuffersGeometry(host_window, 0, 0, format); + ANativeWindow_setBuffersGeometry(host_window, internalWidth_, internalHeight_, format); const int width = ANativeWindow_getWidth(host_window); const int height = ANativeWindow_getHeight(host_window); diff --git a/Common/GL/GLInterface/EGLAndroid.h b/Common/GL/GLInterface/EGLAndroid.h index 7f3ede889c..614b1a412d 100644 --- a/Common/GL/GLInterface/EGLAndroid.h +++ b/Common/GL/GLInterface/EGLAndroid.h @@ -6,10 +6,19 @@ #include "Common/GL/GLInterface/EGL.h" -class cInterfaceEGLAndroid : public cInterfaceEGL -{ +class cInterfaceEGLAndroid : public cInterfaceEGL { +public: + cInterfaceEGLAndroid() : internalWidth_(0), internalHeight_(0) {} protected: EGLDisplay OpenDisplay() override; EGLNativeWindowType InitializePlatform(EGLNativeWindowType host_window, EGLConfig config) override; void ShutdownPlatform() override; + void SetInternalResolution(int internalWidth, int internalHeight) override { + internalWidth_ = internalWidth; + internalHeight_ = internalHeight; + } + +private: + int internalWidth_; + int internalHeight_; }; diff --git a/Common/GL/GLInterfaceBase.h b/Common/GL/GLInterfaceBase.h index 85bd412499..168f1398e6 100644 --- a/Common/GL/GLInterfaceBase.h +++ b/Common/GL/GLInterfaceBase.h @@ -43,8 +43,5 @@ public: virtual bool PeekMessages() { return false; } }; -extern cInterfaceBase *GLInterface; -// This function has to be defined along the Host_ functions from Core/Host.h. -// Current canonical implementation: DolphinWX/GLInterface/GLInterface.cpp. cInterfaceBase* HostGL_CreateGLInterface(); diff --git a/UI/GameSettingsScreen.cpp b/UI/GameSettingsScreen.cpp index 90d2534dae..56c135d6fb 100644 --- a/UI/GameSettingsScreen.cpp +++ b/UI/GameSettingsScreen.cpp @@ -637,6 +637,7 @@ UI::EventReturn GameSettingsScreen::OnHardwareTransform(UI::EventParams &e) { } UI::EventReturn GameSettingsScreen::OnScreenRotation(UI::EventParams &e) { + ILOG("New display rotation: %d", g_Config.iScreenRotation); System_SendMessage("rotate", ""); return UI::EVENT_DONE; } diff --git a/UI/NativeApp.cpp b/UI/NativeApp.cpp index bacead8251..ecc639e3b6 100644 --- a/UI/NativeApp.cpp +++ b/UI/NativeApp.cpp @@ -210,7 +210,8 @@ void QtHost::ShutdownSound() { } std::string NativeQueryConfig(std::string query) { char temp[128]; if (query == "screenRotation") { - sprintf(temp, "%i", g_Config.iScreenRotation); + ILOG("g_Config.screenRotation = %d", g_Config.iScreenRotation); + snprintf(temp, sizeof(temp), "%d", g_Config.iScreenRotation); return std::string(temp); } else if (query == "immersiveMode") { return std::string(g_Config.bImmersiveMode ? "1" : "0"); @@ -224,7 +225,7 @@ std::string NativeQueryConfig(std::string query) { } int max_res = std::max(System_GetPropertyInt(SYSPROP_DISPLAY_XRES), System_GetPropertyInt(SYSPROP_DISPLAY_YRES)) / 480 + 1; - sprintf(temp, "%i", std::min(scale, max_res)); + snprintf(temp, sizeof(temp), "%d", std::min(scale, max_res)); return std::string(temp); } else if (query == "force44khz") { return std::string("0"); diff --git a/android/jni/Android.mk b/android/jni/Android.mk index 6ff5076007..b61f43730c 100644 --- a/android/jni/Android.mk +++ b/android/jni/Android.mk @@ -99,8 +99,14 @@ ARCH_FILES := \ ArmEmitterTest.cpp endif +EGL_FILES := \ + $(SRC)/Common/GL/GLInterface/EGL.cpp \ + $(SRC)/Common/GL/GLInterface/EGLAndroid.cpp \ + $(SRC)/Common/GL/GLInterface/GLInterface.cpp + EXEC_AND_LIB_FILES := \ $(ARCH_FILES) \ + $(EGL_FILES) \ TestRunner.cpp \ $(SRC)/Core/MIPS/MIPS.cpp.arm \ $(SRC)/Core/MIPS/MIPSAnalyst.cpp \ diff --git a/android/jni/Locals.mk b/android/jni/Locals.mk index 377a6a5f7b..1e566d3298 100644 --- a/android/jni/Locals.mk +++ b/android/jni/Locals.mk @@ -15,7 +15,7 @@ LOCAL_C_INCLUDES := \ $(LOCAL_PATH) LOCAL_STATIC_LIBRARIES := native libzip -LOCAL_LDLIBS := -lz -lGLESv2 -lOpenSLES -lEGL -ldl -llog +LOCAL_LDLIBS := -lz -landroid -lGLESv2 -lOpenSLES -lEGL -ldl -llog # ifeq ($(TARGET_ARCH_ABI),armeabi-v7a) ifeq ($(findstring armeabi-v7a,$(TARGET_ARCH_ABI)),armeabi-v7a) diff --git a/android/jni/app-android.cpp b/android/jni/app-android.cpp index c41ba41c90..8c51c2cfcd 100644 --- a/android/jni/app-android.cpp +++ b/android/jni/app-android.cpp @@ -6,10 +6,12 @@ #include #include +#include #include #include #include #include +#include #include #include "base/basictypes.h" @@ -27,6 +29,8 @@ #include "android/jni/native_audio.h" #include "gfx/gl_common.h" +#include "Common/GL/GLInterfaceBase.h" + #include "app-android.h" static JNIEnv *jniEnvUI; @@ -66,6 +70,8 @@ static int display_yres; static jmethodID postCommand; static jobject nativeActivity; +static volatile bool exitRenderLoop; +bool renderLoopRunning; // Android implementation of callbacks to the Java part of the app void SystemToast(const char *text) { @@ -313,18 +319,7 @@ extern "C" void Java_org_ppsspp_ppsspp_NativeApp_shutdown(JNIEnv *, jclass) { ILOG("NativeApp.shutdown() -- end"); } -extern "C" void Java_org_ppsspp_ppsspp_NativeRenderer_displayInit(JNIEnv * env, jobject obj) { - ILOG("NativeApp.displayInit()"); - if (!renderer_inited) { - NativeInitGraphics(); - renderer_inited = true; - } else { - NativeDeviceLost(); // ??? - ILOG("displayInit: NativeDeviceLost completed."); - } -} - -extern "C" void Java_org_ppsspp_ppsspp_NativeRenderer_displayResize(JNIEnv *, jobject clazz, jint w, jint h, jint dpi, jfloat refreshRate) { +extern "C" void Java_org_ppsspp_ppsspp_NativeApp_displayResize(JNIEnv *, jclass, jint w, jint h, jint dpi, jfloat refreshRate) { ILOG("NativeApp.displayResize(%i x %i, dpi=%i, refresh=%0.2f)", w, h, dpi, refreshRate); g_dpi = dpi; @@ -341,69 +336,8 @@ extern "C" void Java_org_ppsspp_ppsspp_NativeRenderer_displayResize(JNIEnv *, jo NativeResized(); } -extern "C" void Java_org_ppsspp_ppsspp_NativeRenderer_displayRender(JNIEnv *env, jobject obj) { - static bool hasSetThreadName = false; - if (!hasSetThreadName) { - hasSetThreadName = true; - setCurrentThreadName("AndroidRender"); - } - - if (renderer_inited) { - // TODO: Look into if these locks are a perf loss - { - lock_guard guard(input_state.lock); - - input_state.pad_lstick_x = left_joystick_x_async; - input_state.pad_lstick_y = left_joystick_y_async; - input_state.pad_rstick_x = right_joystick_x_async; - input_state.pad_rstick_y = right_joystick_y_async; - - UpdateInputState(&input_state); - } - NativeUpdate(input_state); - - { - lock_guard guard(input_state.lock); - EndInputState(&input_state); - } - - NativeRender(); - time_update(); - } else { - ELOG("BAD: Ended up in nativeRender even though app has quit.%s", ""); - // Shouldn't really get here. Let's draw magenta. - glDepthMask(GL_TRUE); - glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); - glClearColor(1.0, 0.0, 1.0f, 1.0f); - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); - } - - lock_guard guard(frameCommandLock); - if (!nativeActivity) { - while (!frameCommands.empty()) - frameCommands.pop(); - return; - } - while (!frameCommands.empty()) { - FrameCommand frameCmd; - frameCmd = frameCommands.front(); - frameCommands.pop(); - - DLOG("frameCommand %s %s", frameCmd.command.c_str(), frameCmd.params.c_str()); - - jstring cmd = env->NewStringUTF(frameCmd.command.c_str()); - jstring param = env->NewStringUTF(frameCmd.params.c_str()); - env->CallVoidMethod(nativeActivity, postCommand, cmd, param); - env->DeleteLocalRef(cmd); - env->DeleteLocalRef(param); - } -} - extern "C" void Java_org_ppsspp_ppsspp_NativeRenderer_displayShutdown(JNIEnv *env, jobject obj) { if (renderer_inited) { - NativeDeviceLost(); - ILOG("NativeDeviceLost completed."); - NativeShutdownGraphics(); renderer_inited = false; NativeMessageReceived("recreateviews", ""); } @@ -538,7 +472,7 @@ extern "C" jboolean JNICALL Java_org_ppsspp_ppsspp_NativeApp_accelerometer(JNIEn return retvalX || retvalY || retvalZ; } -extern "C" void Java_org_ppsspp_ppsspp_NativeApp_sendMessage(JNIEnv *env, jclass, jstring message, jstring param) { +extern "C" void JNICALL Java_org_ppsspp_ppsspp_NativeApp_sendMessage(JNIEnv *env, jclass, jstring message, jstring param) { std::string msg = GetJavaString(env, message); std::string prm = GetJavaString(env, param); @@ -547,3 +481,99 @@ extern "C" void Java_org_ppsspp_ppsspp_NativeApp_sendMessage(JNIEnv *env, jclass } NativeMessageReceived(msg.c_str(), prm.c_str()); } + +extern "C" void JNICALL Java_org_ppsspp_ppsspp_NativeActivity_exitEGLRenderLoop(JNIEnv *env, jobject obj) { + if (!renderLoopRunning) { + ELOG("Render loop already exited"); + return; + } + exitRenderLoop = true; + while (renderLoopRunning) { + sleep_ms(10); + } +} + +extern "C" void JNICALL Java_org_ppsspp_ppsspp_NativeActivity_runEGLRenderLoop(JNIEnv *env, jobject obj, jobject _surf) { + ANativeWindow *wnd = ANativeWindow_fromSurface(env, _surf); + + WLOG("runEGLRenderLoop"); + + if (wnd == nullptr) { + ELOG("Error: Surface is null."); + return; + } + + cInterfaceBase *gl = HostGL_CreateGLInterface(); + if (!gl) { + ELOG("ERROR: Failed to create GL interface"); + return; + } + gl->SetMode(MODE_DETECT); + gl->Create(wnd); + gl->MakeCurrent(); + + if (!renderer_inited) { + NativeInitGraphics(); + renderer_inited = true; + } + + exitRenderLoop = false; + renderLoopRunning = true; + + while (!exitRenderLoop) { + static bool hasSetThreadName = false; + if (!hasSetThreadName) { + hasSetThreadName = true; + setCurrentThreadName("AndroidRender"); + } + + // TODO: Look into if these locks are a perf loss + { + lock_guard guard(input_state.lock); + + input_state.pad_lstick_x = left_joystick_x_async; + input_state.pad_lstick_y = left_joystick_y_async; + input_state.pad_rstick_x = right_joystick_x_async; + input_state.pad_rstick_y = right_joystick_y_async; + + UpdateInputState(&input_state); + } + NativeUpdate(input_state); + + { + lock_guard guard(input_state.lock); + EndInputState(&input_state); + } + + NativeRender(); + time_update(); + + gl->Swap(); + + lock_guard guard(frameCommandLock); + while (!frameCommands.empty()) { + FrameCommand frameCmd; + frameCmd = frameCommands.front(); + frameCommands.pop(); + + WLOG("frameCommand! '%s' '%s'", frameCmd.command.c_str(), frameCmd.params.c_str()); + + jstring cmd = env->NewStringUTF(frameCmd.command.c_str()); + jstring param = env->NewStringUTF(frameCmd.params.c_str()); + env->CallVoidMethod(nativeActivity, postCommand, cmd, param); + env->DeleteLocalRef(cmd); + env->DeleteLocalRef(param); + } + } + + NativeDeviceLost(); + ILOG("NativeDeviceLost completed."); + NativeShutdownGraphics(); + renderer_inited = false; + + delete gl; + + ANativeWindow_release(wnd); + renderLoopRunning = false; + WLOG("Render loop exited;"); +} diff --git a/android/src/org/ppsspp/ppsspp/NativeActivity.java b/android/src/org/ppsspp/ppsspp/NativeActivity.java index f9017ba0cb..41f6b8509c 100644 --- a/android/src/org/ppsspp/ppsspp/NativeActivity.java +++ b/android/src/org/ppsspp/ppsspp/NativeActivity.java @@ -37,6 +37,8 @@ import android.view.InputEvent; import android.view.KeyEvent; import android.view.HapticFeedbackConstants; import android.view.MotionEvent; +import android.view.Surface; +import android.view.SurfaceHolder; import android.view.View; import android.view.View.OnSystemUiVisibilityChangeListener; import android.view.Window; @@ -46,57 +48,54 @@ import android.widget.EditText; import android.widget.FrameLayout; import android.widget.Toast; -public class NativeActivity extends Activity { +public class NativeActivity extends Activity implements SurfaceHolder.Callback { // Remember to loadLibrary your JNI .so in a static {} block // Adjust these as necessary private static String TAG = "NativeActivity"; - + // Allows us to skip a lot of initialization on secondary calls to onCreate. private static boolean initialized = false; - + // Graphics and audio interfaces - private NativeGLView mGLSurfaceView; - protected NativeRenderer nativeRenderer; - + private NativeSurfaceView mSurfaceView; + private Surface mSurface; + private Thread mRenderLoopThread; + private String shortcutParam = ""; - + public static String runCommand; public static String commandParameter; public static String installID; - + // Remember settings for best audio latency private int optimalFramesPerBuffer; private int optimalSampleRate; - + // audioFocusChangeListener to listen to changes in audio state private AudioFocusChangeListener audioFocusChangeListener; private AudioManager audioManager; - + private Vibrator vibrator; private boolean isXperiaPlay; - + // Allow for multiple connected gamepads but just consider them the same for now. // Actually this is not entirely true, see the code. InputDeviceState inputPlayerA; InputDeviceState inputPlayerB; InputDeviceState inputPlayerC; String inputPlayerADesc; - + // Functions for the app activity to override to change behaviour. - + public native void registerCallbacks(); public native void unregisterCallbacks(); public boolean useLowProfileButtons() { return true; } - - NativeRenderer getRenderer() { - return nativeRenderer; - } - + @TargetApi(17) private void detectOptimalAudioSettings() { try { @@ -110,8 +109,8 @@ public class NativeActivity extends Activity { // Ignore, if we can't parse it it's bogus and zero is a fine value (means we couldn't detect it). } } - - String getApplicationLibraryDir(ApplicationInfo application) { + + String getApplicationLibraryDir(ApplicationInfo application) { String libdir = null; try { // Starting from Android 2.3, nativeLibraryDir is available: @@ -159,28 +158,28 @@ public class NativeActivity extends Activity { size.y = d.getHeight(); } } - + public void setShortcutParam(String shortcutParam) { this.shortcutParam = ((shortcutParam == null) ? "" : shortcutParam); } - + public void Initialize() { // Initialize audio classes. Do this here since detectOptimalAudioSettings() // needs audioManager this.audioManager = (AudioManager)getSystemService(Context.AUDIO_SERVICE); this.audioFocusChangeListener = new AudioFocusChangeListener(); - + if (Build.VERSION.SDK_INT >= 17) { // Get the optimal buffer sz detectOptimalAudioSettings(); } - // isLandscape is used to trigger GetAppInfo currently, we + // isLandscape is used to trigger GetAppInfo currently, we boolean landscape = NativeApp.isLandscape(); Log.d(TAG, "Landscape: " + landscape); - + // Get system information - ApplicationInfo appInfo = null; + ApplicationInfo appInfo = null; PackageManager packMgmr = getPackageManager(); String packageName = getPackageName(); try { @@ -205,16 +204,16 @@ public class NativeActivity extends Activity { } isXperiaPlay = IsXperiaPlay(); - + String libraryDir = getApplicationLibraryDir(appInfo); File sdcard = Environment.getExternalStorageDirectory(); - String externalStorageDir = sdcard.getAbsolutePath(); + String externalStorageDir = sdcard.getAbsolutePath(); String dataDir = this.getFilesDir().getAbsolutePath(); - String apkFilePath = appInfo.sourceDir; + String apkFilePath = appInfo.sourceDir; String model = Build.MANUFACTURER + ":" + Build.MODEL; - String languageRegion = Locale.getDefault().getLanguage() + "_" + Locale.getDefault().getCountry(); + String languageRegion = Locale.getDefault().getLanguage() + "_" + Locale.getDefault().getCountry(); Point displaySize = new Point(); GetScreenSize(displaySize); @@ -223,24 +222,6 @@ public class NativeActivity extends Activity { NativeApp.sendMessage("cacheDir", getCacheDir().getAbsolutePath()); - // OK, config should be initialized, we can query for screen rotation. - if (Build.VERSION.SDK_INT >= 9) { - updateScreenRotation(); - } - - // Detect OpenGL support. - // We don't currently use this detection for anything but good to have in the log. - if (!detectOpenGLES20()) { - Log.i(TAG, "OpenGL ES 2.0 NOT detected. Things will likely go badly."); - } else { - if (detectOpenGLES30()) { - Log.i(TAG, "OpenGL ES 3.0 detected."); - } - else { - Log.i(TAG, "OpenGL ES 2.0 detected."); - } - } - vibrator = (Vibrator)getSystemService(VIBRATOR_SERVICE); if (Build.VERSION.SDK_INT >= 11) { checkForVibrator(); @@ -258,6 +239,7 @@ public class NativeActivity extends Activity { Log.e(TAG, "Invalid rotation: " + rotString); return; } + Log.i(TAG, "Requested rotation: " + rot + " ('" + rotString + "')"); switch (rot) { case 0: setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED); @@ -276,7 +258,7 @@ public class NativeActivity extends Activity { break; } } - + private boolean useImmersive() { String immersive = NativeApp.queryConfig("immersiveMode"); return immersive.equals("1") && Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT; @@ -298,7 +280,7 @@ public class NativeActivity extends Activity { Log.e(TAG, "updateSystemUiVisibility: decor view not yet created, ignoring"); } } - + // Need API 11 to check for existence of a vibrator? Zany. @TargetApi(11) public void checkForVibrator() { @@ -315,9 +297,37 @@ public class NativeActivity extends Activity { sz.y = 0; } + private Runnable mEmulationRunner = new Runnable() + { + @Override + public void run() + { + // Bit of a hack - loop until onSurfaceCreated succeeds. + try { + while (mSurface == null) + { + Thread.sleep(10); + } + } catch (InterruptedException e) { + e.printStackTrace(); + } + + Log.i(TAG, "Starting the render loop: " + mSurface); + + // Start emulation using the provided Surface. + runEGLRenderLoop(mSurface); + + Log.i(TAG, "Left the render loop: " + mSurface); + } + }; + + public native void runEGLRenderLoop(Surface surface); + // Tells the render loop thread to exit, so we can restart it. + public native void exitEGLRenderLoop(); + @Override public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); + super.onCreate(savedInstanceState); registerCallbacks(); installID = Installation.id(this); @@ -325,55 +335,32 @@ public class NativeActivity extends Activity { Initialize(); initialized = true; } + + // OK, config should be initialized, we can query for screen rotation. + if (Build.VERSION.SDK_INT >= 9) { + updateScreenRotation(); + } + // Keep the screen bright - very annoying if it goes dark when tilting away Window window = this.getWindow(); window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); setVolumeControlStream(AudioManager.STREAM_MUSIC); - + gainAudioFocus(this.audioManager, this.audioFocusChangeListener); NativeApp.audioInit(); - - mGLSurfaceView = new NativeGLView(this); - nativeRenderer = new NativeRenderer(this); + + mSurfaceView = new NativeSurfaceView(this); + mSurfaceView.getHolder().addCallback(this); Point sz = new Point(); getDesiredBackbufferSize(sz); if (sz.x > 0) { Log.i(TAG, "Requesting fixed size buffer: " + sz.x + "x" + sz.y); // Auto-calculates new DPI and forwards to the correct call on mGLSurfaceView.getHolder() - nativeRenderer.setFixedSize(sz.x, sz.y, mGLSurfaceView); + mSurfaceView.setFixedSize(sz.x, sz.y); } - mGLSurfaceView.setEGLContextClientVersion(2); - - // Setup the GLSurface and ask android for the correct - // Number of bits for r, g, b, a, depth and stencil components - // The PSP only has 16-bit Z so that should be enough. - // Might want to change this for other apps (24-bit might be useful). - // Actually, we might be able to do without both stencil and depth in - // the back buffer, but that would kill non-buffered rendering. - - // It appears some gingerbread devices blow up if you use a config chooser at all ???? (Xperia Play) - //if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { - - // On some (especially older devices), things blow up later (EGL_BAD_MATCH) if we don't set the format here, - // if we specify that we want destination alpha in the config chooser, which we do. - // http://grokbase.com/t/gg/android-developers/11bj40jm4w/fall-back - - - // Needed to avoid banding on Ouya? - if (Build.MANUFACTURER == "OUYA") { - mGLSurfaceView.getHolder().setFormat(PixelFormat.RGBX_8888); - mGLSurfaceView.setEGLConfigChooser(new NativeEGLConfigChooser()); - } else { - // Many devices require that we set a config chooser, despite the documentation - // explicitly stating: "If no setEGLConfigChooser method is called, then by default the view will choose an RGB_888 surface with a depth buffer depth of at least 16 bits." - // On these devices, I get these crashes: http://stackoverflow.com/questions/14167319/android-opengl-demo-no-config-chosen - // So let's try it... - mGLSurfaceView.setEGLConfigChooser(8, 8, 8, 8, 16, 8); - } - - mGLSurfaceView.setRenderer(nativeRenderer); - setContentView(mGLSurfaceView); + + setContentView(mSurfaceView); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) { updateSystemUiVisibility(); @@ -381,8 +368,46 @@ public class NativeActivity extends Activity { setupSystemUiCallback(); } } + + mRenderLoopThread = new Thread(mEmulationRunner); + mRenderLoopThread.start(); } + @Override + public void surfaceCreated(SurfaceHolder holder) + { + Log.d(TAG, "Surface created."); + } + + @Override + public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) + { + Log.w(TAG, "Surface changed. Resolution: " + width + "x" + height); + mSurface = holder.getSurface(); + if (mRenderLoopThread == null || !mRenderLoopThread.isAlive()) { + mRenderLoopThread = new Thread(mEmulationRunner); + mRenderLoopThread.start(); + } + } + + @Override + public void surfaceDestroyed(SurfaceHolder holder) + { + mSurface = null; + Log.w(TAG, "Surface destroyed."); + if (mRenderLoopThread != null && mRenderLoopThread.isAlive()) { + // This will wait until the thread has exited. + exitEGLRenderLoop(); + try { + mRenderLoopThread.join(); + } catch (InterruptedException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + } + + @TargetApi(19) void setupSystemUiCallback() { getWindow().getDecorView().setOnSystemUiVisibilityChangeListener(new OnSystemUiVisibilityChangeListener() { @@ -394,27 +419,27 @@ public class NativeActivity extends Activity { } }); } - + @Override protected void onStop() { - super.onStop(); + exitEGLRenderLoop(); + super.onStop(); Log.i(TAG, "onStop - do nothing special"); - } + } @Override protected void onDestroy() { super.onDestroy(); Log.i(TAG, "onDestroy"); - mGLSurfaceView.onDestroy(); - nativeRenderer.onDestroyed(); + mSurfaceView.onDestroy(); NativeApp.audioShutdown(); // Probably vain attempt to help the garbage collector... - mGLSurfaceView = null; + mSurfaceView = null; audioFocusChangeListener = null; audioManager = null; unregisterCallbacks(); - } - + } + private boolean detectOpenGLES20() { ActivityManager am = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE); ConfigurationInfo info = am.getDeviceConfigurationInfo(); @@ -426,16 +451,16 @@ public class NativeActivity extends Activity { ConfigurationInfo info = am.getDeviceConfigurationInfo(); return info.reqGlEsVersion >= 0x30000; } - - @Override + + @Override protected void onPause() { super.onPause(); Log.i(TAG, "onPause"); loseAudioFocus(this.audioManager, this.audioFocusChangeListener); NativeApp.pause(); - mGLSurfaceView.onPause(); + mSurfaceView.onPause(); } - + @Override protected void onResume() { super.onResume(); @@ -445,30 +470,30 @@ public class NativeActivity extends Activity { // OK, config should be initialized, we can query for screen rotation. if (Build.VERSION.SDK_INT >= 9) { updateScreenRotation(); - } + } Log.i(TAG, "onResume"); - if (mGLSurfaceView != null) { - mGLSurfaceView.onResume(); + if (mSurfaceView != null) { + mSurfaceView.onResume(); } else { Log.e(TAG, "mGLSurfaceView really shouldn't be null in onResume"); } - + gainAudioFocus(this.audioManager, this.audioFocusChangeListener); NativeApp.resume(); } - + @Override public void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) { updateSystemUiVisibility(); } - + Point sz = new Point(); getDesiredBackbufferSize(sz); if (sz.x > 0) { - mGLSurfaceView.getHolder().setFixedSize(sz.x/2, sz.y/2); + mSurfaceView.getHolder().setFixedSize(sz.x/2, sz.y/2); } } @@ -480,7 +505,7 @@ public class NativeActivity extends Activity { AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN); } } - + //keep this static so we can call this even if we don't //instantiate NativeAudioPlayer public static void loseAudioFocus(AudioManager audioManager,AudioFocusChangeListener focusChangeListener){ @@ -488,7 +513,7 @@ public class NativeActivity extends Activity { audioManager.abandonAudioFocus(focusChangeListener); } } - + // We simply grab the first input device to produce an event and ignore all others that are connected. @TargetApi(Build.VERSION_CODES.GINGERBREAD) private InputDeviceState getInputDeviceState(InputEvent event) { @@ -579,11 +604,11 @@ public class NativeActivity extends Activity { } } } - + // Let's go through the old path (onKeyUp, onKeyDown). return super.dispatchKeyEvent(event); - } - + } + @TargetApi(16) static public String getInputDesc(InputDevice input) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { @@ -648,7 +673,7 @@ public class NativeActivity extends Activity { case KeyEvent.KEYCODE_SEARCH: NativeApp.keyDown(0, keyCode, repeat); return true; - + case KeyEvent.KEYCODE_DPAD_UP: case KeyEvent.KEYCODE_DPAD_DOWN: case KeyEvent.KEYCODE_DPAD_LEFT: @@ -703,12 +728,12 @@ public class NativeActivity extends Activity { } } - + @TargetApi(11) private AlertDialog.Builder createDialogBuilderWithTheme() { return new AlertDialog.Builder(this, AlertDialog.THEME_HOLO_DARK); } - + // The return value is sent elsewhere. TODO in java, in SendMessage in C++. public void inputBox(String title, String defaultText, String defaultAction) { final FrameLayout fl = new FrameLayout(this); @@ -722,7 +747,7 @@ public class NativeActivity extends Activity { input.setInputType(InputType.TYPE_CLASS_TEXT); input.setText(defaultText); input.selectAll(); - + AlertDialog.Builder bld = null; if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) bld = new AlertDialog.Builder(this); @@ -733,22 +758,24 @@ public class NativeActivity extends Activity { .setView(fl) .setTitle(title) .setPositiveButton(defaultAction, new DialogInterface.OnClickListener(){ - public void onClick(DialogInterface d, int which) { + @Override + public void onClick(DialogInterface d, int which) { NativeApp.sendMessage("inputbox_completed", input.getText().toString()); d.dismiss(); } }) .setNegativeButton("Cancel", new DialogInterface.OnClickListener(){ - public void onClick(DialogInterface d, int which) { + @Override + public void onClick(DialogInterface d, int which) { NativeApp.sendMessage("inputbox_failed", ""); d.cancel(); } }).create(); - + dlg.setCancelable(true); dlg.show(); } - + public boolean processCommand(String command, String params) { if (command.equals("launchBrowser")) { try { @@ -825,18 +852,18 @@ public class NativeActivity extends Activity { toast.show(); Log.i(TAG, params); return true; - } else if (command.equals("showKeyboard") && mGLSurfaceView != null) { + } else if (command.equals("showKeyboard") && mSurfaceView != null) { InputMethodManager inputMethodManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); // No idea what the point of the ApplicationWindowToken is or if it // matters where we get it from... inputMethodManager.toggleSoftInputFromWindow( - mGLSurfaceView.getApplicationWindowToken(), + mSurfaceView.getApplicationWindowToken(), InputMethodManager.SHOW_FORCED, 0); return true; - } else if (command.equals("hideKeyboard") && mGLSurfaceView != null) { + } else if (command.equals("hideKeyboard") && mSurfaceView != null) { InputMethodManager inputMethodManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); inputMethodManager.toggleSoftInputFromWindow( - mGLSurfaceView.getApplicationWindowToken(), + mSurfaceView.getApplicationWindowToken(), InputMethodManager.SHOW_FORCED, 0); return true; } else if (command.equals("inputbox")) { @@ -850,7 +877,7 @@ public class NativeActivity extends Activity { Log.i(TAG, "Launching inputbox: " + title + " " + defString); inputBox(title, defString, "OK"); return true; - } else if (command.equals("vibrate") && mGLSurfaceView != null) { + } else if (command.equals("vibrate") && mSurfaceView != null) { int milliseconds = -1; if (params != "") { try { @@ -867,13 +894,13 @@ public class NativeActivity extends Activity { // permission. switch (milliseconds) { case -1: - mGLSurfaceView.performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP); + mSurfaceView.performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP); break; case -2: - mGLSurfaceView.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY); + mSurfaceView.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY); break; case -3: - mGLSurfaceView.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS); + mSurfaceView.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS); break; default: if (vibrator != null) { @@ -887,12 +914,13 @@ public class NativeActivity extends Activity { } else if (command.equals("rotate")) { if (Build.VERSION.SDK_INT >= 9) { updateScreenRotation(); - } + } } else if (command.equals("immersive")) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { updateSystemUiVisibility(); } } else if (command.equals("recreate")) { + exitEGLRenderLoop(); recreate(); } return false; diff --git a/android/src/org/ppsspp/ppsspp/NativeApp.java b/android/src/org/ppsspp/ppsspp/NativeApp.java index 9265f6a47c..0b59fb2d71 100644 --- a/android/src/org/ppsspp/ppsspp/NativeApp.java +++ b/android/src/org/ppsspp/ppsspp/NativeApp.java @@ -1,24 +1,25 @@ package org.ppsspp.ppsspp; -// Note that the display* methods are in NativeRenderer.java +// Note that the display* methods are in NativeRenderer.java public class NativeApp { - public final static int DEVICE_ID_DEFAULT = 0; - public final static int DEVICE_ID_KEYBOARD = 1; - public final static int DEVICE_ID_MOUSE = 2; - public final static int DEVICE_ID_PAD_0 = 10; - + public final static int DEVICE_ID_DEFAULT = 0; + public final static int DEVICE_ID_KEYBOARD = 1; + public final static int DEVICE_ID_MOUSE = 2; + public final static int DEVICE_ID_PAD_0 = 10; + public final static int DEVICE_TYPE_MOBILE = 0; public final static int DEVICE_TYPE_TV = 1; public final static int DEVICE_TYPE_DESKTOP = 2; public static native void init(String model, int deviceType, int xres, int yres, String languageRegion, String apkPath, String dataDir, String externalDir, String libraryDir, String shortcutParam, String installID, int androidVersion); - + public static native void audioInit(); public static native void audioShutdown(); public static native void audioConfig(int optimalFramesPerBuffer, int optimalSampleRate); - + + public static native void displayResize(int width, int height, int scaled_dpi, float refreshRate); public static native boolean isLandscape(); public static native boolean isAtTopLevel(); @@ -35,7 +36,7 @@ public class NativeApp { public static native void beginJoystickEvent(); public static native void joystickAxis(int deviceId, int axis, float value); public static native void endJoystickEvent(); - + public static native boolean mouseWheelEvent(float x, float y); // will only be called between init() and shutdown() @@ -47,7 +48,6 @@ public class NativeApp { public static native boolean accelerometer(float x, float y, float z); public static native void sendMessage(String msg, String arg); - + public static native String queryConfig(String queryName); } - \ No newline at end of file diff --git a/android/src/org/ppsspp/ppsspp/NativeEGLConfigChooser.java b/android/src/org/ppsspp/ppsspp/NativeEGLConfigChooser.java deleted file mode 100644 index fe4d09fe0d..0000000000 --- a/android/src/org/ppsspp/ppsspp/NativeEGLConfigChooser.java +++ /dev/null @@ -1,205 +0,0 @@ -package org.ppsspp.ppsspp; - -import javax.microedition.khronos.egl.EGL10; -import javax.microedition.khronos.egl.EGLConfig; -import javax.microedition.khronos.egl.EGLDisplay; - -import android.opengl.GLSurfaceView.EGLConfigChooser; -import android.util.Log; - -public class NativeEGLConfigChooser implements EGLConfigChooser { - private static final String TAG = "NativeEGLConfigChooser"; - - private static final int EGL_OPENGL_ES2_BIT = 4; - - private class ConfigAttribs { - EGLConfig config; - public int red; - public int green; - public int blue; - public int alpha; - public int stencil; - public int depth; - public int samples; - public void Log() { - Log.i(TAG, "EGLConfig: red=" + red + " green=" + green + " blue=" + blue + " alpha=" + alpha + " depth=" + depth + " stencil=" + stencil + " samples=" + samples); - } - } - - int getEglConfigAttrib(EGL10 egl, EGLDisplay display, EGLConfig config, int attr) { - int[] value = new int[1]; - try { - if (egl.eglGetConfigAttrib(display, config, attr, value)) - return value[0]; - else - return -1; - } catch (IllegalArgumentException e) { - if (config == null) { - Log.e(TAG, "Called getEglConfigAttrib with null config. Bad developer."); - } else { - Log.e(TAG, "Illegal argument to getEglConfigAttrib: attr=" + attr); - } - return -1; - } - } - - ConfigAttribs[] getConfigAttribs(EGL10 egl, EGLDisplay display, EGLConfig[] configs) { - ConfigAttribs[] attr = new ConfigAttribs[configs.length]; - for (int i = 0; i < configs.length; i++) { - ConfigAttribs cfg = new ConfigAttribs(); - cfg.config = configs[i]; - cfg.red = getEglConfigAttrib(egl, display, configs[i], EGL10.EGL_RED_SIZE); - cfg.green = getEglConfigAttrib(egl, display, configs[i], EGL10.EGL_GREEN_SIZE); - cfg.blue = getEglConfigAttrib(egl, display, configs[i], EGL10.EGL_BLUE_SIZE); - cfg.alpha = getEglConfigAttrib(egl, display, configs[i], EGL10.EGL_ALPHA_SIZE); - cfg.depth = getEglConfigAttrib(egl, display, configs[i], EGL10.EGL_DEPTH_SIZE); - cfg.stencil = getEglConfigAttrib(egl, display, configs[i], EGL10.EGL_STENCIL_SIZE); - cfg.samples = getEglConfigAttrib(egl, display, configs[i], EGL10.EGL_SAMPLES); - attr[i] = cfg; - } - return attr; - } - - public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display) { - // The absolute minimum. We will do our best to choose a better config though. - int[] configSpec = { - EGL10.EGL_RED_SIZE, 5, - EGL10.EGL_GREEN_SIZE, 6, - EGL10.EGL_BLUE_SIZE, 5, - EGL10.EGL_DEPTH_SIZE, 16, - EGL10.EGL_STENCIL_SIZE, 0, - EGL10.EGL_SURFACE_TYPE, EGL10.EGL_WINDOW_BIT, - EGL10.EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, - EGL10.EGL_NONE - }; - - int[] num_config = new int[1]; - if (!egl.eglChooseConfig(display, configSpec, null, 0, num_config)) { - throw new IllegalArgumentException("eglChooseConfig failed when counting"); - } - - int numConfigs = num_config[0]; - Log.i(TAG, "There are " + numConfigs + " egl configs"); - if (numConfigs <= 0) { - throw new IllegalArgumentException("No configs match configSpec"); - } - - EGLConfig[] eglConfigs = new EGLConfig[numConfigs]; - if (!egl.eglChooseConfig(display, configSpec, eglConfigs, numConfigs, num_config)) { - throw new IllegalArgumentException("eglChooseConfig failed when retrieving"); - } - - ConfigAttribs [] configs = getConfigAttribs(egl, display, eglConfigs); - - ConfigAttribs chosen = null; - - // Log them all. - for (int i = 0; i < configs.length; i++) { - configs[i].Log(); - } - - // We now ignore destination alpha as a workaround for the Mali issue - // where we get badly composited if we use it. - - // First, find our ideal configuration. Prefer depth. - for (int i = 0; i < configs.length; i++) { - ConfigAttribs c = configs[i]; - if (c.red == 8 && c.green == 8 && c.blue == 8 && c.alpha == 0 && c.stencil >= 8 && c.depth >= 24) { - chosen = c; - break; - } - } - - if (chosen == null) { - // Then, prefer one with 20-bit depth (Tegra 3) - for (int i = 0; i < configs.length; i++) { - ConfigAttribs c = configs[i]; - if (c.red == 8 && c.green == 8 && c.blue == 8 && c.alpha == 0 && c.stencil >= 8 && c.depth >= 20) { - chosen = c; - break; - } - } - } - - if (chosen == null) { - // Second, accept one with 16-bit depth. - for (int i = 0; i < configs.length; i++) { - ConfigAttribs c = configs[i]; - if (c.red == 8 && c.green == 8 && c.blue == 8 && c.alpha == 0 && c.stencil >= 8 && c.depth >= 16) { - chosen = c; - break; - } - } - } - - if (chosen == null) { - // Third, accept one with no stencil. - for (int i = 0; i < configs.length; i++) { - ConfigAttribs c = configs[i]; - if (c.red == 8 && c.green == 8 && c.blue == 8 && c.alpha == 0 && c.depth >= 16) { - chosen = c; - break; - } - } - } - - if (chosen == null) { - // Third, accept one with alpha but with stencil, 24-bit depth. - for (int i = 0; i < configs.length; i++) { - ConfigAttribs c = configs[i]; - if (c.red == 8 && c.green == 8 && c.blue == 8 && c.alpha == 8 && c.stencil >= 8 && c.depth >= 24) { - chosen = c; - break; - } - } - } - - if (chosen == null) { - // Third, accept one with alpha but with stencil, 16-bit depth. - for (int i = 0; i < configs.length; i++) { - ConfigAttribs c = configs[i]; - if (c.red == 8 && c.green == 8 && c.blue == 8 && c.alpha == 8 && c.stencil >= 8 && c.depth >= 16) { - chosen = c; - break; - } - } - } - - if (chosen == null) { - // Fourth, accept one with 16-bit color but depth and stencil required. - for (int i = 0; i < configs.length; i++) { - ConfigAttribs c = configs[i]; - if (c.red >= 5 && c.green >= 6 && c.blue >= 5 && c.depth >= 16 && c.stencil >= 8) { - chosen = c; - break; - } - } - } - - if (chosen == null) { - // Fifth, accept one with 16-bit color but depth required. - for (int i = 0; i < configs.length; i++) { - ConfigAttribs c = configs[i]; - if (c.red >= 5 && c.green >= 6 && c.blue >= 5 && c.depth >= 16) { - chosen = c; - break; - } - } - } - - if (chosen == null) { - // Final, accept the first one in the list. - if (configs.length > 0) - chosen = configs[0]; - } - - if (chosen == null) { - throw new IllegalArgumentException("Failed to find a valid EGL config"); - } - - Log.i(TAG, "Final chosen config: "); - chosen.Log(); - return chosen.config; - } - -} diff --git a/android/src/org/ppsspp/ppsspp/NativeRenderer.java b/android/src/org/ppsspp/ppsspp/NativeRenderer.java deleted file mode 100644 index ea2a021958..0000000000 --- a/android/src/org/ppsspp/ppsspp/NativeRenderer.java +++ /dev/null @@ -1,94 +0,0 @@ -package org.ppsspp.ppsspp; - -import javax.microedition.khronos.egl.EGLConfig; -import javax.microedition.khronos.opengles.GL10; - -import android.graphics.Point; -import android.opengl.GLES20; -import android.opengl.GLSurfaceView; -import android.util.DisplayMetrics; -import android.util.Log; -import android.view.Display; - -public class NativeRenderer implements GLSurfaceView.Renderer { - private static String TAG = "NativeRenderer"; - private NativeActivity mActivity; - private boolean isDark = false; - private int dpi; - private float refreshRate; - - private double dpi_scale_x; - private double dpi_scale_y; - - int last_width, last_height; - - NativeRenderer(NativeActivity act) { - mActivity = act; - DisplayMetrics metrics = new DisplayMetrics(); - Display display = act.getWindowManager().getDefaultDisplay(); - display.getMetrics(metrics); - dpi = metrics.densityDpi; - - refreshRate = display.getRefreshRate(); - } - - double getDpiScaleX() { - return dpi_scale_x; - } - double getDpiScaleY() { - return dpi_scale_y; - } - - public void setDark(boolean d) { - isDark = d; - } - - public void setFixedSize(int xres, int yres, GLSurfaceView surfaceView) { - Log.i(TAG, "Setting surface to fixed size " + xres + "x" + yres); - surfaceView.getHolder().setFixedSize(xres, yres); - } - - public void onDrawFrame(GL10 unused /*use GLES20*/) { - if (isDark) { - GLES20.glDisable(GLES20.GL_SCISSOR_TEST); - GLES20.glClearColor(0.0f, 0.0f, 0.0f, 0.0f); - GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT | GLES20.GL_STENCIL_BUFFER_BIT); - } else { - displayRender(); - } - } - - public void onSurfaceCreated(GL10 unused, EGLConfig config) { - // Log.i(TAG, "onSurfaceCreated - EGL context is new or was lost"); - // Actually, it seems that it is here we should recreate lost GL objects. - displayInit(); - } - - public void onSurfaceChanged(GL10 unused, int width, int height) { - Point sz = new Point(); - mActivity.GetScreenSize(sz); - double actualW = sz.x; - double actualH = sz.y; - dpi_scale_x = ((double)width / (double)actualW); - dpi_scale_y = ((double)height / (double)actualH); - Log.i(TAG, "onSurfaceChanged: " + dpi_scale_x + "x" + dpi_scale_y + " (width=" + width + ", actualW=" + actualW); - int scaled_dpi = (int)((double)dpi * dpi_scale_x); - displayResize(width, height, scaled_dpi, refreshRate); - last_width = width; - last_height = height; - } - - // Not override, it's custom. - public void onDestroyed() { - displayShutdown(); - } - - // NATIVE METHODS - - // Note: This also means "device lost" and you should reload - // all buffered objects. - public native void displayInit(); - public native void displayResize(int w, int h, int dpi, float refreshRate); - public native void displayRender(); - public native void displayShutdown(); -} \ No newline at end of file diff --git a/android/src/org/ppsspp/ppsspp/NativeGLView.java b/android/src/org/ppsspp/ppsspp/NativeSurfaceView.java similarity index 81% rename from android/src/org/ppsspp/ppsspp/NativeGLView.java rename to android/src/org/ppsspp/ppsspp/NativeSurfaceView.java index 603569fbfb..8822e86bdb 100644 --- a/android/src/org/ppsspp/ppsspp/NativeGLView.java +++ b/android/src/org/ppsspp/ppsspp/NativeSurfaceView.java @@ -5,6 +5,7 @@ package org.ppsspp.ppsspp; import android.annotation.TargetApi; import android.app.Activity; +import android.graphics.Point; import android.hardware.Sensor; import android.hardware.SensorEvent; import android.hardware.SensorEventListener; @@ -12,48 +13,55 @@ import android.hardware.SensorManager; import android.opengl.GLSurfaceView; import android.os.Build; import android.os.Handler; +import android.util.DisplayMetrics; // import android.os.Build; // import android.util.Log; import android.util.Log; +import android.view.Display; import android.view.MotionEvent; +import android.view.SurfaceView; + import com.bda.controller.*; -public class NativeGLView extends GLSurfaceView implements SensorEventListener, ControllerListener { +public class NativeSurfaceView extends SurfaceView implements SensorEventListener, ControllerListener { private static String TAG = "NativeGLView"; private SensorManager mSensorManager; private Sensor mAccelerometer; private NativeActivity mActivity; - + // Moga controller private Controller mController = null; private boolean isMogaPro = false; - - public NativeGLView(NativeActivity activity) { + private int dpi; + private float refreshRate; + + private double dpi_scale_x; + private double dpi_scale_y; + + int last_width, last_height; + + public int fixedW = 0; + public int fixedH = 0; + + public NativeSurfaceView(NativeActivity activity) { super(activity); + + DisplayMetrics metrics = new DisplayMetrics(); + Display display = activity.getWindowManager().getDefaultDisplay(); + display.getMetrics(metrics); + dpi = metrics.densityDpi; + + refreshRate = display.getRefreshRate(); + mActivity = activity; - /*// TODO: This would be nice. - if (Build.VERSION.SDK_INT >= 11) { - try { - Method method_setPreserveEGLContextOnPause = GLSurfaceView.class.getMethod( - "setPreserveEGLContextOnPause", new Class[] { Boolean.class }); - Log.i(TAG, "Invoking setPreserveEGLContextOnPause"); - method_setPreserveEGLContextOnPause.invoke(this, true); - } catch (NoSuchMethodException e) { - e.printStackTrace(); - } catch (IllegalArgumentException e) { - e.printStackTrace(); - } catch (IllegalAccessException e) { - e.printStackTrace(); - } catch (InvocationTargetException e) { - e.printStackTrace(); - } - }*/ - mSensorManager = (SensorManager)activity.getSystemService(Activity.SENSOR_SERVICE); mAccelerometer = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER); - + mController = Controller.getInstance(activity); + + onResize(display.getWidth(), display.getHeight()); + try { MogaHack.init(mController, activity); Log.i(TAG, "MOGA initialized"); @@ -63,23 +71,43 @@ public class NativeGLView extends GLSurfaceView implements SensorEventListener, } } + void onResize(int width, int height) { + Point sz = new Point(); + mActivity.GetScreenSize(sz); + double actualW = sz.x; + double actualH = sz.y; + dpi_scale_x = (width / actualW); + dpi_scale_y = (height / actualH); + Log.i(TAG, "onSurfaceChanged: " + dpi_scale_x + "x" + dpi_scale_y + " (width=" + width + ", actualW=" + actualW); + int scaled_dpi = (int)(dpi * dpi_scale_x); + NativeApp.displayResize(width, height, scaled_dpi, refreshRate); + last_width = width; + last_height = height; + } + + void setFixedSize(int w, int h) { + fixedW = w; + fixedH = h; + } + @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH) private int getToolType(final MotionEvent ev, int pointer) { return ev.getToolType(pointer); } - + + @Override public boolean onTouchEvent(final MotionEvent ev) { boolean canReadToolType = Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH; int numTouchesHandled = 0; - float scaleX = (float)mActivity.getRenderer().getDpiScaleX(); - float scaleY = (float)mActivity.getRenderer().getDpiScaleY(); + float scaleX = (float)this.dpi_scale_x; + float scaleY = (float)this.dpi_scale_y; for (int i = 0; i < ev.getPointerCount(); i++) { int pid = ev.getPointerId(i); int code = 0; - + final int action = ev.getActionMasked(); - + // These code bits are now the same as the constants in input_state.h. switch (action) { case MotionEvent.ACTION_DOWN: @@ -98,7 +126,7 @@ public class NativeGLView extends GLSurfaceView implements SensorEventListener, default: break; } - + if (code != 0) { if (canReadToolType) { int tool = getToolType(ev, i); @@ -112,9 +140,11 @@ public class NativeGLView extends GLSurfaceView implements SensorEventListener, } // Sensor management + @Override public void onAccuracyChanged(Sensor sensor, int arg1) { } + @Override public void onSensorChanged(SensorEvent event) { if (event.sensor.getType() != Sensor.TYPE_ACCELEROMETER) { return; @@ -122,41 +152,37 @@ public class NativeGLView extends GLSurfaceView implements SensorEventListener, // Can also look at event.timestamp for accuracy magic NativeApp.accelerometer(event.values[0], event.values[1], event.values[2]); } - - @Override + public void onPause() { - super.onPause(); mSensorManager.unregisterListener(this); if (mController != null) { mController.onPause(); } } - - @Override + public void onResume() { - super.onResume(); mSensorManager.registerListener(this, mAccelerometer, SensorManager.SENSOR_DELAY_GAME); if (mController != null) { mController.onResume(); - + // According to the docs, the Moga's state can be inconsistent here. // We should do a one time poll. TODO } } - + public void onDestroy() { if (mController != null) { - mController.exit(); + mController.exit(); } } - + // MOGA Controller - from ControllerListener @Override public void onKeyEvent(KeyEvent event) { // The Moga left stick doubles as a D-pad. This creates mapping conflicts so let's turn it off. // Unfortunately this breaks menu navigation in PPSSPP currently but meh. // This is different on Moga Pro though. - + if (!isMogaPro) { switch (event.getKeyCode()) { case KeyEvent.KEYCODE_DPAD_DOWN: @@ -216,7 +242,7 @@ public class NativeGLView extends GLSurfaceView implements SensorEventListener, break; } break; - + case StateEvent.STATE_POWER_LOW: switch (state.getAction()) { case StateEvent.ACTION_TRUE: diff --git a/android/src/org/ppsspp/ppsspp/PpssppActivity.java b/android/src/org/ppsspp/ppsspp/PpssppActivity.java index f66d1b963d..18e1f4179e 100644 --- a/android/src/org/ppsspp/ppsspp/PpssppActivity.java +++ b/android/src/org/ppsspp/ppsspp/PpssppActivity.java @@ -11,7 +11,7 @@ public class PpssppActivity extends NativeActivity { private static final String TAG = "PpssppActivity"; // Key used by shortcut. public static final String SHORTCUT_EXTRA_KEY = "org.ppsspp.ppsspp.Shortcuts"; - + private static boolean m_hasUnsupportedABI = false; private static boolean m_hasNoNativeBinary = false; @@ -47,15 +47,15 @@ public class PpssppActivity extends NativeActivity { } Looper.loop(); } - + }.start(); - + try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } - + System.exit(-1); return; } @@ -114,7 +114,7 @@ public class PpssppActivity extends NativeActivity { sz.y = 0; return; } - correctRatio(sz, (float)scale); + correctRatio(sz, scale); } // called by the C++ code through JNI. Dispatch anything we can't directly handle @@ -123,6 +123,7 @@ public class PpssppActivity extends NativeActivity { final String cmd = command; final String param = parameter; runOnUiThread(new Runnable() { + @Override public void run() { processCommand(cmd, param); } From 13515bf23531ea767a522e6a55ea7dd11a3013bc Mon Sep 17 00:00:00 2001 From: Henrik Rydgard Date: Wed, 16 Dec 2015 14:52:44 +0100 Subject: [PATCH 3/9] Use the hardware scaler again on Android. Move all DPI handling code from java to app-android.cpp. --- Common/Common.vcxproj | 39 ++++- Common/Common.vcxproj.filters | 24 +++ Common/GL/GLInterface/EGL.cpp | 11 +- Common/GL/GLInterface/EGLAndroid.cpp | 1 - Common/GL/GLInterface/EGLAndroid.h | 2 +- Common/GL/GLInterface/GLInterface.cpp | 3 +- Common/GL/GLInterfaceBase.h | 6 +- android/jni/app-android.cpp | 151 ++++++++++++++---- .../src/org/ppsspp/ppsspp/NativeActivity.java | 65 +++----- android/src/org/ppsspp/ppsspp/NativeApp.java | 10 +- .../org/ppsspp/ppsspp/NativeSurfaceView.java | 64 ++------ .../src/org/ppsspp/ppsspp/PpssppActivity.java | 58 +------ ext/native/ui/ui_context.cpp | 2 +- 13 files changed, 239 insertions(+), 197 deletions(-) diff --git a/Common/Common.vcxproj b/Common/Common.vcxproj index 6f0ab6e6e2..d93b8d8667 100644 --- a/Common/Common.vcxproj +++ b/Common/Common.vcxproj @@ -72,7 +72,7 @@ USING_WIN_UI;_CRT_SECURE_NO_WARNINGS;WIN32;_ARCH_32=1;_M_IX86=1;_DEBUG;_LIB;_UNICODE;UNICODE;%(PreprocessorDefinitions) stdafx.h MultiThreadedDebug - ../ext/native + ../ext/native;.. StreamingSIMDExtensions2 Fast true @@ -99,7 +99,7 @@ stdafx.h MultiThreadedDebug USING_WIN_UI;_CRT_SECURE_NO_WARNINGS;WIN32;_ARCH_64=1;_M_X64=1;_DEBUG;_LIB;_UNICODE;UNICODE;%(PreprocessorDefinitions) - ../ext/native + ../ext/native;.. NotSet Fast false @@ -132,7 +132,7 @@ false StreamingSIMDExtensions2 Fast - ../ext/native + ../ext/native;.. Speed true false @@ -161,7 +161,7 @@ true USING_WIN_UI;_CRT_SECURE_NO_WARNINGS;WIN32;_ARCH_64=1;_M_X64=1;NDEBUG;_LIB;_UNICODE;UNICODE;%(PreprocessorDefinitions) stdafx.h - ../ext/native + ../ext/native;.. false NotSet Fast @@ -209,6 +209,19 @@ + + + true + true + true + true + + + true + true + true + true + @@ -258,6 +271,24 @@ + + true + true + true + true + + + true + true + true + true + + + true + true + true + true + diff --git a/Common/Common.vcxproj.filters b/Common/Common.vcxproj.filters index 2bce532528..5b2bb42345 100644 --- a/Common/Common.vcxproj.filters +++ b/Common/Common.vcxproj.filters @@ -48,6 +48,15 @@ + + GL\GLInterface + + + GL\GLInterface + + + GL + @@ -84,6 +93,15 @@ + + GL\GLInterface + + + GL\GLInterface + + + GL\GLInterface + @@ -92,5 +110,11 @@ {1b593f03-7b28-4707-9228-4981796f5589} + + {2f2ca112-9e26-499e-9cb9-38a78b4ac09d} + + + {2c723cf4-75b6-406a-90c0-ebb7a13ba476} + \ No newline at end of file diff --git a/Common/GL/GLInterface/EGL.cpp b/Common/GL/GLInterface/EGL.cpp index 917e2392c0..4bd159969e 100644 --- a/Common/GL/GLInterface/EGL.cpp +++ b/Common/GL/GLInterface/EGL.cpp @@ -116,6 +116,7 @@ bool cInterfaceEGL::Create(void *window_handle, bool core) INFO_LOG(G3D, "Error: eglInitialize() failed\n"); return false; } + INFO_LOG(G3D, "eglInitialize() succeeded\n"); /* Detection code */ EGLConfig config; @@ -124,7 +125,7 @@ bool cInterfaceEGL::Create(void *window_handle, bool core) DetectMode(); // attributes for a visual in RGBA format with at least - // 8 bits per color + // 8 bits per color, 16 bits of depth and 8 bits of stencil. int attribs[] = { EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, EGL_RED_SIZE, 8, @@ -139,8 +140,8 @@ bool cInterfaceEGL::Create(void *window_handle, bool core) EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE }; - switch (s_opengl_mode) - { + + switch (s_opengl_mode) { case MODE_OPENGL: attribs[1] = EGL_OPENGL_BIT; ctx_attribs[0] = EGL_NONE; @@ -159,11 +160,13 @@ bool cInterfaceEGL::Create(void *window_handle, bool core) break; } - if (!eglChooseConfig( egl_dpy, attribs, &config, 1, &num_configs)) { + if (!eglChooseConfig(egl_dpy, attribs, &config, 1, &num_configs)) { INFO_LOG(G3D, "Error: couldn't get an EGL visual config\n"); exit(1); } + INFO_LOG(G3D, "eglChooseConfig successful"); + if (s_opengl_mode == MODE_OPENGL) eglBindAPI(EGL_OPENGL_API); else diff --git a/Common/GL/GLInterface/EGLAndroid.cpp b/Common/GL/GLInterface/EGLAndroid.cpp index f357982105..29796fae29 100644 --- a/Common/GL/GLInterface/EGLAndroid.cpp +++ b/Common/GL/GLInterface/EGLAndroid.cpp @@ -25,4 +25,3 @@ EGLNativeWindowType cInterfaceEGLAndroid::InitializePlatform(EGLNativeWindowType void cInterfaceEGLAndroid::ShutdownPlatform() { } - diff --git a/Common/GL/GLInterface/EGLAndroid.h b/Common/GL/GLInterface/EGLAndroid.h index 614b1a412d..811485d91b 100644 --- a/Common/GL/GLInterface/EGLAndroid.h +++ b/Common/GL/GLInterface/EGLAndroid.h @@ -13,7 +13,7 @@ protected: EGLDisplay OpenDisplay() override; EGLNativeWindowType InitializePlatform(EGLNativeWindowType host_window, EGLConfig config) override; void ShutdownPlatform() override; - void SetInternalResolution(int internalWidth, int internalHeight) override { + void OverrideBackbufferDimensions(int internalWidth, int internalHeight) override { internalWidth_ = internalWidth; internalHeight_ = internalHeight; } diff --git a/Common/GL/GLInterface/GLInterface.cpp b/Common/GL/GLInterface/GLInterface.cpp index 8abe67dc71..bf8d729745 100644 --- a/Common/GL/GLInterface/GLInterface.cpp +++ b/Common/GL/GLInterface/GLInterface.cpp @@ -20,8 +20,7 @@ #error Platform doesnt have a GLInterface #endif -cInterfaceBase* HostGL_CreateGLInterface() -{ +cInterfaceBase* HostGL_CreateGLInterface(){ #ifdef ANDROID return new cInterfaceEGLAndroid; #elif defined(__APPLE__) diff --git a/Common/GL/GLInterfaceBase.h b/Common/GL/GLInterfaceBase.h index 168f1398e6..9e8de11303 100644 --- a/Common/GL/GLInterfaceBase.h +++ b/Common/GL/GLInterfaceBase.h @@ -15,8 +15,7 @@ enum GLInterfaceMode { MODE_OPENGLES3, }; -class cInterfaceBase -{ +class cInterfaceBase { protected: // Window dimensions. u32 s_backbuffer_width; @@ -38,6 +37,9 @@ public: virtual void SwapInterval(int Interval) { } virtual u32 GetBackBufferWidth() { return s_backbuffer_width; } virtual u32 GetBackBufferHeight() { return s_backbuffer_height; } + + virtual void OverrideBackbufferDimensions(int w, int h) = 0; + virtual void SetBackBufferDimensions(u32 W, u32 H) {s_backbuffer_width = W; s_backbuffer_height = H; } virtual void Update() { } virtual bool PeekMessages() { return false; } diff --git a/android/jni/app-android.cpp b/android/jni/app-android.cpp index 8c51c2cfcd..164bdcffe6 100644 --- a/android/jni/app-android.cpp +++ b/android/jni/app-android.cpp @@ -11,7 +11,6 @@ #include #include #include -#include #include #include "base/basictypes.h" @@ -65,14 +64,29 @@ static int androidVersion; static int deviceType; // Should only be used for display detection during startup (for config defaults etc) +// This is the ACTUAL display size, not the hardware scaled display size. +static int display_dpi; static int display_xres; static int display_yres; +static int desiredBackbufferSizeX; +static int desiredBackbufferSizeY; + static jmethodID postCommand; static jobject nativeActivity; static volatile bool exitRenderLoop; bool renderLoopRunning; +float dp_xscale = 1.0f; +float dp_yscale = 1.0f; + +InputState input_state; + +static bool renderer_inited = false; +static bool first_lost = true; +static std::string library_path; + + // Android implementation of callbacks to the Java part of the app void SystemToast(const char *text) { lock_guard guard(frameCommandLock); @@ -149,18 +163,6 @@ int System_GetPropertyInt(SystemProperty prop) { } } -// Remember that all of these need initialization on init! The process -// may be reused when restarting the game. Globals are DANGEROUS. - -float dp_xscale = 1.0f; -float dp_yscale = 1.0f; - -InputState input_state; - -static bool renderer_inited = false; -static bool first_lost = true; -static std::string library_path; - std::string GetJavaString(JNIEnv *env, jstring jstr) { const char *str = env->GetStringUTFChars(jstr, 0); std::string cpp_string = std::string(str); @@ -208,7 +210,7 @@ extern "C" jstring Java_org_ppsspp_ppsspp_NativeApp_queryConfig } extern "C" void Java_org_ppsspp_ppsspp_NativeApp_init - (JNIEnv *env, jclass, jstring jmodel, jint jdeviceType, jint jxres, jint jyres, jstring jlangRegion, jstring japkpath, + (JNIEnv *env, jclass, jstring jmodel, jint jdeviceType, jstring jlangRegion, jstring japkpath, jstring jdataDir, jstring jexternalDir, jstring jlibraryDir, jstring jshortcutParam, jstring jinstallID, jint jAndroidVersion) { jniEnvUI = env; @@ -232,8 +234,6 @@ extern "C" void Java_org_ppsspp_ppsspp_NativeApp_init right_joystick_y_async = 0; hat_joystick_x_async = 0; hat_joystick_y_async = 0; - display_xres = jxres; - display_yres = jyres; std::string apkPath = GetJavaString(env, japkpath); VFSRegister("", new ZipAssetReader(apkPath.c_str(), "assets/")); @@ -319,23 +319,6 @@ extern "C" void Java_org_ppsspp_ppsspp_NativeApp_shutdown(JNIEnv *, jclass) { ILOG("NativeApp.shutdown() -- end"); } -extern "C" void Java_org_ppsspp_ppsspp_NativeApp_displayResize(JNIEnv *, jclass, jint w, jint h, jint dpi, jfloat refreshRate) { - ILOG("NativeApp.displayResize(%i x %i, dpi=%i, refresh=%0.2f)", w, h, dpi, refreshRate); - - g_dpi = dpi; - g_dpi_scale = 240.0f / (float)g_dpi; - - pixel_xres = w; - pixel_yres = h; - dp_xres = pixel_xres * g_dpi_scale; - dp_yres = pixel_yres * g_dpi_scale; - dp_xscale = (float)dp_xres / pixel_xres; - dp_yscale = (float)dp_yres / pixel_yres; - display_hz = refreshRate; - - NativeResized(); -} - extern "C" void Java_org_ppsspp_ppsspp_NativeRenderer_displayShutdown(JNIEnv *env, jobject obj) { if (renderer_inited) { renderer_inited = false; @@ -493,21 +476,116 @@ extern "C" void JNICALL Java_org_ppsspp_ppsspp_NativeActivity_exitEGLRenderLoop( } } +void correctRatio(int &sz_x, int &sz_y, float scale) { + float x = (float)sz_x; + float y = (float)sz_y; + float ratio = x / y; + ILOG("CorrectRatio: Considering size: %0.2f/%0.2f=%0.2f for scale %f", x, y, ratio, scale); + float targetRatio; + + // Try to get the longest dimension to match scale*PSP resolution. + if (x >= y) { + targetRatio = 480.0f / 272.0f; + x = 480.f * scale; + y = 272.f * scale; + } else { + targetRatio = 272.0f / 480.0f; + x = 272.0f * scale; + y = 480.0f * scale; + } + + float correction = targetRatio / ratio; + ILOG("Target ratio: %0.2f ratio: %0.2f correction: %0.2f", targetRatio, ratio, correction); + if (ratio < targetRatio) { + y *= correction; + } else { + x /= correction; + } + + sz_x = x; + sz_y = y; + ILOG("Corrected ratio: %dx%d", sz_x, sz_y); +} + +void getDesiredBackbufferSize(int &sz_x, int &sz_y) { + sz_x = display_xres; + sz_y = display_yres; + std::string config = NativeQueryConfig("hwScale"); + int scale; + if (1 == sscanf(config.c_str(), "%d", &scale) && scale > 0) { + correctRatio(sz_x, sz_y, scale); + } else { + sz_x = 0; + sz_y = 0; + } +} + +extern "C" void JNICALL Java_org_ppsspp_ppsspp_NativeApp_setDisplayParameters(JNIEnv *, jclass, jint xres, jint yres, jint dpi, jfloat refreshRate) { + ILOG("NativeApp.setDisplayParameters(%d x %d, dpi=%d, refresh=%0.2f)", xres, yres, dpi, refreshRate); + display_xres = xres; + display_yres = yres; + display_dpi = dpi; + display_hz = refreshRate; +} + +extern "C" void JNICALL Java_org_ppsspp_ppsspp_NativeApp_backbufferResize(JNIEnv *, jclass, jint bufw, jint bufh) { + ILOG("NativeApp.backbufferResize(%d x %d)", bufw, bufh); + + // pixel_*res is the backbuffer resolution. + pixel_xres = bufw; + pixel_yres = bufh; + + g_dpi = (int)display_dpi; + g_dpi_scale = 240.0f / (float)g_dpi; + + dp_xres = display_xres * g_dpi_scale; + dp_yres = display_yres * g_dpi_scale; + + // Touch scaling is from display pixels to dp pixels. + dp_xscale = (float)dp_xres / (float)display_xres; + dp_yscale = (float)dp_yres / (float)display_yres; + + pixel_in_dps = (float)pixel_xres / dp_xres; + + ILOG("dp_xscale=%f dp_yscale=%f", dp_xscale, dp_yscale); + ILOG("dp_xres=%d dp_yres=%d", dp_xres, dp_yres); + ILOG("pixel_xres=%d pixel_yres=%d", pixel_xres, pixel_yres); + ILOG("g_dpi=%d g_dpi_scale=%f", g_dpi, g_dpi_scale); + + NativeResized(); +} + +extern "C" void JNICALL Java_org_ppsspp_ppsspp_NativeApp_computeDesiredBackbufferDimensions() { + getDesiredBackbufferSize(desiredBackbufferSizeX, desiredBackbufferSizeY); +} + +extern "C" jint JNICALL Java_org_ppsspp_ppsspp_NativeApp_getDesiredBackbufferWidth(JNIEnv *, jclass) { + return desiredBackbufferSizeX; +} + +extern "C" jint JNICALL Java_org_ppsspp_ppsspp_NativeApp_getDesiredBackbufferHeight(JNIEnv *, jclass) { + return desiredBackbufferSizeY; +} + extern "C" void JNICALL Java_org_ppsspp_ppsspp_NativeActivity_runEGLRenderLoop(JNIEnv *env, jobject obj, jobject _surf) { ANativeWindow *wnd = ANativeWindow_fromSurface(env, _surf); - WLOG("runEGLRenderLoop"); + WLOG("runEGLRenderLoop. display_xres=%d display_yres=%d", display_xres, display_yres); if (wnd == nullptr) { ELOG("Error: Surface is null."); return; } - + cInterfaceBase *gl = HostGL_CreateGLInterface(); if (!gl) { ELOG("ERROR: Failed to create GL interface"); return; } + ILOG("EGL interface created. Desired backbuffer size: %dx%d", desiredBackbufferSizeX, desiredBackbufferSizeY); + + // Apparently we still have to set this through Java through setFixedSize on the bufferHolder for it to take effect... + gl->SetBackBufferDimensions(desiredBackbufferSizeX, desiredBackbufferSizeY); gl->SetMode(MODE_DETECT); gl->Create(wnd); gl->MakeCurrent(); @@ -566,11 +644,14 @@ extern "C" void JNICALL Java_org_ppsspp_ppsspp_NativeActivity_runEGLRenderLoop(J } } + // Restore lost device objects. TODO: This feels like the wrong place for this. NativeDeviceLost(); ILOG("NativeDeviceLost completed."); + NativeShutdownGraphics(); renderer_inited = false; + gl->ClearCurrent(); delete gl; ANativeWindow_release(wnd); diff --git a/android/src/org/ppsspp/ppsspp/NativeActivity.java b/android/src/org/ppsspp/ppsspp/NativeActivity.java index 41f6b8509c..c465b3417b 100644 --- a/android/src/org/ppsspp/ppsspp/NativeActivity.java +++ b/android/src/org/ppsspp/ppsspp/NativeActivity.java @@ -8,7 +8,6 @@ import java.util.Locale; import android.annotation.SuppressLint; import android.annotation.TargetApi; import android.app.Activity; -import android.app.ActivityManager; import android.app.AlertDialog; import android.app.UiModeManager; import android.content.Context; @@ -16,11 +15,9 @@ import android.content.DialogInterface; import android.content.Intent; import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; -import android.content.pm.ConfigurationInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.content.res.Configuration; -import android.graphics.PixelFormat; import android.graphics.Point; import android.media.AudioManager; import android.net.Uri; @@ -29,6 +26,7 @@ import android.os.Bundle; import android.os.Environment; import android.os.Vibrator; import android.text.InputType; +import android.util.DisplayMetrics; import android.util.Log; import android.view.Display; import android.view.Gravity; @@ -215,10 +213,8 @@ public class NativeActivity extends Activity implements SurfaceHolder.Callback { String model = Build.MANUFACTURER + ":" + Build.MODEL; String languageRegion = Locale.getDefault().getLanguage() + "_" + Locale.getDefault().getCountry(); - Point displaySize = new Point(); - GetScreenSize(displaySize); NativeApp.audioConfig(optimalFramesPerBuffer, optimalSampleRate); - NativeApp.init(model, deviceType, displaySize.x, displaySize.y, languageRegion, apkFilePath, dataDir, externalStorageDir, libraryDir, shortcutParam, installID, Build.VERSION.SDK_INT); + NativeApp.init(model, deviceType, languageRegion, apkFilePath, dataDir, externalStorageDir, libraryDir, shortcutParam, installID, Build.VERSION.SDK_INT); NativeApp.sendMessage("cacheDir", getCacheDir().getAbsolutePath()); @@ -291,12 +287,6 @@ public class NativeActivity extends Activity implements SurfaceHolder.Callback { } } - // Override this to scale the backbuffer (use the Android hardware scaler) - public void getDesiredBackbufferSize(Point sz) { - sz.x = 0; - sz.y = 0; - } - private Runnable mEmulationRunner = new Runnable() { @Override @@ -331,6 +321,15 @@ public class NativeActivity extends Activity implements SurfaceHolder.Callback { registerCallbacks(); installID = Installation.id(this); + DisplayMetrics metrics = new DisplayMetrics(); + Display display = getWindowManager().getDefaultDisplay(); + display.getMetrics(metrics); + + float refreshRate = display.getRefreshRate(); + Point outSize = new Point(); + GetScreenSize(outSize); + NativeApp.setDisplayParameters(outSize.x, outSize.y, metrics.densityDpi, refreshRate); + if (!initialized) { Initialize(); initialized = true; @@ -349,19 +348,6 @@ public class NativeActivity extends Activity implements SurfaceHolder.Callback { gainAudioFocus(this.audioManager, this.audioFocusChangeListener); NativeApp.audioInit(); - mSurfaceView = new NativeSurfaceView(this); - mSurfaceView.getHolder().addCallback(this); - - Point sz = new Point(); - getDesiredBackbufferSize(sz); - if (sz.x > 0) { - Log.i(TAG, "Requesting fixed size buffer: " + sz.x + "x" + sz.y); - // Auto-calculates new DPI and forwards to the correct call on mGLSurfaceView.getHolder() - mSurfaceView.setFixedSize(sz.x, sz.y); - } - - setContentView(mSurfaceView); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) { updateSystemUiVisibility(); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { @@ -369,6 +355,16 @@ public class NativeActivity extends Activity implements SurfaceHolder.Callback { } } + NativeApp.computeDesiredBackbufferDimensions(); + int bbW = NativeApp.getDesiredBackbufferWidth(); + int bbH = NativeApp.getDesiredBackbufferHeight(); + + mSurfaceView = new NativeSurfaceView(this, bbW, bbH); + mSurfaceView.getHolder().addCallback(this); + Log.i(TAG, "setcontentview before"); + setContentView(mSurfaceView); + Log.i(TAG, "setcontentview after"); + mRenderLoopThread = new Thread(mEmulationRunner); mRenderLoopThread.start(); } @@ -383,6 +379,7 @@ public class NativeActivity extends Activity implements SurfaceHolder.Callback { public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { Log.w(TAG, "Surface changed. Resolution: " + width + "x" + height); + NativeApp.backbufferResize(width, height); mSurface = holder.getSurface(); if (mRenderLoopThread == null || !mRenderLoopThread.isAlive()) { mRenderLoopThread = new Thread(mEmulationRunner); @@ -440,18 +437,6 @@ public class NativeActivity extends Activity implements SurfaceHolder.Callback { unregisterCallbacks(); } - private boolean detectOpenGLES20() { - ActivityManager am = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE); - ConfigurationInfo info = am.getDeviceConfigurationInfo(); - return info.reqGlEsVersion >= 0x20000; - } - - private boolean detectOpenGLES30() { - ActivityManager am = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE); - ConfigurationInfo info = am.getDeviceConfigurationInfo(); - return info.reqGlEsVersion >= 0x30000; - } - @Override protected void onPause() { super.onPause(); @@ -489,12 +474,6 @@ public class NativeActivity extends Activity implements SurfaceHolder.Callback { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) { updateSystemUiVisibility(); } - - Point sz = new Point(); - getDesiredBackbufferSize(sz); - if (sz.x > 0) { - mSurfaceView.getHolder().setFixedSize(sz.x/2, sz.y/2); - } } //keep this static so we can call this even if we don't diff --git a/android/src/org/ppsspp/ppsspp/NativeApp.java b/android/src/org/ppsspp/ppsspp/NativeApp.java index 0b59fb2d71..84b2003d5d 100644 --- a/android/src/org/ppsspp/ppsspp/NativeApp.java +++ b/android/src/org/ppsspp/ppsspp/NativeApp.java @@ -13,13 +13,19 @@ public class NativeApp { public final static int DEVICE_TYPE_TV = 1; public final static int DEVICE_TYPE_DESKTOP = 2; - public static native void init(String model, int deviceType, int xres, int yres, String languageRegion, String apkPath, String dataDir, String externalDir, String libraryDir, String shortcutParam, String installID, int androidVersion); + public static native void init(String model, int deviceType, String languageRegion, String apkPath, String dataDir, String externalDir, String libraryDir, String shortcutParam, String installID, int androidVersion); public static native void audioInit(); public static native void audioShutdown(); public static native void audioConfig(int optimalFramesPerBuffer, int optimalSampleRate); - public static native void displayResize(int width, int height, int scaled_dpi, float refreshRate); + public static native void computeDesiredBackbufferDimensions(); + public static native int getDesiredBackbufferWidth(); + public static native int getDesiredBackbufferHeight(); + + public static native void setDisplayParameters(int display_xres, int display_yres, int dpi, float refreshRate); + public static native void backbufferResize(int bufferWidth, int bufferHeight); + public static native boolean isLandscape(); public static native boolean isAtTopLevel(); diff --git a/android/src/org/ppsspp/ppsspp/NativeSurfaceView.java b/android/src/org/ppsspp/ppsspp/NativeSurfaceView.java index 8822e86bdb..81d8a22adc 100644 --- a/android/src/org/ppsspp/ppsspp/NativeSurfaceView.java +++ b/android/src/org/ppsspp/ppsspp/NativeSurfaceView.java @@ -1,66 +1,48 @@ package org.ppsspp.ppsspp; -// Touch- and sensor-enabled GLSurfaceView. +// Touch- and sensor-enabled SurfaceView. // Supports simple multitouch and pressure. +// DPI scaling is handled by the native code. +import android.annotation.SuppressLint; import android.annotation.TargetApi; import android.app.Activity; -import android.graphics.Point; import android.hardware.Sensor; import android.hardware.SensorEvent; import android.hardware.SensorEventListener; import android.hardware.SensorManager; -import android.opengl.GLSurfaceView; import android.os.Build; import android.os.Handler; -import android.util.DisplayMetrics; -// import android.os.Build; -// import android.util.Log; import android.util.Log; -import android.view.Display; import android.view.MotionEvent; import android.view.SurfaceView; import com.bda.controller.*; public class NativeSurfaceView extends SurfaceView implements SensorEventListener, ControllerListener { - private static String TAG = "NativeGLView"; + private static String TAG = "NativeSurfaceView"; private SensorManager mSensorManager; private Sensor mAccelerometer; - private NativeActivity mActivity; // Moga controller private Controller mController = null; private boolean isMogaPro = false; - private int dpi; - private float refreshRate; - private double dpi_scale_x; - private double dpi_scale_y; - - int last_width, last_height; - - public int fixedW = 0; - public int fixedH = 0; - - public NativeSurfaceView(NativeActivity activity) { + public NativeSurfaceView(NativeActivity activity, int fixedW, int fixedH) { super(activity); - DisplayMetrics metrics = new DisplayMetrics(); - Display display = activity.getWindowManager().getDefaultDisplay(); - display.getMetrics(metrics); - dpi = metrics.densityDpi; - - refreshRate = display.getRefreshRate(); - - mActivity = activity; + Log.i(TAG, "NativeSurfaceView"); mSensorManager = (SensorManager)activity.getSystemService(Activity.SENSOR_SERVICE); mAccelerometer = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER); mController = Controller.getInstance(activity); - onResize(display.getWidth(), display.getHeight()); + // Maybe we need to use this? + if (fixedW != 0 && fixedH != 0) { + Log.i(TAG, "Setting surface holder to use a fixed size of " + fixedW + "x" + fixedH + " pixels"); + this.getHolder().setFixedSize(fixedW, fixedH); + } try { MogaHack.init(mController, activity); @@ -71,37 +53,17 @@ public class NativeSurfaceView extends SurfaceView implements SensorEventListene } } - void onResize(int width, int height) { - Point sz = new Point(); - mActivity.GetScreenSize(sz); - double actualW = sz.x; - double actualH = sz.y; - dpi_scale_x = (width / actualW); - dpi_scale_y = (height / actualH); - Log.i(TAG, "onSurfaceChanged: " + dpi_scale_x + "x" + dpi_scale_y + " (width=" + width + ", actualW=" + actualW); - int scaled_dpi = (int)(dpi * dpi_scale_x); - NativeApp.displayResize(width, height, scaled_dpi, refreshRate); - last_width = width; - last_height = height; - } - - void setFixedSize(int w, int h) { - fixedW = w; - fixedH = h; - } - @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH) private int getToolType(final MotionEvent ev, int pointer) { return ev.getToolType(pointer); } + @SuppressLint("ClickableViewAccessibility") @Override public boolean onTouchEvent(final MotionEvent ev) { boolean canReadToolType = Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH; int numTouchesHandled = 0; - float scaleX = (float)this.dpi_scale_x; - float scaleY = (float)this.dpi_scale_y; for (int i = 0; i < ev.getPointerCount(); i++) { int pid = ev.getPointerId(i); int code = 0; @@ -133,7 +95,7 @@ public class NativeSurfaceView extends SurfaceView implements SensorEventListene code |= tool << 10; // We use the Android tool type codes } // Can't use || due to short circuit evaluation - numTouchesHandled += NativeApp.touch(scaleX * ev.getX(i), scaleY * ev.getY(i), code, pid) ? 1 : 0; + numTouchesHandled += NativeApp.touch(ev.getX(i), ev.getY(i), code, pid) ? 1 : 0; } } return numTouchesHandled > 0; diff --git a/android/src/org/ppsspp/ppsspp/PpssppActivity.java b/android/src/org/ppsspp/ppsspp/PpssppActivity.java index 18e1f4179e..bb61d9007d 100644 --- a/android/src/org/ppsspp/ppsspp/PpssppActivity.java +++ b/android/src/org/ppsspp/ppsspp/PpssppActivity.java @@ -1,7 +1,6 @@ package org.ppsspp.ppsspp; import android.app.AlertDialog; -import android.graphics.Point; import android.os.Build; import android.os.Bundle; import android.os.Looper; @@ -15,7 +14,8 @@ public class PpssppActivity extends NativeActivity { private static boolean m_hasUnsupportedABI = false; private static boolean m_hasNoNativeBinary = false; - static { + @SuppressWarnings("deprecation") + static void CheckABIAndLoadLibrary() { if (Build.CPU_ABI.equals("armeabi")) { m_hasUnsupportedABI = true; } else { @@ -28,6 +28,10 @@ public class PpssppActivity extends NativeActivity { } } + static { + CheckABIAndLoadLibrary(); + } + public PpssppActivity() { super(); } @@ -36,6 +40,7 @@ public class PpssppActivity extends NativeActivity { public void onCreate(Bundle savedInstanceState) { if (m_hasUnsupportedABI || m_hasNoNativeBinary) { new Thread() { + @SuppressWarnings("deprecation") @Override public void run() { Looper.prepare(); @@ -68,55 +73,6 @@ public class PpssppActivity extends NativeActivity { super.onCreate(savedInstanceState); } - - private void correctRatio(Point sz, float scale) { - float x = sz.x; - float y = sz.y; - float ratio = x / y; - // Log.i(TAG, "Considering size: " + sz.x + "x" + sz.y + "=" + ratio); - float targetRatio; - if (x >= y) { - targetRatio = 480.0f / 272.0f; - x = 480.f * scale; - y = 272.f * scale; - } else { - targetRatio = 272.0f / 480.0f; - x = 272.0f * scale; - y = 480.0f * scale; - } - float correction = targetRatio / ratio; - // Log.i(TAG, "Target ratio: " + targetRatio + " ratio: " + ratio + " correction: " + correction); - if (ratio < targetRatio) { - y *= correction; - } else { - x /= correction; - } - sz.x = (int)x; - sz.y = (int)y; - // Log.i(TAG, "Corrected ratio: " + sz.x + "x" + sz.y); - } - - @Override - public void getDesiredBackbufferSize(Point sz) { - GetScreenSize(sz); - String config = NativeApp.queryConfig("hwScale"); - int scale; - try { - scale = Integer.parseInt(config); - if (scale == 0) { - sz.x = 0; - sz.y = 0; - return; - } - } - catch (NumberFormatException e) { - sz.x = 0; - sz.y = 0; - return; - } - correctRatio(sz, scale); - } - // called by the C++ code through JNI. Dispatch anything we can't directly handle // on the gfx thread to the UI thread. public void postCommand(String command, String parameter) { diff --git a/ext/native/ui/ui_context.cpp b/ext/native/ui/ui_context.cpp index 56039e1809..d5b3bc3972 100644 --- a/ext/native/ui/ui_context.cpp +++ b/ext/native/ui/ui_context.cpp @@ -97,7 +97,7 @@ Bounds UIContext::GetScissorBounds() { void UIContext::ActivateTopScissor() { if (scissorStack_.size()) { const Bounds &bounds = scissorStack_.back(); - float scale = 1.0f / g_dpi_scale; + float scale = pixel_in_dps; int x = scale * bounds.x; int y = scale * bounds.y; int w = scale * bounds.w; From ac202c16c325f9151c8cc58d5d43e3fc9bb943ae Mon Sep 17 00:00:00 2001 From: Henrik Rydgard Date: Wed, 16 Dec 2015 19:43:39 +0100 Subject: [PATCH 4/9] Screen rotation works correctly on startup on older Android devices now. Changing in-menu still has issues because the activity will not restart. --- .../src/org/ppsspp/ppsspp/NativeActivity.java | 63 +++++++++++-------- 1 file changed, 36 insertions(+), 27 deletions(-) diff --git a/android/src/org/ppsspp/ppsspp/NativeActivity.java b/android/src/org/ppsspp/ppsspp/NativeActivity.java index c465b3417b..b9ee3e195c 100644 --- a/android/src/org/ppsspp/ppsspp/NativeActivity.java +++ b/android/src/org/ppsspp/ppsspp/NativeActivity.java @@ -5,6 +5,8 @@ import java.lang.reflect.Field; import java.util.List; import java.util.Locale; +import com.google.common.collect.Sets.SetView; + import android.annotation.SuppressLint; import android.annotation.TargetApi; import android.app.Activity; @@ -172,17 +174,13 @@ public class NativeActivity extends Activity implements SurfaceHolder.Callback { detectOptimalAudioSettings(); } - // isLandscape is used to trigger GetAppInfo currently, we - boolean landscape = NativeApp.isLandscape(); - Log.d(TAG, "Landscape: " + landscape); - // Get system information ApplicationInfo appInfo = null; PackageManager packMgmr = getPackageManager(); String packageName = getPackageName(); try { appInfo = packMgmr.getApplicationInfo(packageName, 0); - } catch (NameNotFoundException e) { + } catch (NameNotFoundException e) { e.printStackTrace(); throw new RuntimeException("Unable to locate assets, aborting..."); } @@ -235,7 +233,8 @@ public class NativeActivity extends Activity implements SurfaceHolder.Callback { Log.e(TAG, "Invalid rotation: " + rotString); return; } - Log.i(TAG, "Requested rotation: " + rot + " ('" + rotString + "')"); + Log.i(TAG, "Setting requested rotation: " + rot + " ('" + rotString + "')"); + switch (rot) { case 0: setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED); @@ -256,8 +255,10 @@ public class NativeActivity extends Activity implements SurfaceHolder.Callback { } private boolean useImmersive() { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) + return false; String immersive = NativeApp.queryConfig("immersiveMode"); - return immersive.equals("1") && Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT; + return immersive.equals("1"); } @SuppressLint("InlinedApi") @@ -287,26 +288,20 @@ public class NativeActivity extends Activity implements SurfaceHolder.Callback { } } - private Runnable mEmulationRunner = new Runnable() - { + private Runnable mEmulationRunner = new Runnable() { @Override - public void run() - { + public void run() { // Bit of a hack - loop until onSurfaceCreated succeeds. try { while (mSurface == null) - { Thread.sleep(10); - } } catch (InterruptedException e) { e.printStackTrace(); } Log.i(TAG, "Starting the render loop: " + mSurface); - // Start emulation using the provided Surface. runEGLRenderLoop(mSurface); - Log.i(TAG, "Left the render loop: " + mSurface); } }; @@ -315,20 +310,26 @@ public class NativeActivity extends Activity implements SurfaceHolder.Callback { // Tells the render loop thread to exit, so we can restart it. public native void exitEGLRenderLoop(); - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - registerCallbacks(); - installID = Installation.id(this); - + void updateDisplayMetrics(Point outSize) { DisplayMetrics metrics = new DisplayMetrics(); Display display = getWindowManager().getDefaultDisplay(); display.getMetrics(metrics); float refreshRate = display.getRefreshRate(); - Point outSize = new Point(); + if (outSize == null) { + outSize = new Point(); + } GetScreenSize(outSize); NativeApp.setDisplayParameters(outSize.x, outSize.y, metrics.densityDpi, refreshRate); + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + registerCallbacks(); + installID = Installation.id(this); + + updateDisplayMetrics(null); if (!initialized) { Initialize(); @@ -354,13 +355,14 @@ public class NativeActivity extends Activity implements SurfaceHolder.Callback { setupSystemUiCallback(); } } + updateDisplayMetrics(null); NativeApp.computeDesiredBackbufferDimensions(); int bbW = NativeApp.getDesiredBackbufferWidth(); int bbH = NativeApp.getDesiredBackbufferHeight(); - mSurfaceView = new NativeSurfaceView(this, bbW, bbH); - mSurfaceView.getHolder().addCallback(this); + mSurfaceView = new NativeSurfaceView(NativeActivity.this, bbW, bbH); + mSurfaceView.getHolder().addCallback(NativeActivity.this); Log.i(TAG, "setcontentview before"); setContentView(mSurfaceView); Log.i(TAG, "setcontentview after"); @@ -379,7 +381,11 @@ public class NativeActivity extends Activity implements SurfaceHolder.Callback { public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { Log.w(TAG, "Surface changed. Resolution: " + width + "x" + height); - NativeApp.backbufferResize(width, height); + // Make sure we have fresh display metrics so the computations go right. + // This is needed on some very old devices, I guess event order is different or something... + Point sz = new Point(); + updateDisplayMetrics(sz); + NativeApp.backbufferResize(width, height); mSurface = holder.getSurface(); if (mRenderLoopThread == null || !mRenderLoopThread.isAlive()) { mRenderLoopThread = new Thread(mEmulationRunner); @@ -470,10 +476,12 @@ public class NativeActivity extends Activity implements SurfaceHolder.Callback { @Override public void onConfigurationChanged(Configuration newConfig) { + Log.i(TAG, "onConfigurationChanged"); super.onConfigurationChanged(newConfig); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) { updateSystemUiVisibility(); } + updateDisplayMetrics(null); } //keep this static so we can call this even if we don't @@ -891,8 +899,9 @@ public class NativeActivity extends Activity implements SurfaceHolder.Callback { } else if (command.equals("finish")) { finish(); } else if (command.equals("rotate")) { - if (Build.VERSION.SDK_INT >= 9) { - updateScreenRotation(); + updateScreenRotation(); + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH) { + Log.i(TAG, "Must recreate activity on rotation"); } } else if (command.equals("immersive")) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { From db633aa37794f40d7e7d71e6c958f3fbe3663a01 Mon Sep 17 00:00:00 2001 From: Henrik Rydgard Date: Wed, 16 Dec 2015 21:37:09 +0100 Subject: [PATCH 5/9] Lots more debug logging. Terrifying compatibility hack for devices that for some reason create 16-bit buffers. Like Xperia Play. --- Common/GL/GLInterface/EGL.cpp | 143 ++++++++++++++---- Common/GL/GLInterface/EGL.h | 3 +- Common/GL/GLInterface/EGLAndroid.cpp | 18 ++- Common/GL/GLInterfaceBase.h | 2 +- UI/NativeApp.cpp | 2 +- android/jni/app-android.cpp | 20 ++- .../src/org/ppsspp/ppsspp/NativeActivity.java | 4 +- android/src/org/ppsspp/ppsspp/NativeApp.java | 2 +- .../org/ppsspp/ppsspp/NativeSurfaceView.java | 5 + 9 files changed, 155 insertions(+), 44 deletions(-) diff --git a/Common/GL/GLInterface/EGL.cpp b/Common/GL/GLInterface/EGL.cpp index 4bd159969e..a9d8cb3c71 100644 --- a/Common/GL/GLInterface/EGL.cpp +++ b/Common/GL/GLInterface/EGL.cpp @@ -25,9 +25,6 @@ void* cInterfaceEGL::GetFuncAddress(const std::string& name) void cInterfaceEGL::DetectMode() { - if (s_opengl_mode != MODE_DETECT) - return; - EGLint num_configs; bool supportsGL = false, supportsGLES2 = false, supportsGLES3 = false; std::array renderable_types = { @@ -97,8 +94,62 @@ void cInterfaceEGL::DetectMode() s_opengl_mode = GLInterfaceMode::MODE_OPENGL; // Fall back to OpenGL } +static void LogEGLConfig(EGLDisplay egl_dpy, EGLConfig config) { + EGLint red = 0, green = 0, blue = 0, alpha = 0, depth = 0, stencil = 0, format = -1, type; + + struct { + EGLint value; + const char *name; + } vals[] = { + { EGL_RED_SIZE, "EGL_RED_SIZE" }, + { EGL_GREEN_SIZE, "EGL_GREEN_SIZE" }, + { EGL_BLUE_SIZE, "EGL_BLUE_SIZE" }, + { EGL_ALPHA_SIZE, "EGL_ALPHA_SIZE" }, + { EGL_DEPTH_SIZE, "EGL_DEPTH_SIZE" }, + { EGL_STENCIL_SIZE, "EGL_STENCIL_SIZE" }, + { EGL_NATIVE_VISUAL_ID, "EGL_NATIVE_VISUAL_ID" }, + { EGL_NATIVE_VISUAL_TYPE, "EGL_NATIVE_VISUAL_TYPE" }, + { EGL_MAX_SWAP_INTERVAL, "EGL_MAX_SWAP_INTERVAL" }, + { EGL_MIN_SWAP_INTERVAL, "EGL_MIN_SWAP_INTERVAL" }, + { EGL_MIN_SWAP_INTERVAL, "EGL_MIN_SWAP_INTERVAL" }, + { EGL_NATIVE_RENDERABLE, "EGL_NATIVE_RENDERABLE" }, + { EGL_COLOR_BUFFER_TYPE, "EGL_COLOR_BUFFER_TYPE" }, + { EGL_BUFFER_SIZE, "EGL_BUFFER_SIZE" }, + { EGL_CONFIG_ID, "EGL_CONFIG_ID" }, + { EGL_SAMPLES, "EGL_SAMPLES" }, + }; + + for (int i = 0; i < (int)(sizeof(vals)/sizeof(vals[0])); i++) { + EGLint value; + eglGetConfigAttrib(egl_dpy, config, vals[i].value, &value); + INFO_LOG(G3D, " %s = %d", vals[i].name, value); + } +} + +const char *cInterfaceEGL::EGLGetErrorString(EGLint error) { + switch (error) { + case EGL_SUCCESS: return "EGL_SUCCESS"; + case EGL_NOT_INITIALIZED: return "EGL_NOT_INITIALIZED"; + case EGL_BAD_ACCESS: return "EGL_BAD_ACCESS"; + case EGL_BAD_ALLOC: return "EGL_BAD_ALLOC"; + case EGL_BAD_ATTRIBUTE: return "EGL_BAD_ATTRIBUTE"; + case EGL_BAD_CONTEXT: return "EGL_BAD_CONTEXT"; + case EGL_BAD_CONFIG: return "EGL_BAD_CONFIG"; + case EGL_BAD_CURRENT_SURFACE: return "EGL_BAD_CURRENT_SURFACE"; + case EGL_BAD_DISPLAY: return "EGL_BAD_DISPLAY"; + case EGL_BAD_SURFACE: return "EGL_BAD_SURFACE"; + case EGL_BAD_MATCH: return "EGL_BAD_MATCH"; + case EGL_BAD_PARAMETER: return "EGL_BAD_PARAMETER"; + case EGL_BAD_NATIVE_PIXMAP: return "EGL_BAD_NATIVE_PIXMAP"; + case EGL_BAD_NATIVE_WINDOW: return "EGL_BAD_NATIVE_WINDOW"; + case EGL_CONTEXT_LOST: return "EGL_CONTEXT_LOST"; + default: + return "(UNKNOWN)"; + } +} + // Create rendering window. -bool cInterfaceEGL::Create(void *window_handle, bool core) +bool cInterfaceEGL::Create(void *window_handle, bool core, bool use16bit) { const char *s; EGLint egl_major, egl_minor; @@ -118,27 +169,43 @@ bool cInterfaceEGL::Create(void *window_handle, bool core) } INFO_LOG(G3D, "eglInitialize() succeeded\n"); - /* Detection code */ - EGLConfig config; - EGLint num_configs; + if (s_opengl_mode == MODE_DETECT) + DetectMode(); - DetectMode(); - - // attributes for a visual in RGBA format with at least - // 8 bits per color, 16 bits of depth and 8 bits of stencil. - int attribs[] = { - EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, + int attribs32[] = { + EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, // Keep this first! EGL_RED_SIZE, 8, EGL_GREEN_SIZE, 8, EGL_BLUE_SIZE, 8, EGL_ALPHA_SIZE, 8, EGL_DEPTH_SIZE, 16, EGL_STENCIL_SIZE, 8, - EGL_NONE }; + EGL_SURFACE_TYPE, EGL_WINDOW_BIT, + EGL_TRANSPARENT_TYPE, EGL_NONE, + EGL_SAMPLES, 0, + EGL_NONE, 0 + }; + int attribs16[] = { + EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, // Keep this first! + EGL_RED_SIZE, 5, + EGL_GREEN_SIZE, 6, + EGL_BLUE_SIZE, 5, + EGL_ALPHA_SIZE, 0, + EGL_DEPTH_SIZE, 16, + EGL_STENCIL_SIZE, 8, + EGL_SURFACE_TYPE, EGL_WINDOW_BIT, + EGL_TRANSPARENT_TYPE, EGL_NONE, + EGL_SAMPLES, 0, + EGL_NONE, 0 + }; + int *attribs = attribs32; + if (use16bit) { + attribs = attribs16; + } EGLint ctx_attribs[] = { EGL_CONTEXT_CLIENT_VERSION, 2, - EGL_NONE + EGL_NONE, 0 }; switch (s_opengl_mode) { @@ -155,17 +222,32 @@ bool cInterfaceEGL::Create(void *window_handle, bool core) ctx_attribs[1] = 3; break; default: - ERROR_LOG(G3D, "Unknown opengl mode set\n"); + ERROR_LOG(G3D, "Unknown OpenGL mode set\n"); return false; break; } - if (!eglChooseConfig(egl_dpy, attribs, &config, 1, &num_configs)) { - INFO_LOG(G3D, "Error: couldn't get an EGL visual config\n"); - exit(1); + EGLConfig *configs; + EGLint num_configs; + if (!eglChooseConfig(egl_dpy, attribs, NULL, 0, &num_configs) || num_configs == 0) { + INFO_LOG(G3D, "Error: couldn't get a number of configs\n"); + eglTerminate(egl_dpy); + return false; } - INFO_LOG(G3D, "eglChooseConfig successful"); + configs = new EGLConfig[num_configs]; + + if (!eglChooseConfig(egl_dpy, attribs, configs, num_configs, &num_configs)) { + INFO_LOG(G3D, "Error: couldn't get an EGL visual config\n"); + eglTerminate(egl_dpy); + return false; + } + + INFO_LOG(G3D, "eglChooseConfig successful: num_configs=%d, choosing config 0", num_configs); + for (int i = 0; i < num_configs; i++) { + INFO_LOG(G3D, "Config %d:", i); + LogEGLConfig(egl_dpy, configs[i]); + } if (s_opengl_mode == MODE_OPENGL) eglBindAPI(EGL_OPENGL_API); @@ -173,7 +255,7 @@ bool cInterfaceEGL::Create(void *window_handle, bool core) eglBindAPI(EGL_OPENGL_ES_API); EGLNativeWindowType host_window = (EGLNativeWindowType) window_handle; - EGLNativeWindowType native_window = InitializePlatform(host_window, config); + EGLNativeWindowType native_window = InitializePlatform(host_window, configs[0]); s = eglQueryString(egl_dpy, EGL_VERSION); INFO_LOG(G3D, "EGL_VERSION = %s\n", s); @@ -187,18 +269,25 @@ bool cInterfaceEGL::Create(void *window_handle, bool core) s = eglQueryString(egl_dpy, EGL_CLIENT_APIS); INFO_LOG(G3D, "EGL_CLIENT_APIS = %s\n", s); - egl_ctx = eglCreateContext(egl_dpy, config, EGL_NO_CONTEXT, ctx_attribs ); + egl_ctx = eglCreateContext(egl_dpy, configs[0], EGL_NO_CONTEXT, ctx_attribs); if (!egl_ctx) { - INFO_LOG(G3D, "Error: eglCreateContext failed\n"); - exit(1); + INFO_LOG(G3D, "Error: eglCreateContext failed: %s\n", EGLGetErrorString(eglGetError())); + eglTerminate(egl_dpy); + delete[] configs; + return false; } - egl_surf = eglCreateWindowSurface(egl_dpy, config, native_window, nullptr); + egl_surf = eglCreateWindowSurface(egl_dpy, configs[0], native_window, nullptr); if (!egl_surf) { - INFO_LOG(G3D, "Error: eglCreateWindowSurface failed\n"); - exit(1); + INFO_LOG(G3D, "Error: eglCreateWindowSurface failed: native_window=%p error=%s ctx_attribs[1]==%d\n", native_window, EGLGetErrorString(eglGetError()), ctx_attribs[1]); + + eglDestroyContext(egl_dpy, egl_ctx); + eglTerminate(egl_dpy); + delete[] configs; + return false; } + delete[] configs; return true; } diff --git a/Common/GL/GLInterface/EGL.h b/Common/GL/GLInterface/EGL.h index 527af54918..cf8ed1db61 100644 --- a/Common/GL/GLInterface/EGL.h +++ b/Common/GL/GLInterface/EGL.h @@ -21,13 +21,14 @@ protected: virtual EGLNativeWindowType InitializePlatform(EGLNativeWindowType host_window, EGLConfig config) = 0; virtual void ShutdownPlatform() = 0; virtual void SetInternalResolution(int internalWidth, int internalHeight) {} + const char *EGLGetErrorString(EGLint error); public: void SwapInterval(int Interval); void Swap(); void SetMode(u32 mode) { s_opengl_mode = mode; } void* GetFuncAddress(const std::string& name); - bool Create(void *window_handle, bool core); + bool Create(void *window_handle, bool core, bool use16bit) override; bool MakeCurrent(); bool ClearCurrent(); void Shutdown(); diff --git a/Common/GL/GLInterface/EGLAndroid.cpp b/Common/GL/GLInterface/EGLAndroid.cpp index 29796fae29..0d4a0e2339 100644 --- a/Common/GL/GLInterface/EGLAndroid.cpp +++ b/Common/GL/GLInterface/EGLAndroid.cpp @@ -2,17 +2,20 @@ // Licensed under GPLv2+ // Refer to the license.txt file included. +#include "Common/Log.h" #include "Common/GL/GLInterface/EGLAndroid.h" -EGLDisplay cInterfaceEGLAndroid::OpenDisplay() -{ +EGLDisplay cInterfaceEGLAndroid::OpenDisplay() { return eglGetDisplay(EGL_DEFAULT_DISPLAY); } -EGLNativeWindowType cInterfaceEGLAndroid::InitializePlatform(EGLNativeWindowType host_window, EGLConfig config) -{ - EGLint format = 0; - eglGetConfigAttrib(egl_dpy, config, EGL_NATIVE_VISUAL_ID, &format); +EGLNativeWindowType cInterfaceEGLAndroid::InitializePlatform(EGLNativeWindowType host_window, EGLConfig config) { + EGLint format; + if (EGL_FALSE == eglGetConfigAttrib(egl_dpy, config, EGL_NATIVE_VISUAL_ID, &format)) { + ERROR_LOG(G3D, "Failed getting EGL_NATIVE_VISUAL_ID: error %s", EGLGetErrorString(eglGetError())); + return NULL; + } + ANativeWindow_setBuffersGeometry(host_window, internalWidth_, internalHeight_, format); const int width = ANativeWindow_getWidth(host_window); @@ -22,6 +25,5 @@ EGLNativeWindowType cInterfaceEGLAndroid::InitializePlatform(EGLNativeWindowType return host_window; } -void cInterfaceEGLAndroid::ShutdownPlatform() -{ +void cInterfaceEGLAndroid::ShutdownPlatform() { } diff --git a/Common/GL/GLInterfaceBase.h b/Common/GL/GLInterfaceBase.h index 9e8de11303..3d9b8dd10e 100644 --- a/Common/GL/GLInterfaceBase.h +++ b/Common/GL/GLInterfaceBase.h @@ -29,7 +29,7 @@ public: virtual void SetMode(u32 mode) { s_opengl_mode = GLInterfaceMode::MODE_OPENGL; } virtual u32 GetMode() { return s_opengl_mode; } virtual void* GetFuncAddress(const std::string& name) { return nullptr; } - virtual bool Create(void *window_handle, bool core = true) { return true; } + virtual bool Create(void *window_handle, bool core = true, bool use16bit = false) = 0; virtual bool MakeCurrent() { return true; } virtual bool ClearCurrent() { return true; } virtual void Shutdown() {} diff --git a/UI/NativeApp.cpp b/UI/NativeApp.cpp index ecc639e3b6..7843d5e50c 100644 --- a/UI/NativeApp.cpp +++ b/UI/NativeApp.cpp @@ -372,7 +372,7 @@ void NativeInit(int argc, const char *argv[], const char *fileToLog = 0; const char *stateToLoad = 0; - bool gfxLog = false; + bool gfxLog = true; // Parse command line LogTypes::LOG_LEVELS logLevel = LogTypes::LINFO; for (int i = 1; i < argc; i++) { diff --git a/android/jni/app-android.cpp b/android/jni/app-android.cpp index 164bdcffe6..9883bf8130 100644 --- a/android/jni/app-android.cpp +++ b/android/jni/app-android.cpp @@ -68,6 +68,7 @@ static int deviceType; static int display_dpi; static int display_xres; static int display_yres; +static int backbuffer_format; // Android PixelFormat enum static int desiredBackbufferSizeX; static int desiredBackbufferSizeY; @@ -528,12 +529,13 @@ extern "C" void JNICALL Java_org_ppsspp_ppsspp_NativeApp_setDisplayParameters(JN display_hz = refreshRate; } -extern "C" void JNICALL Java_org_ppsspp_ppsspp_NativeApp_backbufferResize(JNIEnv *, jclass, jint bufw, jint bufh) { +extern "C" void JNICALL Java_org_ppsspp_ppsspp_NativeApp_backbufferResize(JNIEnv *, jclass, jint bufw, jint bufh, jint format) { ILOG("NativeApp.backbufferResize(%d x %d)", bufw, bufh); // pixel_*res is the backbuffer resolution. pixel_xres = bufw; pixel_yres = bufh; + backbuffer_format = format; g_dpi = (int)display_dpi; g_dpi_scale = 240.0f / (float)g_dpi; @@ -586,8 +588,20 @@ extern "C" void JNICALL Java_org_ppsspp_ppsspp_NativeActivity_runEGLRenderLoop(J // Apparently we still have to set this through Java through setFixedSize on the bufferHolder for it to take effect... gl->SetBackBufferDimensions(desiredBackbufferSizeX, desiredBackbufferSizeY); - gl->SetMode(MODE_DETECT); - gl->Create(wnd); + gl->SetMode(MODE_OPENGLES2); + + bool use565 = false; + switch (backbuffer_format) { + case 4: // PixelFormat.RGB_565 + use565 = true; + break; + } + + if (!gl->Create(wnd, false, use565)) { + ELOG("EGL creation failed"); + // TODO: What do we do now? + return; + } gl->MakeCurrent(); if (!renderer_inited) { diff --git a/android/src/org/ppsspp/ppsspp/NativeActivity.java b/android/src/org/ppsspp/ppsspp/NativeActivity.java index b9ee3e195c..5ce6805957 100644 --- a/android/src/org/ppsspp/ppsspp/NativeActivity.java +++ b/android/src/org/ppsspp/ppsspp/NativeActivity.java @@ -380,12 +380,12 @@ public class NativeActivity extends Activity implements SurfaceHolder.Callback { @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { - Log.w(TAG, "Surface changed. Resolution: " + width + "x" + height); + Log.w(TAG, "Surface changed. Resolution: " + width + "x" + height + " Format: " + format); // Make sure we have fresh display metrics so the computations go right. // This is needed on some very old devices, I guess event order is different or something... Point sz = new Point(); updateDisplayMetrics(sz); - NativeApp.backbufferResize(width, height); + NativeApp.backbufferResize(width, height, format); mSurface = holder.getSurface(); if (mRenderLoopThread == null || !mRenderLoopThread.isAlive()) { mRenderLoopThread = new Thread(mEmulationRunner); diff --git a/android/src/org/ppsspp/ppsspp/NativeApp.java b/android/src/org/ppsspp/ppsspp/NativeApp.java index 84b2003d5d..e15252191a 100644 --- a/android/src/org/ppsspp/ppsspp/NativeApp.java +++ b/android/src/org/ppsspp/ppsspp/NativeApp.java @@ -24,7 +24,7 @@ public class NativeApp { public static native int getDesiredBackbufferHeight(); public static native void setDisplayParameters(int display_xres, int display_yres, int dpi, float refreshRate); - public static native void backbufferResize(int bufferWidth, int bufferHeight); + public static native void backbufferResize(int bufferWidth, int bufferHeight, int format); public static native boolean isLandscape(); public static native boolean isAtTopLevel(); diff --git a/android/src/org/ppsspp/ppsspp/NativeSurfaceView.java b/android/src/org/ppsspp/ppsspp/NativeSurfaceView.java index 81d8a22adc..f0eb2369e3 100644 --- a/android/src/org/ppsspp/ppsspp/NativeSurfaceView.java +++ b/android/src/org/ppsspp/ppsspp/NativeSurfaceView.java @@ -7,6 +7,7 @@ package org.ppsspp.ppsspp; import android.annotation.SuppressLint; import android.annotation.TargetApi; import android.app.Activity; +import android.graphics.PixelFormat; import android.hardware.Sensor; import android.hardware.SensorEvent; import android.hardware.SensorEventListener; @@ -42,8 +43,12 @@ public class NativeSurfaceView extends SurfaceView implements SensorEventListene if (fixedW != 0 && fixedH != 0) { Log.i(TAG, "Setting surface holder to use a fixed size of " + fixedW + "x" + fixedH + " pixels"); this.getHolder().setFixedSize(fixedW, fixedH); + } else { + Log.i(TAG, "Using default backbuffer size."); } + // this.getHolder().setFormat(PixelFormat.RGBA_8888); + try { MogaHack.init(mController, activity); Log.i(TAG, "MOGA initialized"); From fd2557dbd7bf370a8955ad763d3150a4b43bc0ae Mon Sep 17 00:00:00 2001 From: Henrik Rydgard Date: Wed, 16 Dec 2015 21:48:19 +0100 Subject: [PATCH 6/9] EGL: Add autodetect mode that only chooses between ES2 and ES3 --- Common/GL/GLInterface/EGL.cpp | 4 ++-- Common/GL/GLInterfaceBase.h | 1 + android/jni/app-android.cpp | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/Common/GL/GLInterface/EGL.cpp b/Common/GL/GLInterface/EGL.cpp index a9d8cb3c71..97e1b8bcec 100644 --- a/Common/GL/GLInterface/EGL.cpp +++ b/Common/GL/GLInterface/EGL.cpp @@ -72,7 +72,7 @@ void cInterfaceEGL::DetectMode() ret = eglGetConfigAttrib(egl_dpy, config[i], EGL_RENDERABLE_TYPE, &attribVal); if (ret) { - if (attribVal & EGL_OPENGL_BIT) + if ((attribVal & EGL_OPENGL_BIT) && s_opengl_mode != GLInterfaceMode::MODE_DETECT_ES) supportsGL = true; if (attribVal & (1 << 6)) /* EGL_OPENGL_ES3_BIT_KHR */ supportsGLES3 = true; @@ -169,7 +169,7 @@ bool cInterfaceEGL::Create(void *window_handle, bool core, bool use16bit) } INFO_LOG(G3D, "eglInitialize() succeeded\n"); - if (s_opengl_mode == MODE_DETECT) + if (s_opengl_mode == MODE_DETECT || s_opengl_mode == MODE_DETECT_ES) DetectMode(); int attribs32[] = { diff --git a/Common/GL/GLInterfaceBase.h b/Common/GL/GLInterfaceBase.h index 3d9b8dd10e..250f0f35e9 100644 --- a/Common/GL/GLInterfaceBase.h +++ b/Common/GL/GLInterfaceBase.h @@ -10,6 +10,7 @@ enum GLInterfaceMode { MODE_DETECT = 0, + MODE_DETECT_ES, MODE_OPENGL, MODE_OPENGLES2, MODE_OPENGLES3, diff --git a/android/jni/app-android.cpp b/android/jni/app-android.cpp index 9883bf8130..b5a2272f42 100644 --- a/android/jni/app-android.cpp +++ b/android/jni/app-android.cpp @@ -588,7 +588,7 @@ extern "C" void JNICALL Java_org_ppsspp_ppsspp_NativeActivity_runEGLRenderLoop(J // Apparently we still have to set this through Java through setFixedSize on the bufferHolder for it to take effect... gl->SetBackBufferDimensions(desiredBackbufferSizeX, desiredBackbufferSizeY); - gl->SetMode(MODE_OPENGLES2); + gl->SetMode(MODE_DETECT_ES); bool use565 = false; switch (backbuffer_format) { From 0e1e52242fce463a090dcd56f9fbe7ca4aff01bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Wed, 16 Dec 2015 22:25:01 +0100 Subject: [PATCH 7/9] Remove unused import --- android/src/org/ppsspp/ppsspp/NativeActivity.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/android/src/org/ppsspp/ppsspp/NativeActivity.java b/android/src/org/ppsspp/ppsspp/NativeActivity.java index 5ce6805957..1f1e1af0a0 100644 --- a/android/src/org/ppsspp/ppsspp/NativeActivity.java +++ b/android/src/org/ppsspp/ppsspp/NativeActivity.java @@ -5,8 +5,6 @@ import java.lang.reflect.Field; import java.util.List; import java.util.Locale; -import com.google.common.collect.Sets.SetView; - import android.annotation.SuppressLint; import android.annotation.TargetApi; import android.app.Activity; From 38263c907367a7622aa012ce06c5f61f6d13e240 Mon Sep 17 00:00:00 2001 From: Henrik Rydgard Date: Wed, 16 Dec 2015 22:30:23 +0100 Subject: [PATCH 8/9] Turn gfxlog off again --- UI/NativeApp.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/UI/NativeApp.cpp b/UI/NativeApp.cpp index 7843d5e50c..ecc639e3b6 100644 --- a/UI/NativeApp.cpp +++ b/UI/NativeApp.cpp @@ -372,7 +372,7 @@ void NativeInit(int argc, const char *argv[], const char *fileToLog = 0; const char *stateToLoad = 0; - bool gfxLog = true; + bool gfxLog = false; // Parse command line LogTypes::LOG_LEVELS logLevel = LogTypes::LINFO; for (int i = 1; i < argc; i++) { From ebf9bd74c629f502d11d7864377ab1a85e08e16c Mon Sep 17 00:00:00 2001 From: Henrik Rydgard Date: Thu, 17 Dec 2015 19:38:23 +0100 Subject: [PATCH 9/9] Minor fixes --- Common/GL/GLInterface/EGL.cpp | 5 ++++- android/jni/app-android.cpp | 9 +++++---- android/src/org/ppsspp/ppsspp/NativeActivity.java | 15 +++++++-------- 3 files changed, 16 insertions(+), 13 deletions(-) diff --git a/Common/GL/GLInterface/EGL.cpp b/Common/GL/GLInterface/EGL.cpp index 97e1b8bcec..dee999b160 100644 --- a/Common/GL/GLInterface/EGL.cpp +++ b/Common/GL/GLInterface/EGL.cpp @@ -38,13 +38,16 @@ void cInterfaceEGL::DetectMode() // attributes for a visual in RGBA format with at least // 8 bits per color int attribs[] = { + EGL_RENDERABLE_TYPE, renderable_type, EGL_RED_SIZE, 8, EGL_GREEN_SIZE, 8, EGL_BLUE_SIZE, 8, EGL_ALPHA_SIZE, 8, EGL_DEPTH_SIZE, 16, EGL_STENCIL_SIZE, 8, - EGL_RENDERABLE_TYPE, renderable_type, + EGL_SURFACE_TYPE, EGL_WINDOW_BIT, + EGL_TRANSPARENT_TYPE, EGL_NONE, + EGL_SAMPLES, 0, EGL_NONE }; diff --git a/android/jni/app-android.cpp b/android/jni/app-android.cpp index b5a2272f42..c812c25771 100644 --- a/android/jni/app-android.cpp +++ b/android/jni/app-android.cpp @@ -569,20 +569,20 @@ extern "C" jint JNICALL Java_org_ppsspp_ppsspp_NativeApp_getDesiredBackbufferHei return desiredBackbufferSizeY; } -extern "C" void JNICALL Java_org_ppsspp_ppsspp_NativeActivity_runEGLRenderLoop(JNIEnv *env, jobject obj, jobject _surf) { +extern "C" bool JNICALL Java_org_ppsspp_ppsspp_NativeActivity_runEGLRenderLoop(JNIEnv *env, jobject obj, jobject _surf) { ANativeWindow *wnd = ANativeWindow_fromSurface(env, _surf); WLOG("runEGLRenderLoop. display_xres=%d display_yres=%d", display_xres, display_yres); if (wnd == nullptr) { ELOG("Error: Surface is null."); - return; + return false; } cInterfaceBase *gl = HostGL_CreateGLInterface(); if (!gl) { ELOG("ERROR: Failed to create GL interface"); - return; + return false; } ILOG("EGL interface created. Desired backbuffer size: %dx%d", desiredBackbufferSizeX, desiredBackbufferSizeY); @@ -600,7 +600,7 @@ extern "C" void JNICALL Java_org_ppsspp_ppsspp_NativeActivity_runEGLRenderLoop(J if (!gl->Create(wnd, false, use565)) { ELOG("EGL creation failed"); // TODO: What do we do now? - return; + return false; } gl->MakeCurrent(); @@ -671,4 +671,5 @@ extern "C" void JNICALL Java_org_ppsspp_ppsspp_NativeActivity_runEGLRenderLoop(J ANativeWindow_release(wnd); renderLoopRunning = false; WLOG("Render loop exited;"); + return true; } diff --git a/android/src/org/ppsspp/ppsspp/NativeActivity.java b/android/src/org/ppsspp/ppsspp/NativeActivity.java index 1f1e1af0a0..5e91e96ea9 100644 --- a/android/src/org/ppsspp/ppsspp/NativeActivity.java +++ b/android/src/org/ppsspp/ppsspp/NativeActivity.java @@ -299,12 +299,15 @@ public class NativeActivity extends Activity implements SurfaceHolder.Callback { Log.i(TAG, "Starting the render loop: " + mSurface); // Start emulation using the provided Surface. - runEGLRenderLoop(mSurface); + if (!runEGLRenderLoop(mSurface)) { + // TODO: Add an alert dialog or something + Log.e(TAG, "Failed to start up OpenGL"); + } Log.i(TAG, "Left the render loop: " + mSurface); } }; - public native void runEGLRenderLoop(Surface surface); + public native boolean runEGLRenderLoop(Surface surface); // Tells the render loop thread to exit, so we can restart it. public native void exitEGLRenderLoop(); @@ -335,9 +338,7 @@ public class NativeActivity extends Activity implements SurfaceHolder.Callback { } // OK, config should be initialized, we can query for screen rotation. - if (Build.VERSION.SDK_INT >= 9) { - updateScreenRotation(); - } + updateScreenRotation(); // Keep the screen bright - very annoying if it goes dark when tilting away Window window = this.getWindow(); @@ -457,9 +458,7 @@ public class NativeActivity extends Activity implements SurfaceHolder.Callback { updateSystemUiVisibility(); } // OK, config should be initialized, we can query for screen rotation. - if (Build.VERSION.SDK_INT >= 9) { - updateScreenRotation(); - } + updateScreenRotation(); Log.i(TAG, "onResume"); if (mSurfaceView != null) {