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 new file mode 100644 index 0000000000..dee999b160 --- /dev/null +++ b/Common/GL/GLInterface/EGL.cpp @@ -0,0 +1,324 @@ +// 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() +{ + 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_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_SURFACE_TYPE, EGL_WINDOW_BIT, + EGL_TRANSPARENT_TYPE, EGL_NONE, + EGL_SAMPLES, 0, + 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) && s_opengl_mode != GLInterfaceMode::MODE_DETECT_ES) + 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 +} + +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 use16bit) +{ + 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; + } + INFO_LOG(G3D, "eglInitialize() succeeded\n"); + + if (s_opengl_mode == MODE_DETECT || s_opengl_mode == MODE_DETECT_ES) + DetectMode(); + + 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_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, 0 + }; + + 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; + } + + 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; + } + + 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); + else + eglBindAPI(EGL_OPENGL_ES_API); + + EGLNativeWindowType host_window = (EGLNativeWindowType) window_handle; + EGLNativeWindowType native_window = InitializePlatform(host_window, configs[0]); + + 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, configs[0], EGL_NO_CONTEXT, ctx_attribs); + if (!egl_ctx) { + INFO_LOG(G3D, "Error: eglCreateContext failed: %s\n", EGLGetErrorString(eglGetError())); + eglTerminate(egl_dpy); + delete[] configs; + return false; + } + + egl_surf = eglCreateWindowSurface(egl_dpy, configs[0], native_window, nullptr); + if (!egl_surf) { + 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; +} + +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); +} + +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..cf8ed1db61 --- /dev/null +++ b/Common/GL/GLInterface/EGL.h @@ -0,0 +1,35 @@ +// 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; + 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 use16bit) override; + 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..0d4a0e2339 --- /dev/null +++ b/Common/GL/GLInterface/EGLAndroid.cpp @@ -0,0 +1,29 @@ +// Copyright 2014 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#include "Common/Log.h" +#include "Common/GL/GLInterface/EGLAndroid.h" + +EGLDisplay cInterfaceEGLAndroid::OpenDisplay() { + return eglGetDisplay(EGL_DEFAULT_DISPLAY); +} + +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); + 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..811485d91b --- /dev/null +++ b/Common/GL/GLInterface/EGLAndroid.h @@ -0,0 +1,24 @@ +// 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 { +public: + cInterfaceEGLAndroid() : internalWidth_(0), internalHeight_(0) {} +protected: + EGLDisplay OpenDisplay() override; + EGLNativeWindowType InitializePlatform(EGLNativeWindowType host_window, EGLConfig config) override; + void ShutdownPlatform() override; + void OverrideBackbufferDimensions(int internalWidth, int internalHeight) override { + internalWidth_ = internalWidth; + internalHeight_ = internalHeight; + } + +private: + int internalWidth_; + int internalHeight_; +}; diff --git a/Common/GL/GLInterface/GLInterface.cpp b/Common/GL/GLInterface/GLInterface.cpp new file mode 100644 index 0000000000..bf8d729745 --- /dev/null +++ b/Common/GL/GLInterface/GLInterface.cpp @@ -0,0 +1,39 @@ +// 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..250f0f35e9 --- /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_DETECT_ES, + 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, bool use16bit = false) = 0; + 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 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; } +}; + + +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..c812c25771 100644 --- a/android/jni/app-android.cpp +++ b/android/jni/app-android.cpp @@ -6,6 +6,7 @@ #include #include +#include #include #include #include @@ -27,6 +28,8 @@ #include "android/jni/native_audio.h" #include "gfx/gl_common.h" +#include "Common/GL/GLInterfaceBase.h" + #include "app-android.h" static JNIEnv *jniEnvUI; @@ -61,11 +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 backbuffer_format; // Android PixelFormat enum + +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) { @@ -143,18 +164,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); @@ -202,7 +211,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; @@ -226,8 +235,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/")); @@ -313,97 +320,8 @@ 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) { - 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_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 +456,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 +465,211 @@ 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); + } +} + +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, 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; + + 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" 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 false; + } + + cInterfaceBase *gl = HostGL_CreateGLInterface(); + if (!gl) { + ELOG("ERROR: Failed to create GL interface"); + return false; + } + 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_ES); + + 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 false; + } + 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); + } + } + + // 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); + 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 f9017ba0cb..5e91e96ea9 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; @@ -37,6 +35,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 +46,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 +107,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,33 +156,29 @@ 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 - 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 { appInfo = packMgmr.getApplicationInfo(packageName, 0); - } catch (NameNotFoundException e) { + } catch (NameNotFoundException e) { e.printStackTrace(); throw new RuntimeException("Unable to locate assets, aborting..."); } @@ -205,42 +198,22 @@ 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); 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()); - // 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 +231,8 @@ public class NativeActivity extends Activity { Log.e(TAG, "Invalid rotation: " + rotString); return; } + Log.i(TAG, "Setting requested rotation: " + rot + " ('" + rotString + "')"); + switch (rot) { case 0: setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED); @@ -276,10 +251,12 @@ public class NativeActivity extends Activity { break; } } - + 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") @@ -298,7 +275,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() { @@ -309,71 +286,67 @@ public class NativeActivity extends Activity { } } - // 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 + 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. + 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 boolean runEGLRenderLoop(Surface surface); + // Tells the render loop thread to exit, so we can restart it. + public native void exitEGLRenderLoop(); + + void updateDisplayMetrics(Point outSize) { + DisplayMetrics metrics = new DisplayMetrics(); + Display display = getWindowManager().getDefaultDisplay(); + display.getMetrics(metrics); + + float refreshRate = display.getRefreshRate(); + if (outSize == null) { + outSize = new Point(); + } + GetScreenSize(outSize); + NativeApp.setDisplayParameters(outSize.x, outSize.y, metrics.densityDpi, refreshRate); } - @Override + @Override public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); + super.onCreate(savedInstanceState); registerCallbacks(); installID = Installation.id(this); + updateDisplayMetrics(null); + if (!initialized) { Initialize(); initialized = true; } + + // OK, config should be initialized, we can query for screen rotation. + 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); - - 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); - } - 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); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) { updateSystemUiVisibility(); @@ -381,8 +354,62 @@ public class NativeActivity extends Activity { setupSystemUiCallback(); } } + updateDisplayMetrics(null); + + NativeApp.computeDesiredBackbufferDimensions(); + int bbW = NativeApp.getDesiredBackbufferWidth(); + int bbH = NativeApp.getDesiredBackbufferHeight(); + + mSurfaceView = new NativeSurfaceView(NativeActivity.this, bbW, bbH); + mSurfaceView.getHolder().addCallback(NativeActivity.this); + Log.i(TAG, "setcontentview before"); + setContentView(mSurfaceView); + Log.i(TAG, "setcontentview after"); + + 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 + " 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, format); + 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,48 +421,36 @@ 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(); - return info.reqGlEsVersion >= 0x20000; - } + } - private boolean detectOpenGLES30() { - ActivityManager am = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE); - 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(); @@ -443,33 +458,27 @@ public class NativeActivity extends Activity { 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 (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) { + Log.i(TAG, "onConfigurationChanged"); 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); - } + updateDisplayMetrics(null); } //keep this static so we can call this even if we don't @@ -480,7 +489,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 +497,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 +588,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 +657,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 +712,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 +731,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 +742,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 +836,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 +861,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 +878,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) { @@ -885,14 +896,16 @@ public class NativeActivity extends Activity { } 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) { 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..e15252191a 100644 --- a/android/src/org/ppsspp/ppsspp/NativeApp.java +++ b/android/src/org/ppsspp/ppsspp/NativeApp.java @@ -1,24 +1,31 @@ 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 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 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, int format); + public static native boolean isLandscape(); public static native boolean isAtTopLevel(); @@ -35,7 +42,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 +54,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..f0eb2369e3 100644 --- a/android/src/org/ppsspp/ppsspp/NativeGLView.java +++ b/android/src/org/ppsspp/ppsspp/NativeSurfaceView.java @@ -1,59 +1,54 @@ 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.PixelFormat; 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.os.Build; -// import android.util.Log; import android.util.Log; import android.view.MotionEvent; +import android.view.SurfaceView; + import com.bda.controller.*; -public class NativeGLView extends GLSurfaceView implements SensorEventListener, ControllerListener { - private static String TAG = "NativeGLView"; +public class NativeSurfaceView extends SurfaceView implements SensorEventListener, ControllerListener { + private static String TAG = "NativeSurfaceView"; private SensorManager mSensorManager; private Sensor mAccelerometer; - private NativeActivity mActivity; - + // Moga controller private Controller mController = null; private boolean isMogaPro = false; - - public NativeGLView(NativeActivity activity) { - super(activity); - 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(); - } - }*/ - + public NativeSurfaceView(NativeActivity activity, int fixedW, int fixedH) { + super(activity); + + Log.i(TAG, "NativeSurfaceView"); + mSensorManager = (SensorManager)activity.getSystemService(Activity.SENSOR_SERVICE); mAccelerometer = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER); - + mController = Controller.getInstance(activity); + + // 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); + } else { + Log.i(TAG, "Using default backbuffer size."); + } + + // this.getHolder().setFormat(PixelFormat.RGBA_8888); + try { MogaHack.init(mController, activity); Log.i(TAG, "MOGA initialized"); @@ -67,19 +62,19 @@ public class NativeGLView extends GLSurfaceView implements SensorEventListener, 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)mActivity.getRenderer().getDpiScaleX(); - float scaleY = (float)mActivity.getRenderer().getDpiScaleY(); 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,23 +93,25 @@ public class NativeGLView extends GLSurfaceView implements SensorEventListener, default: break; } - + if (code != 0) { if (canReadToolType) { int tool = getToolType(ev, i); 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; } // 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 +119,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 +209,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..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; @@ -11,11 +10,12 @@ 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; - 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(); @@ -47,15 +52,15 @@ public class PpssppActivity extends NativeActivity { } Looper.loop(); } - + }.start(); - + try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } - + System.exit(-1); return; } @@ -68,61 +73,13 @@ 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, (float)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) { final String cmd = command; final String param = parameter; runOnUiThread(new Runnable() { + @Override public void run() { processCommand(cmd, param); } 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;