Use EGL from native code to initialize and run OpenGL on Android.

Should be more robust, allows initializing desktop GL if available, and lets us take control of the render loop.
This commit is contained in:
Henrik Rydgard 2015-12-13 22:25:58 +01:00
parent c2eb6a2fcb
commit 04f8bffa29
16 changed files with 379 additions and 576 deletions

View File

@ -44,6 +44,9 @@ void cInterfaceEGL::DetectMode()
EGL_RED_SIZE, 8,
EGL_GREEN_SIZE, 8,
EGL_BLUE_SIZE, 8,
EGL_ALPHA_SIZE, 8,
EGL_DEPTH_SIZE, 16,
EGL_STENCIL_SIZE, 8,
EGL_RENDERABLE_TYPE, renderable_type,
EGL_NONE
};
@ -95,7 +98,6 @@ void cInterfaceEGL::DetectMode()
}
// Create rendering window.
// Call browser: Core.cpp:EmuThread() > main.cpp:Video_Initialize()
bool cInterfaceEGL::Create(void *window_handle, bool core)
{
const char *s;
@ -128,6 +130,9 @@ bool cInterfaceEGL::Create(void *window_handle, bool core)
EGL_RED_SIZE, 8,
EGL_GREEN_SIZE, 8,
EGL_BLUE_SIZE, 8,
EGL_ALPHA_SIZE, 8,
EGL_DEPTH_SIZE, 16,
EGL_STENCIL_SIZE, 8,
EGL_NONE };
EGLint ctx_attribs[] = {
@ -154,8 +159,7 @@ bool cInterfaceEGL::Create(void *window_handle, bool core)
break;
}
if (!eglChooseConfig( egl_dpy, attribs, &config, 1, &num_configs))
{
if (!eglChooseConfig( egl_dpy, attribs, &config, 1, &num_configs)) {
INFO_LOG(G3D, "Error: couldn't get an EGL visual config\n");
exit(1);
}
@ -181,15 +185,13 @@ bool cInterfaceEGL::Create(void *window_handle, bool core)
INFO_LOG(G3D, "EGL_CLIENT_APIS = %s\n", s);
egl_ctx = eglCreateContext(egl_dpy, config, EGL_NO_CONTEXT, ctx_attribs );
if (!egl_ctx)
{
if (!egl_ctx) {
INFO_LOG(G3D, "Error: eglCreateContext failed\n");
exit(1);
}
egl_surf = eglCreateWindowSurface(egl_dpy, config, native_window, nullptr);
if (!egl_surf)
{
if (!egl_surf) {
INFO_LOG(G3D, "Error: eglCreateWindowSurface failed\n");
exit(1);
}
@ -207,7 +209,6 @@ bool cInterfaceEGL::ClearCurrent()
return eglMakeCurrent(egl_dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
}
// Close backend
void cInterfaceEGL::Shutdown()
{
ShutdownPlatform();

View File

@ -20,6 +20,8 @@ protected:
virtual EGLDisplay OpenDisplay() = 0;
virtual EGLNativeWindowType InitializePlatform(EGLNativeWindowType host_window, EGLConfig config) = 0;
virtual void ShutdownPlatform() = 0;
virtual void SetInternalResolution(int internalWidth, int internalHeight) {}
public:
void SwapInterval(int Interval);
void Swap();

View File

@ -11,9 +11,9 @@ EGLDisplay cInterfaceEGLAndroid::OpenDisplay()
EGLNativeWindowType cInterfaceEGLAndroid::InitializePlatform(EGLNativeWindowType host_window, EGLConfig config)
{
EGLint format;
EGLint format = 0;
eglGetConfigAttrib(egl_dpy, config, EGL_NATIVE_VISUAL_ID, &format);
ANativeWindow_setBuffersGeometry(host_window, 0, 0, format);
ANativeWindow_setBuffersGeometry(host_window, internalWidth_, internalHeight_, format);
const int width = ANativeWindow_getWidth(host_window);
const int height = ANativeWindow_getHeight(host_window);

View File

@ -6,10 +6,19 @@
#include "Common/GL/GLInterface/EGL.h"
class cInterfaceEGLAndroid : public cInterfaceEGL
{
class cInterfaceEGLAndroid : public cInterfaceEGL {
public:
cInterfaceEGLAndroid() : internalWidth_(0), internalHeight_(0) {}
protected:
EGLDisplay OpenDisplay() override;
EGLNativeWindowType InitializePlatform(EGLNativeWindowType host_window, EGLConfig config) override;
void ShutdownPlatform() override;
void SetInternalResolution(int internalWidth, int internalHeight) override {
internalWidth_ = internalWidth;
internalHeight_ = internalHeight;
}
private:
int internalWidth_;
int internalHeight_;
};

View File

@ -43,8 +43,5 @@ public:
virtual bool PeekMessages() { return false; }
};
extern cInterfaceBase *GLInterface;
// This function has to be defined along the Host_ functions from Core/Host.h.
// Current canonical implementation: DolphinWX/GLInterface/GLInterface.cpp.
cInterfaceBase* HostGL_CreateGLInterface();

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;
}

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");

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 \

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)

View File

@ -6,10 +6,12 @@
#include <jni.h>
#include <android/log.h>
#include <android/native_window_jni.h>
#include <stdlib.h>
#include <stdint.h>
#include <GLES2/gl2.h>
#include <GLES2/gl2ext.h>
#include <EGL/EGL.h>
#include <queue>
#include "base/basictypes.h"
@ -27,6 +29,8 @@
#include "android/jni/native_audio.h"
#include "gfx/gl_common.h"
#include "Common/GL/GLInterfaceBase.h"
#include "app-android.h"
static JNIEnv *jniEnvUI;
@ -66,6 +70,8 @@ static int display_yres;
static jmethodID postCommand;
static jobject nativeActivity;
static volatile bool exitRenderLoop;
bool renderLoopRunning;
// Android implementation of callbacks to the Java part of the app
void SystemToast(const char *text) {
@ -313,18 +319,7 @@ extern "C" void Java_org_ppsspp_ppsspp_NativeApp_shutdown(JNIEnv *, jclass) {
ILOG("NativeApp.shutdown() -- end");
}
extern "C" void Java_org_ppsspp_ppsspp_NativeRenderer_displayInit(JNIEnv * env, jobject obj) {
ILOG("NativeApp.displayInit()");
if (!renderer_inited) {
NativeInitGraphics();
renderer_inited = true;
} else {
NativeDeviceLost(); // ???
ILOG("displayInit: NativeDeviceLost completed.");
}
}
extern "C" void Java_org_ppsspp_ppsspp_NativeRenderer_displayResize(JNIEnv *, jobject clazz, jint w, jint h, jint dpi, jfloat refreshRate) {
extern "C" void Java_org_ppsspp_ppsspp_NativeApp_displayResize(JNIEnv *, jclass, jint w, jint h, jint dpi, jfloat refreshRate) {
ILOG("NativeApp.displayResize(%i x %i, dpi=%i, refresh=%0.2f)", w, h, dpi, refreshRate);
g_dpi = dpi;
@ -341,69 +336,8 @@ extern "C" void Java_org_ppsspp_ppsspp_NativeRenderer_displayResize(JNIEnv *, jo
NativeResized();
}
extern "C" void Java_org_ppsspp_ppsspp_NativeRenderer_displayRender(JNIEnv *env, jobject obj) {
static bool hasSetThreadName = false;
if (!hasSetThreadName) {
hasSetThreadName = true;
setCurrentThreadName("AndroidRender");
}
if (renderer_inited) {
// TODO: Look into if these locks are a perf loss
{
lock_guard guard(input_state.lock);
input_state.pad_lstick_x = left_joystick_x_async;
input_state.pad_lstick_y = left_joystick_y_async;
input_state.pad_rstick_x = right_joystick_x_async;
input_state.pad_rstick_y = right_joystick_y_async;
UpdateInputState(&input_state);
}
NativeUpdate(input_state);
{
lock_guard guard(input_state.lock);
EndInputState(&input_state);
}
NativeRender();
time_update();
} else {
ELOG("BAD: Ended up in nativeRender even though app has quit.%s", "");
// Shouldn't really get here. Let's draw magenta.
glDepthMask(GL_TRUE);
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
glClearColor(1.0, 0.0, 1.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
}
lock_guard guard(frameCommandLock);
if (!nativeActivity) {
while (!frameCommands.empty())
frameCommands.pop();
return;
}
while (!frameCommands.empty()) {
FrameCommand frameCmd;
frameCmd = frameCommands.front();
frameCommands.pop();
DLOG("frameCommand %s %s", frameCmd.command.c_str(), frameCmd.params.c_str());
jstring cmd = env->NewStringUTF(frameCmd.command.c_str());
jstring param = env->NewStringUTF(frameCmd.params.c_str());
env->CallVoidMethod(nativeActivity, postCommand, cmd, param);
env->DeleteLocalRef(cmd);
env->DeleteLocalRef(param);
}
}
extern "C" void Java_org_ppsspp_ppsspp_NativeRenderer_displayShutdown(JNIEnv *env, jobject obj) {
if (renderer_inited) {
NativeDeviceLost();
ILOG("NativeDeviceLost completed.");
NativeShutdownGraphics();
renderer_inited = false;
NativeMessageReceived("recreateviews", "");
}
@ -538,7 +472,7 @@ extern "C" jboolean JNICALL Java_org_ppsspp_ppsspp_NativeApp_accelerometer(JNIEn
return retvalX || retvalY || retvalZ;
}
extern "C" void Java_org_ppsspp_ppsspp_NativeApp_sendMessage(JNIEnv *env, jclass, jstring message, jstring param) {
extern "C" void JNICALL Java_org_ppsspp_ppsspp_NativeApp_sendMessage(JNIEnv *env, jclass, jstring message, jstring param) {
std::string msg = GetJavaString(env, message);
std::string prm = GetJavaString(env, param);
@ -547,3 +481,99 @@ extern "C" void Java_org_ppsspp_ppsspp_NativeApp_sendMessage(JNIEnv *env, jclass
}
NativeMessageReceived(msg.c_str(), prm.c_str());
}
extern "C" void JNICALL Java_org_ppsspp_ppsspp_NativeActivity_exitEGLRenderLoop(JNIEnv *env, jobject obj) {
if (!renderLoopRunning) {
ELOG("Render loop already exited");
return;
}
exitRenderLoop = true;
while (renderLoopRunning) {
sleep_ms(10);
}
}
extern "C" void JNICALL Java_org_ppsspp_ppsspp_NativeActivity_runEGLRenderLoop(JNIEnv *env, jobject obj, jobject _surf) {
ANativeWindow *wnd = ANativeWindow_fromSurface(env, _surf);
WLOG("runEGLRenderLoop");
if (wnd == nullptr) {
ELOG("Error: Surface is null.");
return;
}
cInterfaceBase *gl = HostGL_CreateGLInterface();
if (!gl) {
ELOG("ERROR: Failed to create GL interface");
return;
}
gl->SetMode(MODE_DETECT);
gl->Create(wnd);
gl->MakeCurrent();
if (!renderer_inited) {
NativeInitGraphics();
renderer_inited = true;
}
exitRenderLoop = false;
renderLoopRunning = true;
while (!exitRenderLoop) {
static bool hasSetThreadName = false;
if (!hasSetThreadName) {
hasSetThreadName = true;
setCurrentThreadName("AndroidRender");
}
// TODO: Look into if these locks are a perf loss
{
lock_guard guard(input_state.lock);
input_state.pad_lstick_x = left_joystick_x_async;
input_state.pad_lstick_y = left_joystick_y_async;
input_state.pad_rstick_x = right_joystick_x_async;
input_state.pad_rstick_y = right_joystick_y_async;
UpdateInputState(&input_state);
}
NativeUpdate(input_state);
{
lock_guard guard(input_state.lock);
EndInputState(&input_state);
}
NativeRender();
time_update();
gl->Swap();
lock_guard guard(frameCommandLock);
while (!frameCommands.empty()) {
FrameCommand frameCmd;
frameCmd = frameCommands.front();
frameCommands.pop();
WLOG("frameCommand! '%s' '%s'", frameCmd.command.c_str(), frameCmd.params.c_str());
jstring cmd = env->NewStringUTF(frameCmd.command.c_str());
jstring param = env->NewStringUTF(frameCmd.params.c_str());
env->CallVoidMethod(nativeActivity, postCommand, cmd, param);
env->DeleteLocalRef(cmd);
env->DeleteLocalRef(param);
}
}
NativeDeviceLost();
ILOG("NativeDeviceLost completed.");
NativeShutdownGraphics();
renderer_inited = false;
delete gl;
ANativeWindow_release(wnd);
renderLoopRunning = false;
WLOG("Render loop exited;");
}

View File

@ -37,6 +37,8 @@ import android.view.InputEvent;
import android.view.KeyEvent;
import android.view.HapticFeedbackConstants;
import android.view.MotionEvent;
import android.view.Surface;
import android.view.SurfaceHolder;
import android.view.View;
import android.view.View.OnSystemUiVisibilityChangeListener;
import android.view.Window;
@ -46,7 +48,7 @@ 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
@ -56,8 +58,9 @@ public class NativeActivity extends Activity {
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 = "";
@ -93,10 +96,6 @@ public class NativeActivity extends Activity {
return true;
}
NativeRenderer getRenderer() {
return nativeRenderer;
}
@TargetApi(17)
private void detectOptimalAudioSettings() {
try {
@ -223,24 +222,6 @@ public class NativeActivity extends Activity {
NativeApp.sendMessage("cacheDir", getCacheDir().getAbsolutePath());
// OK, config should be initialized, we can query for screen rotation.
if (Build.VERSION.SDK_INT >= 9) {
updateScreenRotation();
}
// Detect OpenGL support.
// We don't currently use this detection for anything but good to have in the log.
if (!detectOpenGLES20()) {
Log.i(TAG, "OpenGL ES 2.0 NOT detected. Things will likely go badly.");
} else {
if (detectOpenGLES30()) {
Log.i(TAG, "OpenGL ES 3.0 detected.");
}
else {
Log.i(TAG, "OpenGL ES 2.0 detected.");
}
}
vibrator = (Vibrator)getSystemService(VIBRATOR_SERVICE);
if (Build.VERSION.SDK_INT >= 11) {
checkForVibrator();
@ -258,6 +239,7 @@ public class NativeActivity extends Activity {
Log.e(TAG, "Invalid rotation: " + rotString);
return;
}
Log.i(TAG, "Requested rotation: " + rot + " ('" + rotString + "')");
switch (rot) {
case 0:
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
@ -315,6 +297,34 @@ public class NativeActivity extends Activity {
sz.y = 0;
}
private Runnable mEmulationRunner = new Runnable()
{
@Override
public void run()
{
// Bit of a hack - loop until onSurfaceCreated succeeds.
try {
while (mSurface == null)
{
Thread.sleep(10);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
Log.i(TAG, "Starting the render loop: " + mSurface);
// Start emulation using the provided Surface.
runEGLRenderLoop(mSurface);
Log.i(TAG, "Left the render loop: " + mSurface);
}
};
public native void runEGLRenderLoop(Surface surface);
// Tells the render loop thread to exit, so we can restart it.
public native void exitEGLRenderLoop();
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@ -325,6 +335,12 @@ public class NativeActivity extends Activity {
Initialize();
initialized = true;
}
// OK, config should be initialized, we can query for screen rotation.
if (Build.VERSION.SDK_INT >= 9) {
updateScreenRotation();
}
// Keep the screen bright - very annoying if it goes dark when tilting away
Window window = this.getWindow();
window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
@ -333,47 +349,18 @@ public class NativeActivity extends Activity {
gainAudioFocus(this.audioManager, this.audioFocusChangeListener);
NativeApp.audioInit();
mGLSurfaceView = new NativeGLView(this);
nativeRenderer = new NativeRenderer(this);
mSurfaceView = new NativeSurfaceView(this);
mSurfaceView.getHolder().addCallback(this);
Point sz = new Point();
getDesiredBackbufferSize(sz);
if (sz.x > 0) {
Log.i(TAG, "Requesting fixed size buffer: " + sz.x + "x" + sz.y);
// Auto-calculates new DPI and forwards to the correct call on mGLSurfaceView.getHolder()
nativeRenderer.setFixedSize(sz.x, sz.y, mGLSurfaceView);
}
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);
mSurfaceView.setFixedSize(sz.x, sz.y);
}
mGLSurfaceView.setRenderer(nativeRenderer);
setContentView(mGLSurfaceView);
setContentView(mSurfaceView);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
updateSystemUiVisibility();
@ -381,8 +368,46 @@ public class NativeActivity extends Activity {
setupSystemUiCallback();
}
}
mRenderLoopThread = new Thread(mEmulationRunner);
mRenderLoopThread.start();
}
@Override
public void surfaceCreated(SurfaceHolder holder)
{
Log.d(TAG, "Surface created.");
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height)
{
Log.w(TAG, "Surface changed. Resolution: " + width + "x" + height);
mSurface = holder.getSurface();
if (mRenderLoopThread == null || !mRenderLoopThread.isAlive()) {
mRenderLoopThread = new Thread(mEmulationRunner);
mRenderLoopThread.start();
}
}
@Override
public void surfaceDestroyed(SurfaceHolder holder)
{
mSurface = null;
Log.w(TAG, "Surface destroyed.");
if (mRenderLoopThread != null && mRenderLoopThread.isAlive()) {
// This will wait until the thread has exited.
exitEGLRenderLoop();
try {
mRenderLoopThread.join();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
@TargetApi(19)
void setupSystemUiCallback() {
getWindow().getDecorView().setOnSystemUiVisibilityChangeListener(new OnSystemUiVisibilityChangeListener() {
@ -397,6 +422,7 @@ public class NativeActivity extends Activity {
@Override
protected void onStop() {
exitEGLRenderLoop();
super.onStop();
Log.i(TAG, "onStop - do nothing special");
}
@ -405,11 +431,10 @@ public class NativeActivity extends Activity {
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();
@ -433,7 +458,7 @@ public class NativeActivity extends Activity {
Log.i(TAG, "onPause");
loseAudioFocus(this.audioManager, this.audioFocusChangeListener);
NativeApp.pause();
mGLSurfaceView.onPause();
mSurfaceView.onPause();
}
@Override
@ -448,8 +473,8 @@ public class NativeActivity extends Activity {
}
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");
}
@ -468,7 +493,7 @@ public class NativeActivity extends Activity {
Point sz = new Point();
getDesiredBackbufferSize(sz);
if (sz.x > 0) {
mGLSurfaceView.getHolder().setFixedSize(sz.x/2, sz.y/2);
mSurfaceView.getHolder().setFixedSize(sz.x/2, sz.y/2);
}
}
@ -733,12 +758,14 @@ public class NativeActivity extends Activity {
.setView(fl)
.setTitle(title)
.setPositiveButton(defaultAction, new DialogInterface.OnClickListener(){
@Override
public void onClick(DialogInterface d, int which) {
NativeApp.sendMessage("inputbox_completed", input.getText().toString());
d.dismiss();
}
})
.setNegativeButton("Cancel", new DialogInterface.OnClickListener(){
@Override
public void onClick(DialogInterface d, int which) {
NativeApp.sendMessage("inputbox_failed", "");
d.cancel();
@ -825,18 +852,18 @@ public class NativeActivity extends Activity {
toast.show();
Log.i(TAG, params);
return true;
} else if (command.equals("showKeyboard") && mGLSurfaceView != null) {
} else if (command.equals("showKeyboard") && mSurfaceView != null) {
InputMethodManager inputMethodManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
// No idea what the point of the ApplicationWindowToken is or if it
// matters where we get it from...
inputMethodManager.toggleSoftInputFromWindow(
mGLSurfaceView.getApplicationWindowToken(),
mSurfaceView.getApplicationWindowToken(),
InputMethodManager.SHOW_FORCED, 0);
return true;
} else if (command.equals("hideKeyboard") && mGLSurfaceView != null) {
} else if (command.equals("hideKeyboard") && mSurfaceView != null) {
InputMethodManager inputMethodManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
inputMethodManager.toggleSoftInputFromWindow(
mGLSurfaceView.getApplicationWindowToken(),
mSurfaceView.getApplicationWindowToken(),
InputMethodManager.SHOW_FORCED, 0);
return true;
} else if (command.equals("inputbox")) {
@ -850,7 +877,7 @@ public class NativeActivity extends Activity {
Log.i(TAG, "Launching inputbox: " + title + " " + defString);
inputBox(title, defString, "OK");
return true;
} else if (command.equals("vibrate") && mGLSurfaceView != null) {
} else if (command.equals("vibrate") && mSurfaceView != null) {
int milliseconds = -1;
if (params != "") {
try {
@ -867,13 +894,13 @@ public class NativeActivity extends Activity {
// permission.
switch (milliseconds) {
case -1:
mGLSurfaceView.performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP);
mSurfaceView.performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP);
break;
case -2:
mGLSurfaceView.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY);
mSurfaceView.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY);
break;
case -3:
mGLSurfaceView.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
mSurfaceView.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
break;
default:
if (vibrator != null) {
@ -893,6 +920,7 @@ public class NativeActivity extends Activity {
updateSystemUiVisibility();
}
} else if (command.equals("recreate")) {
exitEGLRenderLoop();
recreate();
}
return false;

View File

@ -19,6 +19,7 @@ public class NativeApp {
public static native void audioShutdown();
public static native void audioConfig(int optimalFramesPerBuffer, int optimalSampleRate);
public static native void displayResize(int width, int height, int scaled_dpi, float refreshRate);
public static native boolean isLandscape();
public static native boolean isAtTopLevel();
@ -50,4 +51,3 @@ public class NativeApp {
public static native String queryConfig(String queryName);
}

View File

@ -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;
}
}

View File

@ -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();
}

View File

@ -5,6 +5,7 @@ package org.ppsspp.ppsspp;
import android.annotation.TargetApi;
import android.app.Activity;
import android.graphics.Point;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
@ -12,13 +13,17 @@ import android.hardware.SensorManager;
import android.opengl.GLSurfaceView;
import android.os.Build;
import android.os.Handler;
import android.util.DisplayMetrics;
// import android.os.Build;
// import android.util.Log;
import android.util.Log;
import android.view.Display;
import android.view.MotionEvent;
import android.view.SurfaceView;
import com.bda.controller.*;
public class NativeGLView extends GLSurfaceView implements SensorEventListener, ControllerListener {
public class NativeSurfaceView extends SurfaceView implements SensorEventListener, ControllerListener {
private static String TAG = "NativeGLView";
private SensorManager mSensorManager;
private Sensor mAccelerometer;
@ -27,33 +32,36 @@ public class NativeGLView extends GLSurfaceView implements SensorEventListener,
// Moga controller
private Controller mController = null;
private boolean isMogaPro = false;
private int dpi;
private float refreshRate;
public NativeGLView(NativeActivity activity) {
private double dpi_scale_x;
private double dpi_scale_y;
int last_width, last_height;
public int fixedW = 0;
public int fixedH = 0;
public NativeSurfaceView(NativeActivity activity) {
super(activity);
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();
}
}*/
DisplayMetrics metrics = new DisplayMetrics();
Display display = activity.getWindowManager().getDefaultDisplay();
display.getMetrics(metrics);
dpi = metrics.densityDpi;
refreshRate = display.getRefreshRate();
mActivity = activity;
mSensorManager = (SensorManager)activity.getSystemService(Activity.SENSOR_SERVICE);
mAccelerometer = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
mController = Controller.getInstance(activity);
onResize(display.getWidth(), display.getHeight());
try {
MogaHack.init(mController, activity);
Log.i(TAG, "MOGA initialized");
@ -63,17 +71,37 @@ public class NativeGLView extends GLSurfaceView implements SensorEventListener,
}
}
void onResize(int width, int height) {
Point sz = new Point();
mActivity.GetScreenSize(sz);
double actualW = sz.x;
double actualH = sz.y;
dpi_scale_x = (width / actualW);
dpi_scale_y = (height / actualH);
Log.i(TAG, "onSurfaceChanged: " + dpi_scale_x + "x" + dpi_scale_y + " (width=" + width + ", actualW=" + actualW);
int scaled_dpi = (int)(dpi * dpi_scale_x);
NativeApp.displayResize(width, height, scaled_dpi, refreshRate);
last_width = width;
last_height = height;
}
void setFixedSize(int w, int h) {
fixedW = w;
fixedH = h;
}
@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
private int getToolType(final MotionEvent ev, int pointer) {
return ev.getToolType(pointer);
}
@Override
public boolean onTouchEvent(final MotionEvent ev) {
boolean canReadToolType = Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH;
int numTouchesHandled = 0;
float scaleX = (float)mActivity.getRenderer().getDpiScaleX();
float scaleY = (float)mActivity.getRenderer().getDpiScaleY();
float scaleX = (float)this.dpi_scale_x;
float scaleY = (float)this.dpi_scale_y;
for (int i = 0; i < ev.getPointerCount(); i++) {
int pid = ev.getPointerId(i);
int code = 0;
@ -112,9 +140,11 @@ public class NativeGLView extends GLSurfaceView implements SensorEventListener,
}
// Sensor management
@Override
public void onAccuracyChanged(Sensor sensor, int arg1) {
}
@Override
public void onSensorChanged(SensorEvent event) {
if (event.sensor.getType() != Sensor.TYPE_ACCELEROMETER) {
return;
@ -123,18 +153,14 @@ public class NativeGLView extends GLSurfaceView implements SensorEventListener,
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();

View File

@ -114,7 +114,7 @@ public class PpssppActivity extends NativeActivity {
sz.y = 0;
return;
}
correctRatio(sz, (float)scale);
correctRatio(sz, scale);
}
// called by the C++ code through JNI. Dispatch anything we can't directly handle
@ -123,6 +123,7 @@ public class PpssppActivity extends NativeActivity {
final String cmd = command;
final String param = parameter;
runOnUiThread(new Runnable() {
@Override
public void run() {
processCommand(cmd, param);
}