Merge pull request #8274 from hrydgard/android-native-egl

Initialize OpenGL from C++ using EGL on Android
This commit is contained in:
Henrik Rydgård committed 2015-12-17 19:38:41 +01:00
commit 99fe0981ee
20 files changed
+1057 -697

No files matched your search

+35 -4
View File
@@ -72,7 +72,7 @@
<PreprocessorDefinitions>USING_WIN_UI;_CRT_SECURE_NO_WARNINGS;WIN32;_ARCH_32=1;_M_IX86=1;_DEBUG;_LIB;_UNICODE;UNICODE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ForcedIncludeFiles>stdafx.h</ForcedIncludeFiles>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
<AdditionalIncludeDirectories>../ext/native</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>../ext/native;..</AdditionalIncludeDirectories>
<EnableEnhancedInstructionSet>StreamingSIMDExtensions2</EnableEnhancedInstructionSet>
<FloatingPointModel>Fast</FloatingPointModel>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
@@ -99,7 +99,7 @@
<ForcedIncludeFiles>stdafx.h</ForcedIncludeFiles>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
<PreprocessorDefinitions>USING_WIN_UI;_CRT_SECURE_NO_WARNINGS;WIN32;_ARCH_64=1;_M_X64=1;_DEBUG;_LIB;_UNICODE;UNICODE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>../ext/native</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>../ext/native;..</AdditionalIncludeDirectories>
<EnableEnhancedInstructionSet>NotSet</EnableEnhancedInstructionSet>
<FloatingPointModel>Fast</FloatingPointModel>
<OmitFramePointers>false</OmitFramePointers>
@@ -132,7 +132,7 @@
<BufferSecurityCheck>false</BufferSecurityCheck>
<EnableEnhancedInstructionSet>StreamingSIMDExtensions2</EnableEnhancedInstructionSet>
<FloatingPointModel>Fast</FloatingPointModel>
<AdditionalIncludeDirectories>../ext/native</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>../ext/native;..</AdditionalIncludeDirectories>
<FavorSizeOrSpeed>Speed</FavorSizeOrSpeed>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
<RuntimeTypeInfo>false</RuntimeTypeInfo>
@@ -161,7 +161,7 @@
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>USING_WIN_UI;_CRT_SECURE_NO_WARNINGS;WIN32;_ARCH_64=1;_M_X64=1;NDEBUG;_LIB;_UNICODE;UNICODE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ForcedIncludeFiles>stdafx.h</ForcedIncludeFiles>
<AdditionalIncludeDirectories>../ext/native</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>../ext/native;..</AdditionalIncludeDirectories>
<BufferSecurityCheck>false</BufferSecurityCheck>
<EnableEnhancedInstructionSet>NotSet</EnableEnhancedInstructionSet>
<FloatingPointModel>Fast</FloatingPointModel>
@@ -209,6 +209,19 @@
<ClInclude Include="Crypto\sha256.h" />
<ClInclude Include="FileUtil.h" />
<ClInclude Include="FixedSizeQueue.h" />
<ClInclude Include="GL\GLInterfaceBase.h" />
<ClInclude Include="GL\GLInterface\EGL.h">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
</ClInclude>
<ClInclude Include="GL\GLInterface\EGLAndroid.h">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
</ClInclude>
<ClInclude Include="KeyMap.h" />
<ClInclude Include="Log.h" />
<ClInclude Include="LogManager.h" />
@@ -258,6 +271,24 @@
<ClCompile Include="Crypto\sha1.cpp" />
<ClCompile Include="Crypto\sha256.cpp" />
<ClCompile Include="FileUtil.cpp" />
<ClCompile Include="GL\GLInterface\EGL.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="GL\GLInterface\EGLAndroid.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="GL\GLInterface\GLInterface.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="KeyMap.cpp" />
<ClCompile Include="LogManager.cpp" />
<ClCompile Include="MemArena.cpp" />
+24
View File
@@ -48,6 +48,15 @@
<ClInclude Include="ColorConv.h" />
<ClInclude Include="ColorConvNEON.h" />
<ClInclude Include="ThreadSafeList.h" />
<ClInclude Include="GL\GLInterface\EGL.h">
<Filter>GL\GLInterface</Filter>
</ClInclude>
<ClInclude Include="GL\GLInterface\EGLAndroid.h">
<Filter>GL\GLInterface</Filter>
</ClInclude>
<ClInclude Include="GL\GLInterfaceBase.h">
<Filter>GL</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="stdafx.cpp" />
@@ -84,6 +93,15 @@
<ClCompile Include="Arm64Emitter.cpp" />
<ClCompile Include="ColorConv.cpp" />
<ClCompile Include="ColorConvNEON.cpp" />
<ClCompile Include="GL\GLInterface\EGL.cpp">
<Filter>GL\GLInterface</Filter>
</ClCompile>
<ClCompile Include="GL\GLInterface\EGLAndroid.cpp">
<Filter>GL\GLInterface</Filter>
</ClCompile>
<ClCompile Include="GL\GLInterface\GLInterface.cpp">
<Filter>GL\GLInterface</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<None Include="CMakeLists.txt" />
@@ -92,5 +110,11 @@
<Filter Include="Crypto">
<UniqueIdentifier>{1b593f03-7b28-4707-9228-4981796f5589}</UniqueIdentifier>
</Filter>
<Filter Include="GL">
<UniqueIdentifier>{2f2ca112-9e26-499e-9cb9-38a78b4ac09d}</UniqueIdentifier>
</Filter>
<Filter Include="GL\GLInterface">
<UniqueIdentifier>{2c723cf4-75b6-406a-90c0-ebb7a13ba476}</UniqueIdentifier>
</Filter>
</ItemGroup>
</Project>
+324
View File
@@ -0,0 +1,324 @@
// Copyright 2012 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#include <array>
#include <cstdlib>
#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<int, 3> 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;
}
}
+35
View File
@@ -0,0 +1,35 @@
// Copyright 2008 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#pragma once
#include <string>
#include <EGL/egl.h>
#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();
};
+29
View File
@@ -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() {
}
+24
View File
@@ -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_;
};
+39
View File
@@ -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
}
+50
View File
@@ -0,0 +1,50 @@
// Copyright 2008 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#pragma once
#include <string>
#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();
+1
View File
@@ -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;
}
+3 -2
View File
@@ -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");
+6
View File
@@ -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 \
+1 -1
View File
@@ -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)
+231 -105
View File
@@ -6,6 +6,7 @@
#include <jni.h>
#include <android/log.h>
#include <android/native_window_jni.h>
#include <stdlib.h>
#include <stdint.h>
#include <GLES2/gl2.h>
@@ -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;
}
+181 -168
View File
@@ -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;
+18 -12
View File
@@ -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);
}
@@ -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;
}
}
@@ -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();
}
@@ -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:
@@ -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);
}
+1 -1
View File
@@ -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;