Merge pull request #8642 from hrydgard/java-egl

Restore Java EGL support, while keeping the new stuff around as an option
This commit is contained in:
Henrik Rydgård 2016-03-17 22:25:39 +01:00
commit eaeddc6c23
20 changed files with 947 additions and 114 deletions

View File

@ -411,7 +411,7 @@ static int DefaultAndroidHwScale() {
int xres = System_GetPropertyInt(SYSPROP_DISPLAY_XRES);
int yres = System_GetPropertyInt(SYSPROP_DISPLAY_YRES);
if (xres < 960) {
if (xres <= 960) {
// Smaller than the PSP*2, let's go native.
return 0;
} else if (xres <= 480 * 3) { // 720p xres

View File

@ -209,8 +209,8 @@ void TransformDrawEngine::DestroyDeviceObjects() {
}
}
void TransformDrawEngine::GLLost() {
ILOG("TransformDrawEngine::GLLost()");
void TransformDrawEngine::GLRestore() {
ILOG("TransformDrawEngine::GLRestore()");
// The objects have already been deleted.
bufferNameCache_.clear();
bufferNameInfo_.clear();

View File

@ -129,7 +129,7 @@ public:
void RestoreVAO();
void InitDeviceObjects();
void DestroyDeviceObjects();
void GLLost() override;
void GLRestore() override;
void Resized();
void DecimateTrackedVertexArrays();

View File

@ -193,6 +193,8 @@ void GameSettingsScreen::CreateViews() {
#ifdef ANDROID
static const char *deviceResolutions[] = { "Native device resolution", "Auto (same as Rendering)", "1x PSP", "2x PSP", "3x PSP", "4x PSP", "5x PSP" };
int max_res_temp = std::max(System_GetPropertyInt(SYSPROP_DISPLAY_XRES), System_GetPropertyInt(SYSPROP_DISPLAY_YRES)) / 480 + 2;
if (max_res_temp == 3)
max_res_temp = 4; // At least allow 2x
int max_res = std::min(max_res_temp, (int)ARRAY_SIZE(deviceResolutions));
UI::PopupMultiChoice *hwscale = graphicsSettings->Add(new PopupMultiChoice(&g_Config.iAndroidHwScale, gr->T("Display Resolution (HW scaler)"), deviceResolutions, 0, max_res, gr->GetName(), screenManager()));
hwscale->OnChoice.Handle(this, &GameSettingsScreen::OnHwScaleChange); // To refresh the display mode
@ -647,14 +649,18 @@ UI::EventReturn GameSettingsScreen::OnHardwareTransform(UI::EventParams &e) {
UI::EventReturn GameSettingsScreen::OnScreenRotation(UI::EventParams &e) {
ILOG("New display rotation: %d", g_Config.iScreenRotation);
ILOG("Sending rotate");
System_SendMessage("rotate", "");
ILOG("Got back from rotate");
return UI::EVENT_DONE;
}
static void RecreateActivity() {
const int SYSTEM_JELLYBEAN = 16;
if (System_GetPropertyInt(SYSPROP_SYSTEMVERSION) >= SYSTEM_JELLYBEAN) {
ILOG("Sending recreate");
System_SendMessage("recreate", "");
ILOG("Got back from recreate");
} else {
I18NCategory *gr = GetI18NCategory("Graphics");
System_SendMessage("toast", gr->T("Must Restart", "You must restart PPSSPP for this change to take effect"));

View File

@ -766,12 +766,13 @@ void NativeUpdate(InputState &input) {
screenManager->update(input);
}
void NativeDeviceLost() {
// g_gameInfoCache.Clear();
void NativeDeviceRestore() {
if (g_gameInfoCache)
g_gameInfoCache->Clear();
screenManager->deviceLost();
if (GetGPUBackend() == GPUBackend::OPENGL) {
gl_lost();
gl_restore();
}
}

View File

@ -121,6 +121,21 @@ void AndroidEGLGraphicsContext::SwapBuffers() {
gl->Swap();
}
// Doesn't do much. Just to fit in.
class AndroidJavaEGLGraphicsContext : public GraphicsContext {
public:
AndroidJavaEGLGraphicsContext() {}
bool Init(ANativeWindow *wnd, int desiredBackbufferSizeX, int desiredBackbufferSizeY, int backbufferFormat, int androidVersion) { return true; }
void Shutdown() override {}
void SwapBuffers() override {}
void SwapInterval(int interval) override {}
void Resize() {}
Thin3DContext *CreateThin3DContext() {
CheckGLExtensions();
return T3DCreateGLContext();
}
};
static recursive_mutex frameCommandLock;
static std::queue<FrameCommand> frameCommands;
@ -165,10 +180,14 @@ InputState input_state;
static bool renderer_inited = false;
static bool first_lost = true;
static bool javaGL = true;
static std::string library_path;
static std::map<SystemPermission, PermissionStatus> permissions;
AndroidEGLGraphicsContext *graphicsContext;
GraphicsContext *graphicsContext;
static void ProcessFrameCommands(JNIEnv *env);
void PushCommand(std::string cmd, std::string param) {
lock_guard guard(frameCommandLock);
@ -295,9 +314,9 @@ 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, jstring jlangRegion, jstring japkpath,
jstring jdataDir, jstring jexternalDir, jstring jlibraryDir, jstring jcacheDir, jstring jshortcutParam,
jint jAndroidVersion) {
jint jAndroidVersion, jboolean jjavaGL) {
jniEnvUI = env;
javaGL = jjavaGL;
setCurrentThreadName("androidInit");
ILOG("NativeApp.init() -- begin");
@ -402,8 +421,110 @@ extern "C" void Java_org_ppsspp_ppsspp_NativeApp_shutdown(JNIEnv *, jclass) {
ILOG("NativeApp.shutdown() -- end");
}
extern "C" void Java_org_ppsspp_ppsspp_NativeRenderer_displayShutdown(JNIEnv *env, jobject obj) {
// JavaEGL
extern "C" void Java_org_ppsspp_ppsspp_NativeRenderer_displayInit(JNIEnv * env, jobject obj) {
ILOG("NativeApp.displayInit()");
if (javaGL && !graphicsContext) {
graphicsContext = new AndroidJavaEGLGraphicsContext();
}
if (!renderer_inited) {
NativeInitGraphics(graphicsContext);
renderer_inited = true;
} else {
NativeDeviceRestore(); // ???
ILOG("displayInit: NativeDeviceRestore completed.");
}
}
// JavaEGL
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;
pixel_xres = w;
pixel_yres = h;
// 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;
NativeResized();
}
// JavaEGL
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(graphicsContext);
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;
}
ProcessFrameCommands(env);
}
extern "C" void Java_org_ppsspp_ppsspp_NativeRenderer_displayShutdown(JNIEnv *env, jobject obj) {
ILOG("NativeApp.displayShutdown()");
if (renderer_inited) {
NativeShutdownGraphics();
renderer_inited = false;
NativeMessageReceived("recreateviews", "");
}
@ -427,6 +548,7 @@ PermissionStatus System_GetPermissionStatus(SystemPermission permission) {
extern "C" jboolean JNICALL Java_org_ppsspp_ppsspp_NativeApp_touch
(JNIEnv *, jclass, float x, float y, int code, int pointerId) {
float scaledX = x * dp_xscale;
float scaledY = y * dp_yscale;
@ -679,7 +801,7 @@ extern "C" jint JNICALL Java_org_ppsspp_ppsspp_NativeApp_getDesiredBackbufferHei
return desiredBackbufferSizeY;
}
void ProcessFrameCommands(JNIEnv *env) {
static void ProcessFrameCommands(JNIEnv *env) {
lock_guard guard(frameCommandLock);
while (!frameCommands.empty()) {
FrameCommand frameCmd;
@ -757,9 +879,6 @@ extern "C" bool JNICALL Java_org_ppsspp_ppsspp_NativeActivity_runEGLRenderLoop(J
ILOG("After render loop.");
g_gameInfoCache->WorkQueue()->Flush();
NativeDeviceLost();
ILOG("NativeDeviceLost completed.");
NativeShutdownGraphics();
renderer_inited = false;

View File

@ -9,6 +9,7 @@ import android.Manifest;
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,9 +17,11 @@ 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;
@ -38,6 +41,7 @@ import android.view.HapticFeedbackConstants;
import android.view.MotionEvent;
import android.view.Surface;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.view.View.OnSystemUiVisibilityChangeListener;
import android.view.Window;
@ -56,11 +60,18 @@ public class NativeActivity extends Activity implements SurfaceHolder.Callback {
// Allows us to skip a lot of initialization on secondary calls to onCreate.
private static boolean initialized = false;
// Graphics and audio interfaces
// Change this to false to switch to C++ EGL.
private static boolean javaGL = true;
// Graphics and audio interfaces for EGL (javaGL = false)
private NativeSurfaceView mSurfaceView;
private Surface mSurface;
private Thread mRenderLoopThread = null;
// Graphics and audio interfaces for Java EGL (javaGL = true)
private NativeGLView mGLSurfaceView;
protected NativeRenderer nativeRenderer;
private String shortcutParam = "";
public static String runCommand;
@ -95,6 +106,10 @@ public class NativeActivity extends Activity implements SurfaceHolder.Callback {
return true;
}
NativeRenderer getRenderer() {
return nativeRenderer;
}
@TargetApi(17)
private void detectOptimalAudioSettings() {
try {
@ -207,8 +222,13 @@ public class NativeActivity extends Activity implements SurfaceHolder.Callback {
detectOptimalAudioSettings();
}
// Get system information
// isLandscape is used to trigger GetAppInfo currently, we
boolean landscape = NativeApp.isLandscape();
Log.d(TAG, "Landscape: " + landscape);
// Get system information
ApplicationInfo appInfo = null;
PackageManager packMgmr = getPackageManager();
String packageName = getPackageName();
try {
@ -246,10 +266,28 @@ public class NativeActivity extends Activity implements SurfaceHolder.Callback {
String languageRegion = Locale.getDefault().getLanguage() + "_" + Locale.getDefault().getCountry();
NativeApp.audioConfig(optimalFramesPerBuffer, optimalSampleRate);
NativeApp.init(model, deviceType, languageRegion, apkFilePath, dataDir, externalStorageDir, libraryDir, cacheDir, shortcutParam, Build.VERSION.SDK_INT);
NativeApp.init(model, deviceType, languageRegion, apkFilePath, dataDir, externalStorageDir, libraryDir, cacheDir, shortcutParam, Build.VERSION.SDK_INT, javaGL);
sendInitialGrants();
// 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();
@ -352,6 +390,13 @@ public class NativeActivity extends Activity implements SurfaceHolder.Callback {
NativeApp.setDisplayParameters(outSize.x, outSize.y, metrics.densityDpi, refreshRate);
}
public void getDesiredBackbufferSize(Point sz) {
NativeApp.computeDesiredBackbufferDimensions();
sz.x = NativeApp.getDesiredBackbufferWidth();
sz.y = NativeApp.getDesiredBackbufferHeight();
}
@SuppressWarnings("deprecation")
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@ -376,37 +421,78 @@ public class NativeActivity extends Activity implements SurfaceHolder.Callback {
gainAudioFocus(this.audioManager, this.audioFocusChangeListener);
NativeApp.audioInit();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
updateSystemUiVisibility();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
setupSystemUiCallback();
}
}
updateDisplayMetrics(null);
if (javaGL) {
mGLSurfaceView = new NativeGLView(this);
nativeRenderer = new NativeRenderer(this);
NativeApp.computeDesiredBackbufferDimensions();
int bbW = NativeApp.getDesiredBackbufferWidth();
int bbH = NativeApp.getDesiredBackbufferHeight();
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);
mSurfaceView = new NativeSurfaceView(NativeActivity.this, bbW, bbH);
mSurfaceView.getHolder().addCallback(NativeActivity.this);
Log.i(TAG, "setcontentview before");
setContentView(mSurfaceView);
Log.i(TAG, "setcontentview after");
// 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.
ensureRenderLoop();
// 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());
}
// Tried to mess around with config choosers here but fail completely on Xperia Play.
mGLSurfaceView.setRenderer(nativeRenderer);
setContentView(mGLSurfaceView);
} else {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
updateSystemUiVisibility();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
setupSystemUiCallback();
}
}
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");
ensureRenderLoop();
}
}
@Override
public void surfaceCreated(SurfaceHolder holder)
{
public void surfaceCreated(SurfaceHolder holder) {
Log.d(TAG, "Surface created.");
}
//
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height)
{
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
if (javaGL) {
Log.e(TAG, "JavaGL - should not get into surfaceChanged.");
return;
}
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...
@ -425,6 +511,10 @@ public class NativeActivity extends Activity implements SurfaceHolder.Callback {
// Invariants: After this, mRenderLoopThread will be set, and the thread will be running.
protected synchronized void ensureRenderLoop() {
if (javaGL) {
Log.e(TAG, "JavaGL - should not get into ensureRenderLoop.");
return;
}
if (mSurface == null) {
Log.w(TAG, "ensureRenderLoop - not starting thread, needs surface");
return;
@ -439,6 +529,11 @@ public class NativeActivity extends Activity implements SurfaceHolder.Callback {
// Invariants: After this, mRenderLoopThread will be null, and the thread has exited.
private synchronized void joinRenderLoopThread() {
if (javaGL) {
Log.e(TAG, "JavaGL - should not get into joinRenderLoopThread.");
return;
}
if (mRenderLoopThread != null) {
// This will wait until the thread has exited.
Log.i(TAG, "exitEGLRenderLoop");
@ -457,12 +552,16 @@ public class NativeActivity extends Activity implements SurfaceHolder.Callback {
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
if (javaGL) {
Log.e(TAG, "JavaGL - should not get into surfaceDestroyed.");
return;
}
mSurface = null;
Log.w(TAG, "Surface destroyed.");
joinRenderLoopThread();
}
@TargetApi(19)
void setupSystemUiCallback() {
getWindow().getDecorView().setOnSystemUiVisibilityChangeListener(new OnSystemUiVisibilityChangeListener() {
@ -484,15 +583,17 @@ public class NativeActivity extends Activity implements SurfaceHolder.Callback {
@Override
protected void onDestroy() {
super.onDestroy();
Log.i(TAG, "onDestroy");
mSurfaceView.onDestroy();
NativeApp.audioShutdown();
// Probably vain attempt to help the garbage collector...
mSurfaceView = null;
audioFocusChangeListener = null;
audioManager = null;
unregisterCallbacks();
if (javaGL) {
Log.i(TAG, "onDestroy");
mGLSurfaceView.onDestroy();
nativeRenderer.onDestroyed();
NativeApp.audioShutdown();
// Probably vain attempt to help the garbage collector...
mGLSurfaceView = null;
audioFocusChangeListener = null;
audioManager = null;
unregisterCallbacks();
}
if (shuttingDown) {
NativeApp.shutdown();
}
@ -503,14 +604,35 @@ public class NativeActivity extends Activity implements SurfaceHolder.Callback {
super.onPause();
Log.i(TAG, "onPause");
loseAudioFocus(this.audioManager, this.audioFocusChangeListener);
Log.i(TAG, "Pausing surface view");
NativeApp.pause();
mSurfaceView.onPause();
Log.i(TAG, "Joining render thread");
joinRenderLoopThread();
if (!javaGL) {
Log.i(TAG, "Pausing surface view");
mSurfaceView.onPause();
Log.i(TAG, "Joining render thread");
joinRenderLoopThread();
} else {
if (mGLSurfaceView != null) {
mGLSurfaceView.onPause();
} else {
Log.e(TAG, "mGLSurfaceView really shouldn't be null in onPause");
}
}
Log.i(TAG, "onPause completed");
}
private boolean detectOpenGLES20() {
ActivityManager am = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
ConfigurationInfo info = am.getDeviceConfigurationInfo();
return info.reqGlEsVersion >= 0x20000;
}
private boolean detectOpenGLES30() {
ActivityManager am = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
ConfigurationInfo info = am.getDeviceConfigurationInfo();
return info.reqGlEsVersion >= 0x30000;
}
@Override
protected void onResume() {
super.onResume();
@ -518,20 +640,26 @@ public class NativeActivity extends Activity implements SurfaceHolder.Callback {
updateSystemUiVisibility();
}
// OK, config should be initialized, we can query for screen rotation.
updateScreenRotation();
if (javaGL || Build.VERSION.SDK_INT >= 9) {
updateScreenRotation();
}
Log.i(TAG, "onResume");
if (mSurfaceView != null) {
mSurfaceView.onResume();
} else {
Log.e(TAG, "mGLSurfaceView really shouldn't be null in onResume");
if (javaGL) {
if (mGLSurfaceView != null) {
mGLSurfaceView.onResume();
} else {
Log.e(TAG, "mGLSurfaceView really shouldn't be null in onResume");
}
}
gainAudioFocus(this.audioManager, this.audioFocusChangeListener);
NativeApp.resume();
// Restart the render loop.
ensureRenderLoop();
if (!javaGL) {
// Restart the render loop.
ensureRenderLoop();
}
}
@Override
@ -542,6 +670,13 @@ public class NativeActivity extends Activity implements SurfaceHolder.Callback {
updateSystemUiVisibility();
}
updateDisplayMetrics(null);
if (javaGL) {
Point sz = new Point();
getDesiredBackbufferSize(sz);
if (sz.x > 0) {
mGLSurfaceView.getHolder().setFixedSize(sz.x/2, sz.y/2);
}
}
}
//keep this static so we can call this even if we don't
@ -775,7 +910,6 @@ public class NativeActivity extends Activity implements SurfaceHolder.Callback {
}
}
@TargetApi(11)
@SuppressWarnings("deprecation")
private AlertDialog.Builder createDialogBuilderWithTheme() {
@ -823,14 +957,14 @@ public class NativeActivity extends Activity implements SurfaceHolder.Callback {
.setTitle(title)
.setPositiveButton(defaultAction, new DialogInterface.OnClickListener(){
@Override
public void onClick(DialogInterface d, int which) {
NativeApp.sendMessage("inputbox_completed", title + ":" + input.getText().toString());
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) {
public void onClick(DialogInterface d, int which) {
NativeApp.sendMessage("inputbox_failed", "");
d.cancel();
}
@ -841,6 +975,7 @@ public class NativeActivity extends Activity implements SurfaceHolder.Callback {
}
public boolean processCommand(String command, String params) {
SurfaceView surfView = javaGL ? mGLSurfaceView : mSurfaceView;
if (command.equals("launchBrowser")) {
try {
Intent i = new Intent(Intent.ACTION_VIEW, Uri.parse(params));
@ -916,18 +1051,18 @@ public class NativeActivity extends Activity implements SurfaceHolder.Callback {
toast.show();
Log.i(TAG, params);
return true;
} else if (command.equals("showKeyboard") && mSurfaceView != null) {
} else if (command.equals("showKeyboard") && surfView != 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(
mSurfaceView.getApplicationWindowToken(),
surfView.getApplicationWindowToken(),
InputMethodManager.SHOW_FORCED, 0);
return true;
} else if (command.equals("hideKeyboard") && mSurfaceView != null) {
} else if (command.equals("hideKeyboard") && surfView != null) {
InputMethodManager inputMethodManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
inputMethodManager.toggleSoftInputFromWindow(
mSurfaceView.getApplicationWindowToken(),
surfView.getApplicationWindowToken(),
InputMethodManager.SHOW_FORCED, 0);
return true;
} else if (command.equals("inputbox")) {
@ -941,7 +1076,7 @@ public class NativeActivity extends Activity implements SurfaceHolder.Callback {
Log.i(TAG, "Launching inputbox: " + title + " " + defString);
inputBox(title, defString, "OK");
return true;
} else if (command.equals("vibrate") && mSurfaceView != null) {
} else if (command.equals("vibrate") && surfView != null) {
int milliseconds = -1;
if (params != "") {
try {
@ -958,13 +1093,13 @@ public class NativeActivity extends Activity implements SurfaceHolder.Callback {
// permission.
switch (milliseconds) {
case -1:
mSurfaceView.performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP);
surfView.performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP);
break;
case -2:
mSurfaceView.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY);
surfView.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY);
break;
case -3:
mSurfaceView.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
surfView.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
break;
default:
if (vibrator != null) {
@ -978,16 +1113,21 @@ public class NativeActivity extends Activity implements SurfaceHolder.Callback {
shuttingDown = true;
finish();
} else if (command.equals("rotate")) {
updateScreenRotation();
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
Log.i(TAG, "Must recreate activity on rotation");
}
if (javaGL) {
updateScreenRotation();
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
Log.i(TAG, "Must recreate activity on rotation");
}
} else {
if (Build.VERSION.SDK_INT >= 9) {
updateScreenRotation();
}
}
} else if (command.equals("immersive")) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
updateSystemUiVisibility();
}
} else if (command.equals("recreate")) {
exitEGLRenderLoop();
recreate();
} else if (command.equals("ask_permission") && params.equals("storage")) {
askForStoragePermission();

View File

@ -13,8 +13,7 @@ public class NativeApp {
public final static int DEVICE_TYPE_TV = 1;
public final static int DEVICE_TYPE_DESKTOP = 2;
public static native void init(String model, int deviceType, String languageRegion, String apkPath, String dataDir, String externalDir, String libraryDir, String cacheDir, String shortcutParam, int androidVersion);
public static native void init(String model, int deviceType, String languageRegion, String apkPath, String dataDir, String externalDir, String libraryDir, String cacheDir, String shortcutParam, int androidVersion, boolean javaGL);
public static native void audioInit();
public static native void audioShutdown();
public static native void audioConfig(int optimalFramesPerBuffer, int optimalSampleRate);
@ -57,3 +56,4 @@ public class NativeApp {
public static native String queryConfig(String queryName);
}

View File

@ -0,0 +1,213 @@
package org.ppsspp.ppsspp;
import javax.microedition.khronos.egl.EGL10;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.egl.EGLDisplay;
import android.graphics.PixelFormat;
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;
NativeEGLConfigChooser() {
}
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;
}
@Override
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_TRANSPARENT_TYPE, EGL10.EGL_NONE
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.
// Though, that may be possible to fix by using EGL10.EGL_TRANSPARENT_TYPE, EGL10.EGL_NONE.
// 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

@ -0,0 +1,243 @@
package org.ppsspp.ppsspp;
// Touch- and sensor-enabled GLSurfaceView.
// Used when javaGL = true.
//
// 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.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.opengl.GLSurfaceView;
import android.os.Build;
import android.os.Handler;
import android.util.Log;
import android.view.MotionEvent;
import com.bda.controller.*;
public class NativeGLView extends GLSurfaceView implements SensorEventListener, ControllerListener {
private static String TAG = "NativeGLView";
private SensorManager mSensorManager;
private Sensor mAccelerometer;
// Moga controller
private Controller mController = null;
private boolean isMogaPro = false;
NativeActivity mActivity;
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();
}
}*/
mSensorManager = (SensorManager)activity.getSystemService(Activity.SENSOR_SERVICE);
mAccelerometer = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
mController = Controller.getInstance(activity);
try {
MogaHack.init(mController, activity);
Log.i(TAG, "MOGA initialized");
mController.setListener(this, new Handler());
} catch (Exception e) {
Log.i(TAG, "Moga failed to initialize");
}
}
@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
private int getToolType(final MotionEvent ev, int pointer) {
return ev.getToolType(pointer);
}
@SuppressLint("ClickableViewAccessibility")
@Override
public boolean onTouchEvent(final MotionEvent ev) {
boolean canReadToolType = Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH;
int numTouchesHandled = 0;
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:
case MotionEvent.ACTION_POINTER_DOWN:
if (ev.getActionIndex() == i)
code = 2;
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_POINTER_UP:
if (ev.getActionIndex() == i)
code = 4;
break;
case MotionEvent.ACTION_MOVE:
code = 1;
break;
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(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;
}
// Can also look at event.timestamp for accuracy magic
NativeApp.accelerometer(event.values[0], event.values[1], event.values[2]);
}
@Override
public void onPause() {
Log.i(TAG, "onPause");
super.onPause();
mSensorManager.unregisterListener(this);
if (mController != null) {
mController.onPause();
}
}
@Override
public void onResume() {
Log.i(TAG, "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() {
Log.i(TAG, "onDestroy");
if (mController != null) {
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:
case KeyEvent.KEYCODE_DPAD_UP:
case KeyEvent.KEYCODE_DPAD_LEFT:
case KeyEvent.KEYCODE_DPAD_RIGHT:
return;
default:
break;
}
}
boolean repeat = false; // Moga has no repeats?
switch (event.getAction()) {
case KeyEvent.ACTION_DOWN:
NativeApp.keyDown(NativeApp.DEVICE_ID_PAD_0, event.getKeyCode(), repeat);
break;
case KeyEvent.ACTION_UP:
NativeApp.keyUp(NativeApp.DEVICE_ID_PAD_0, event.getKeyCode());
break;
}
}
// MOGA Controller - from ControllerListener
@Override
public void onMotionEvent(com.bda.controller.MotionEvent event) {
NativeApp.joystickAxis(NativeApp.DEVICE_ID_PAD_0, com.bda.controller.MotionEvent.AXIS_X, event.getAxisValue(com.bda.controller.MotionEvent.AXIS_X));
NativeApp.joystickAxis(NativeApp.DEVICE_ID_PAD_0, com.bda.controller.MotionEvent.AXIS_Y, event.getAxisValue(com.bda.controller.MotionEvent.AXIS_Y));
NativeApp.joystickAxis(NativeApp.DEVICE_ID_PAD_0, com.bda.controller.MotionEvent.AXIS_Z, event.getAxisValue(com.bda.controller.MotionEvent.AXIS_Z));
NativeApp.joystickAxis(NativeApp.DEVICE_ID_PAD_0, com.bda.controller.MotionEvent.AXIS_RZ, event.getAxisValue(com.bda.controller.MotionEvent.AXIS_RZ));
NativeApp.joystickAxis(NativeApp.DEVICE_ID_PAD_0, com.bda.controller.MotionEvent.AXIS_LTRIGGER, event.getAxisValue(com.bda.controller.MotionEvent.AXIS_LTRIGGER));
NativeApp.joystickAxis(NativeApp.DEVICE_ID_PAD_0, com.bda.controller.MotionEvent.AXIS_RTRIGGER, event.getAxisValue(com.bda.controller.MotionEvent.AXIS_RTRIGGER));
}
// MOGA Controller - from ControllerListener
@Override
public void onStateEvent(StateEvent state) {
switch (state.getState()) {
case StateEvent.STATE_CONNECTION:
switch (state.getAction()) {
case StateEvent.ACTION_CONNECTED:
Log.i(TAG, "Moga Connected");
if (mController.getState(Controller.STATE_CURRENT_PRODUCT_VERSION) == Controller.ACTION_VERSION_MOGA) {
NativeApp.sendMessage("moga", "Moga");
} else {
Log.i(TAG, "MOGA Pro detected");
isMogaPro = true;
NativeApp.sendMessage("moga", "MogaPro");
}
break;
case StateEvent.ACTION_CONNECTING:
Log.i(TAG, "Moga Connecting...");
break;
case StateEvent.ACTION_DISCONNECTED:
Log.i(TAG, "Moga Disconnected (or simply Not connected)");
NativeApp.sendMessage("moga", "");
break;
}
break;
case StateEvent.STATE_POWER_LOW:
switch (state.getAction()) {
case StateEvent.ACTION_TRUE:
Log.i(TAG, "Moga Power Low");
break;
case StateEvent.ACTION_FALSE:
Log.i(TAG, "Moga Power OK");
break;
}
break;
default:
break;
}
}
}

View File

@ -0,0 +1,94 @@
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

@ -13,6 +13,8 @@ import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
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;
@ -68,6 +70,8 @@ public class NativeSurfaceView extends SurfaceView implements SensorEventListene
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;

View File

@ -2,6 +2,7 @@ package org.ppsspp.ppsspp;
import android.app.AlertDialog;
import android.content.Intent;
import android.graphics.Point;
import android.os.Build;
import android.os.Bundle;
import android.os.Looper;
@ -72,11 +73,11 @@ public class PpssppActivity extends NativeActivity {
// (from app drawer or file explorer).
Intent intent = getIntent();
String action = intent.getAction();
if(intent.ACTION_VIEW.equals(action))
{
if(Intent.ACTION_VIEW.equals(action))
{
String path = intent.getData().getPath();
super.setShortcutParam(path);
Toast.makeText(getApplicationContext(), path, Toast.LENGTH_SHORT).show();
Toast.makeText(getApplicationContext(), path, Toast.LENGTH_SHORT).show();
}
else super.setShortcutParam(getIntent().getStringExtra(SHORTCUT_EXTRA_KEY));
@ -95,4 +96,4 @@ public class PpssppActivity extends NativeActivity {
}
});
}
}
}

View File

@ -52,10 +52,13 @@ void NativeInit(int argc, const char *argv[], const char *savegame_dir, const ch
// Should not initialize anything screen-size-dependent - do that in NativeResized.
void NativeInitGraphics(GraphicsContext *graphicsContext);
// Signals that you need to destroy and recreate all buffered OpenGL resources,
// Signals that you need to forget all buffered OpenGL resources,
// like textures, vbo etc.
void NativeDeviceLost();
// Signals that it's time to recreate buffered OpenGL resources
void NativeDeviceRestore();
// If you want to change DPI stuff (such as modifying dp_xres and dp_yres), this is the
// place to do it. You should only read g_dpi_scale and pixel_xres and pixel_yres in this,
// and only write dp_xres and dp_yres.

View File

@ -7,10 +7,11 @@
std::vector<GfxResourceHolder *> *holders;
static bool inLost;
static bool inRestore;
void register_gl_resource_holder(GfxResourceHolder *holder) {
if (inLost) {
FLOG("BAD: Should not call register_gl_resource_holder from lost path");
if (inLost || inRestore) {
FLOG("BAD: Should not call register_gl_resource_holder from lost/restore path");
return;
}
if (holders) {
@ -21,8 +22,8 @@ void register_gl_resource_holder(GfxResourceHolder *holder) {
}
void unregister_gl_resource_holder(GfxResourceHolder *holder) {
if (inLost) {
FLOG("BAD: Should not call unregister_gl_resource_holder from lost path");
if (inLost || inRestore) {
FLOG("BAD: Should not call unregister_gl_resource_holder from lost/restore path");
return;
}
if (holders) {
@ -38,22 +39,22 @@ void unregister_gl_resource_holder(GfxResourceHolder *holder) {
}
}
void gl_lost() {
inLost = true;
void gl_restore() {
inRestore = true;
if (!holders) {
WLOG("GL resource holder not initialized, cannot process lost request");
inLost = false;
WLOG("GL resource holder not initialized, cannot process restore request");
inRestore = false;
return;
}
// TODO: We should really do this when we get the context back, not during gl_lost...
ILOG("gl_lost() restoring %i items:", (int)holders->size());
ILOG("gl_restore() restoring %i items:", (int)holders->size());
for (size_t i = 0; i < holders->size(); i++) {
ILOG("GLLost(%i / %i, %p, %08x)", (int)(i + 1), (int) holders->size(), (*holders)[i], *((uint32_t *)((*holders)[i])));
(*holders)[i]->GLLost();
ILOG("gl_restore(%i / %i, %p, %08x)", (int)(i + 1), (int)holders->size(), (*holders)[i], *((uint32_t *)((*holders)[i])));
(*holders)[i]->GLRestore();
}
ILOG("gl_lost() completed on %i items:", (int)holders->size());
inLost = false;
ILOG("gl_restore() completed on %i items:", (int)holders->size());
inRestore = false;
}
void gl_lost_manager_init() {

View File

@ -6,7 +6,7 @@
class GfxResourceHolder {
public:
virtual ~GfxResourceHolder() {}
virtual void GLLost() = 0;
virtual void GLRestore() = 0;
};
void gl_lost_manager_init();
@ -15,5 +15,5 @@ void gl_lost_manager_shutdown();
void register_gl_resource_holder(GfxResourceHolder *holder);
void unregister_gl_resource_holder(GfxResourceHolder *holder);
// Notifies all objects about the loss.
void gl_lost();
// Notifies all objects that it's time to be restored.
void gl_restore();

View File

@ -220,7 +220,7 @@ bool glsl_recompile(GLSLProgram *program, std::string *error_message) {
return true;
}
void GLSLProgram::GLLost() {
void GLSLProgram::GLRestore() {
// Quoth http://developer.android.com/reference/android/opengl/GLSurfaceView.Renderer.html;
// "Note that when the EGL context is lost, all OpenGL resources associated with that context will be automatically deleted.
// You do not need to call the corresponding "glDelete" methods such as glDeleteTextures to manually delete these lost resources."
@ -228,17 +228,16 @@ void GLSLProgram::GLLost() {
// glDeleteShader(this->vsh_);
// glDeleteShader(this->fsh_);
// glDeleteProgram(this->program_);
ILOG("Restoring GLSL program %s/%s",
strlen(this->vshader_filename) > 0 ? this->vshader_filename : "(mem)",
strlen(this->fshader_filename) > 0 ? this->fshader_filename : "(mem)");
this->program_ = 0;
this->vsh_ = 0;
this->fsh_ = 0;
ILOG("Restoring GLSL program %s/%s",
strlen(this->vshader_filename) > 0 ? this->vshader_filename : "(mem)",
strlen(this->fshader_filename) > 0 ? this->fshader_filename : "(mem)");
glsl_recompile(this);
// Note that uniforms are still lost, hopefully the client sets them every frame at a minimum...
// Note that any shader uniforms are still lost, hopefully the client sets them every frame at a minimum...
}
int glsl_attrib_loc(const GLSLProgram *program, const char *name) {
return glGetAttribLocation(program->program_, name);
}

View File

@ -43,7 +43,7 @@ struct GLSLProgram : public GfxResourceHolder {
GLuint fsh_;
GLuint program_;
void GLLost();
void GLRestore() override;
};
// C API, old skool. Not much point either...

View File

@ -164,7 +164,7 @@ public:
glBindBuffer(target_, buffer_);
}
void GLLost() override {
void GLRestore() override {
ILOG("Recreating vertex buffer after glLost");
knownSize_ = 0; // Will cause a new glBufferData call. Should genBuffers again though?
glGenBuffers(1, &buffer_);
@ -239,7 +239,7 @@ public:
void Apply(const void *base = nullptr);
void Unapply();
void Compile();
void GLLost() override;
void GLRestore() override;
bool RequiresBuffer() override {
return id_ != 0;
}
@ -280,7 +280,7 @@ public:
void SetVector(const char *name, float *value, int n) override;
void SetMatrix4x4(const char *name, const Matrix4x4 &value) override;
void GLLost() override {
void GLRestore() override {
vshader->Compile(vshader->GetSource().c_str());
fshader->Compile(fshader->GetSource().c_str());
Link();
@ -466,13 +466,22 @@ public:
glBindTexture(target_, tex_);
}
void GLLost() override {
// We lost our GL context - zero out the tex_.
void GLRestore() override {
// We can assume that the texture is gone.
tex_ = 0;
generatedMips_ = false;
// Don't try to restore stuff, that's not what this is for. Lost just means
// that all our textures and buffers are invalid.
if (!filename_.empty()) {
if (LoadFromFile(filename_.c_str())) {
ILOG("Reloaded lost texture %s", filename_.c_str());
} else {
ELOG("Failed to reload lost texture %s", filename_.c_str());
}
} else {
WLOG("Texture %p cannot be restored - has no filename", this);
tex_ = 0;
}
}
void Finalize(int zim_flags) override;
private:
@ -573,7 +582,7 @@ void Thin3DGLVertexFormat::Compile() {
lastBase_ = -1;
}
void Thin3DGLVertexFormat::GLLost() {
void Thin3DGLVertexFormat::GLRestore() {
Compile();
}

View File

@ -120,7 +120,7 @@ void UIContext::SetFontStyle(const UI::FontStyle &fontStyle) {
*fontStyle_ = fontStyle;
if (textDrawer_) {
textDrawer_->SetFontScale(fontScaleX_, fontScaleY_);
Text()->SetFont(fontStyle.fontName.c_str(), fontStyle.sizePts, fontStyle.flags);
textDrawer_->SetFont(fontStyle.fontName.c_str(), fontStyle.sizePts, fontStyle.flags);
}
}